State Bags (Networking)
State Bags (or Entity State) are a modern networking feature in FiveM (requires OneSync) that allows you to store arbitrary key-value pairs on entities (players, vehicles, peds, objects) or globally. These values are automatically synchronized between the server and relevant clients.
They replace many traditional TriggerClientEvent patterns for syncing data like “is player handcuffed”, “vehicle fuel level”, or “job duty status”.
Why use State Bags?
- Automatic Sync: No need to manually trigger events when a player connects or comes into range. The state is “just there”.
- Bandwidth Efficient: Only sends updates when data changes.
- Persistent: Data stays on the entity as long as the entity exists.
- Server-Authoritative: You can secure states so clients cannot modify them.
Basic Syntax
State bags are accessed via the .state property on an entity.
Setting State (Server Side)
-- Set a value on a vehicle
local vehicle = GetVehiclePedIsIn(GetPlayerPed(source), false)
local ent = Entity(vehicle)
-- Set 'fuel' to 100.
-- The 'true' argument enables replication to clients.
ent.state:set('fuel', 100, true)
-- Set a value on a player
local playerEnt = Entity(GetPlayerPed(source))
playerEnt.state:set('isHandcuffed', true, true)Reading State (Client & Server)
local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
local ent = Entity(vehicle)
-- Access directly like a table
local fuel = ent.state.fuel
if fuel then
print("Current fuel:", fuel)
endGlobal State
GlobalState is a special state bag that is not attached to any specific entity but is available everywhere. It’s perfect for server-wide variables like “weather”, “server announcement”, or “event active”.
-- Server: Set global state
GlobalState.weather = "EXTRASUNNY"
-- Client: Read global state
print("Current weather:", GlobalState.weather)State Change Handlers
You can listen for changes to state bags using AddStateBagChangeHandler. This is useful for triggering visual effects or logic when a value changes.
-- Client Side: Listen for 'isHandcuffed' changes on any player
AddStateBagChangeHandler('isHandcuffed', nil, function(bagName, key, value, _reserved, replicated)
-- bagName format: "entity:12345" or "player:1"
local entity = GetEntityFromStateBagName(bagName)
-- Verify the entity exists and is a player
if entity == 0 then return end
if value == true then
-- Apply handcuff animation
print("Player handcuffed!")
else
-- Remove animation
print("Player released!")
end
end)Replication and Security
When setting a state bag value, the third argument controls replication:
-- replicated: true = sends to clients
-- replicated: false = stays on server only
entity.state:set('secretKey', '12345', false) Local vs. Replicated
- Server: Can set replicated state (visible to clients) or local state (server only).
- Client: By default, clients can only set local state (visible to themselves). They cannot replicate state to the server or other players unless the server allows it.
To allow a client to replicate a specific state key, use SetResourceKvp policy (advanced) or trust the client to trigger a server event to change the state. Best Practice: Always have the server set important game states.
Player State (PlayerState)
For player entities, you can access state via the player’s server ID.
-- Server Side
local playerState = Player(source).state
playerState.job = "police"
-- Client Side
local myState = LocalPlayer.state
print(myState.job) -- prints "police"Best Practices
- Don’t Overuse: State bags are for state (persistent data). Don’t use them for fleeting events (like playing a sound once). Use standard Events for that.
- Use Namespaces: Prefix your keys to avoid collisions with other resources (e.g.,
myScript:fuelinstead of justfuel). - Security: Never trust client-set state for critical logic (money, permission, etc.).
Summary Table
| Feature | Syntax |
|---|---|
| Get Entity | Entity(entityHandle) |
| Get Player | Player(serverId) |
| Get Local Player | LocalPlayer |
| Set State | ent.state:set(key, value, replicated) |
| Get State | ent.state.key |
| Global State | GlobalState.key |
| Listen for Change | AddStateBagChangeHandler(...) |