Adapt your scripts to OneSync
OneSync is FiveM's entity synchronization system, enabled by default since 2020. It changed fundamental rules: entities are owned by specific players, the default culling radius is 424 units (entities beyond it are not synced to a client), and events must target the right players. If your resource predates OneSync or you learned from an old tutorial, this lesson fixes the patterns that silently fail.
Build it
Check entity ownership before modifying
The wrong way:
-- WRONG: may fail silently if another player owns this vehicle
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
DeleteEntity(veh) -- only works if YOU own it
The right way:
-- RIGHT: check ownership first
local veh = GetVehiclePedIsIn(PlayerPedId(), false)
local netId = NetworkGetNetworkIdFromEntity(veh)
local owner = NetworkGetEntityOwner(veh)
if owner == PlayerId() then
DeleteEntity(veh)
else
-- Ask the server to request ownership transfer
TriggerServerEvent('myresource:requestDelete', netId)
end
Server-side:
RegisterNetEvent('myresource:requestDelete', function(netId)
local entity = NetworkGetEntityFromNetworkId(netId)
if entity and DoesEntityExist(entity) then
DeleteEntity(entity) -- server can always delete
end
end)
Rule: modify entities on the side that created them, or ask the server to do it.
Use scoped events instead of broadcast
Old pattern (broadcasts to everyone):
-- WRONG: sends to ALL players, even ones 10km away
TriggerClientEvent('myresource:updateHUD', -1, data)
New pattern (scoped to players in the same bucket, or nearby players):
-- RIGHT: build a list of server ids of players in the sender's bucket,
-- then target each one. The 2nd arg to TriggerClientEvent is a player
-- target: a serverId, or -1 for everyone. It is NOT a bucket id.
local bucket = GetPlayerRoutingBucket(source)
for _, playerId in ipairs(GetPlayers()) do
if GetPlayerRoutingBucket(playerId) == bucket then
TriggerClientEvent('myresource:updateHUD', playerId, data)
end
end
-- Or send only to players within range of a position (compute distance
-- server-side from the entity's coords; do not use client-only natives here)
local ex, ey, ez = table.unpack(GetEntityCoords(entity))
for _, playerId in ipairs(GetPlayers()) do
local px, py, pz = table.unpack(GetEntityCoords(GetPlayerPed(playerId)))
if #(vector3(ex, ey, ez) - vector3(px, py, pz)) <= 100.0 then
TriggerClientEvent('myresource:updateHUD', playerId, data)
end
end
Use -1 only for truly global events (announcements, weather changes). Everything else should be scoped.
Use routing buckets for instances
Routing buckets isolate players into separate worlds:
-- Put a player into an instance (e.g., an apartment interior)
local bucketId = 100 + apartmentId
SetPlayerRoutingBucket(source, bucketId)
-- Later, return them to the default world
SetPlayerRoutingBucket(source, 0)
Players in different buckets cannot see each other, cannot interact, and their events are isolated. Use this for apartments, heists, and any instanced content.
Keep reading the full lesson
Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.
- Common failures
The remainder of Migrating legacy resources to OneSync is available to FiveM School members.