<!-- fivemad.com, Markdown source of https://fivemad.com/docs/development/state-bags-onesync -->
> Source: https://fivemad.com/docs/development/state-bags-onesync
> Section: Development
> Tags: state bags, onesync, GlobalState, AddStateBagChangeHandler, entity ownership, routing buckets, network id
> Last updated: 2026-09-22
> Site map for assistants: https://fivemad.com/llms.txt

# State bags, OneSync and entity ownership

Sync data the FiveM way: GlobalState, Player and Entity state bags, change handlers, replication rules, OneSync entity ownership, server side entities and routing buckets.

With OneSync on, the server knows about every player and entity and decides which clients see what. On top of that, FiveM gives you **state bags**: key/value data attached to the server, a player or an entity, synced to clients automatically. Together they replace a lot of hand written event syncing.

[How to use State Bags (CharlesHacks, FiveM Lua Scripting Master Series)](https://www.youtube.com/watch?v=6CSXtJpH0qQ) (YouTube video)

## OneSync in two minutes

- Enabled with `set onesync on` in `server.cfg`. Every current framework requires it.
- The server tracks all networked entities. Clients only receive entities near them: the culling radius is 424 units around each player.
- Each networked entity has an **owner**: the client that simulates it (usually the closest or the one who created it). The owner sends updates, the server forwards them.
- The server can **read** entity state (`GetEntityCoords(GetPlayerPed(src))`, `GetVehicleNumberPlateText`) and **create** entities.
- **Routing buckets** split the world into separate dimensions.
- Up to 48 slots free, up to 2048 with the right Cfx.re tier.

## State bags

There are three kinds, all with the same API:

| Bag | Where | Who can write (default) |
| --- | --- | --- |
| `GlobalState` | Server wide | Server only |
| `Player(serverId).state` (server) / `LocalPlayer.state` (client) | One player | The server, or that player's client |
| `Entity(entity).state` | One networked entity | The server, or the entity's owner |

### Set and read

```lua title="server.lua"
GlobalState.weather = 'RAIN'
GlobalState.maintenance = false

Player(src).state:set('job', 'police', true)          -- true = replicate to clients

local veh = CreateVehicleServerSetter(`police3`, 'automobile', 441.0, -1019.0, 28.5, 90.0)
Entity(veh).state:set('fuel', 100.0, true)
```

```lua title="client.lua"
print(GlobalState.weather)                           -- 'RAIN'
print(LocalPlayer.state.job)                         -- 'police'

local veh = GetVehiclePedIsIn(PlayerPedId(), false)
if veh ~= 0 then
    print(Entity(veh).state.fuel)
end
```

Setting with `.key = value` uses the default replication: values set by the server replicate to clients, values set by a client do **not** replicate. `state:set(key, value, replicated)` lets you choose. The server can also keep private data on a bag with `replicated = false`.

### React to changes

```lua title="client.lua"
AddStateBagChangeHandler('fuel', nil, function(bagName, key, value, _reserved, replicated)
    local entity = GetEntityFromStateBagName(bagName)
    if entity == 0 then return end
    print(('Fuel of %d is now %.1f'):format(entity, value))
end)
```

- The second argument filters by bag name (`nil` for all bags). Bag names look like `player:12`, `entity:345` or `global`.
- `GetEntityFromStateBagName` and `GetPlayerFromStateBagName` turn a bag name back into a handle.
- The entity may not exist yet on this client when the handler fires, so check for `0`.

### Rules and limits

- **Flat keys only.** Nested writes like `Entity(x).state.car.fuel = 5` don't sync, the getters and setters are naive. Use `Entity(x).state['car:fuel']` or set the whole table again.
- **Keep values small.** Every replicated change goes to every client that can see the bag.
- **Security**: by default a client can write its own player bag and bags of entities it owns (not replicated unless it chooses to). If your resources only set state from the server, set `sv_stateBagStrictMode true` so clients can't write at all. Never trust a value a client could have written.

## When to use what

| You want | Use |
| --- | --- |
| A value everyone can read, changes rarely (weather, server flags) | `GlobalState` |
| Per player data other scripts or clients need (job, duty, radio channel, is dead) | `Player(src).state` |
| Data that belongs to a car or object (fuel, locked, owner, siren mode) | `Entity(ent).state` |
| A one off action ("play this sound now") | An event |
| A request with an answer | A callback |
| Large or private data | Server memory or database, send only what's needed |

## Entity ownership

- `NetworkGetEntityOwner(entity)` (server) returns the server ID of the client that owns it.
- Ownership moves as players move. Scripts that change an entity (freeze it, set its velocity) should do it on the owner, or on the server with the server setters.
- Before changing a networked entity on a client that doesn't own it, request control: `NetworkRequestControlOfEntity(entity)` and wait for `NetworkHasControlOfEntity(entity)`. It can fail or take time.
- `SetEntityOrphanMode` (server) controls what happens to an entity when its owner leaves, so server created vehicles don't vanish.

## Creating entities on the server

With OneSync the server can create entities, which is safer than trusting clients to spawn things:

```lua title="server.lua"
RegisterNetEvent('garage:takeOut', function(plate)
    local src = source
    -- ...check that src owns this car and is near the garage...
    local coords = vector4(215.0, -810.0, 30.7, 157.0)
    local veh = CreateVehicleServerSetter(`sultan`, 'automobile', coords.x, coords.y, coords.z, coords.w)
    while not DoesEntityExist(veh) do Wait(0) end
    SetVehicleNumberPlateText(veh, plate)
    Entity(veh).state:set('owner', GetPlayerIdentifierByType(src, 'license'), true)
    TriggerClientEvent('garage:enter', src, NetworkGetNetworkIdFromEntity(veh))
end)
```

The client then gets the entity from the network ID:

```lua title="client.lua"
RegisterNetEvent('garage:enter', function(netId)
    local veh = NetworkGetEntityFromNetworkId(netId)
    local timeout = GetGameTimer() + 3000
    while not DoesEntityExist(veh) and GetGameTimer() < timeout do
        Wait(0)
        veh = NetworkGetEntityFromNetworkId(netId)
    end
    if DoesEntityExist(veh) then
        SetPedIntoVehicle(PlayerPedId(), veh, -1)
    end
end)
```

`CreateVehicleServerSetter` is the recommended server vehicle native (it takes the vehicle type, like `automobile`, `bike`, `heli`). `CreateVehicle`, `CreatePed` and `CreateObjectNoOffset` also exist on the server. If you enable `sv_entityLockdown strict`, the server is the **only** place entities can be created, so this pattern becomes required.

## Routing buckets

A routing bucket is a separate world. Players and entities in different buckets can't see or interact with each other. Bucket `0` is the default.

```lua title="server.lua"
-- put a player in their own apartment instance
SetPlayerRoutingBucket(src, 1000 + src)
SetRoutingBucketPopulationEnabled(1000 + src, false)  -- no ambient peds and traffic there
SetRoutingBucketEntityLockdownMode(1000 + src, 'strict')

-- back to the main world
SetPlayerRoutingBucket(src, 0)
```

Use cases: character selection, apartments and instanced interiors, races, events, admin testing. Entities created by a player in a bucket stay in that bucket (`SetEntityRoutingBucket` moves them).

[How to use Routing Buckets (CharlesHacks)](https://www.youtube.com/watch?v=Bh2JuQoWbno) (YouTube video)

## Common mistakes

- Expecting a client written state value to show up on other clients: it doesn't unless replicated, and the server should be the writer anyway.
- Nested table writes to state bags (see limits above).
- Keeping entity handles across machines: send network IDs.
- Deleting or changing an entity you don't own and wondering why it comes back.
- Forgetting that players in another routing bucket are invisible to each other, then debugging "sync bugs".

Next: [NUI basics](/docs/development/nui).
