Please read the docs before reading this guide.
Disclaimer: Entity state bags are a OneSync feature, if you do not have OneSync they will not work.
With the addition of Entity State Bags back in v2843, there’s now a better way to sync data across multiple players. Previously the way to do this was to use Decors, but it wasn’t able to be accessed/modified on the server.
Replication
State bags will only replicate to every player when set to replicate.
Only the owning player of an entity can replicate an entity’s state bag, a prime example of this would be the Player
state bag, if you’re Player(1)
you would only be able to replicate the state on Player(1)
, but not any other players state, such as Player(2)
. Attempting to do the code below with Player(1)
substituted as Player(2)
will get rejected by the server as we’re not Player(2)
.
By default if you’re on the server, Player(1).state.someState = true
will automatically replicate to every client, while if you’re on the client Player(1).state.someState = true
will only change the state bag for your client.
If you want to replicate some value on the client you would instead have to use
Player(1).state:set( --[[keyName]] 'alive', --[[value]] false, --[[replicate to server/client]] true)
You should also not put any large-scale data into state bags if you plan on replicating the data, if you need to sync large-scale data you should use Latent Events, which doesn’t block the clients’ network thread (sadly there’s no available documentation on them currently).
Its also useful to note not every entity is networked, in that case you can still use state bags on that entity, but when the entity leaves the player scope it will be removed alongside the state bag tied to it.
How to use GlobalStates:
GlobalStates are a global variable that is shared across every resource, server, and client-side. Any variable stored in a GlobalState will be replicated to the client, so don’t put any sensitive data them.
GlobalStates are also immutable to the client.
Server:
GlobalState.curState = 'yes!'
print(GlobalState.curState) -- returns 'yes!'
Client:
print(GlobalState.curState) -- returns 'yes!'
GlobalState.curState = 'no!' -- doesn't work, client can't change global state.
How to use state bags:
There are two different types of state bags, one for the player Player(plySrc)
, and one for the entity Entity(entityId)
.
State bags can be initialized on the client, but they cannot replicate data to the server unless it's created on the server.
In the example below ‘source’ is assumed to be the player source.
Server:
local ply = Player(source).state
ply.alive = true
print(ply.alive) -- returns true
Client:
print(LocalPlayer.state.alive) -- returns true as we set it to true in the previous example
LocalPlayer.state:set('alive', false, true)
print(LocalPlayer.state.alive)
This works exactly the same for Entities
In the example below ‘vehNet’ is assumed to be the entity’s network id.
local ent = Entity(NetworkGetEntityFromNetworkId(vehNet))
ent.state.exploded = false
print(ent.state.exploded) -- returns false
Client:
local ent = Entity(NetToVeh(vehNet))
print(ent.state.exploded) -- returns false
ent.state:set('exploded', true, true)
print(ent.state.exploded) -- returns true
State Bag Change Handlers
State Bag Change handlers are a way to listen to when a specific state bag value gets changed.This means you can do specific logic on a state bag change, like cause a explosion on the specified entity
-- setting bag filter to 'entity:5' instead of nil would only listen to the event for the entity with the network id of 5.
AddStateBagChangeHandler('exploded' --[[key filter]], nil --[[bag filter]], function(bagName, key, value, _unused, replicated)
-- we only want to cause an explosion when the value is set to true!
if not value then return end
local ent = GetEntityFromStateBagName(bagName)
-- the entity didn't exist
if ent == 0 then return end
local entCoords = GetEntityCoords(ent)
AddExplosion(entCoords, 0xFFFFFFFF, 1.0, true, false, 1.0)
end)