Global State
Global state contains shared game entities and values controlled only by the host:
- enemies and NPCs;
- items on the map;
- round state and timers;
- shared score and objectives.
Global state is synchronized separately from individual player state.
Method Listβ
Actions:
gp.multiplayer.defineGlobalSchema()- define synchronized shared fields. FREEgp.multiplayer.setGlobalState()- set the host-controlled global state. FREEgp.multiplayer.onTick()andoffTick()- manage the shared-world logic loop. FREE
Properties:
gp.multiplayer.globalState- get the latest global state. FREE
Events:
globalStateUpdated- track global state updates on peers.becameHostandbecamePeer- start and stop host-only logic.
Define a Global Schemaβ
Global state uses the same schema options as player state:
- JavaScript
- Unity
gp.multiplayer.defineGlobalSchema({
enemies: {
id: { readonly: true },
type: { readonly: true },
x: { interpolate: true },
y: { interpolate: true },
hp: { interpolate: false },
},
items: {
id: { readonly: true },
type: { readonly: true },
x: { interpolate: false },
y: { interpolate: false },
},
round: {
number: { interpolate: false },
timeLeft: { interpolate: true },
status: { interpolate: false },
},
});
GP_Multiplayer.defineGlobalSchema(new GP_Data(@"{
""enemies"": {
""id"": { ""readonly"": true },
""type"": { ""readonly"": true },
""x"": { ""interpolate"": true },
""y"": { ""interpolate"": true },
""hp"": { ""interpolate"": false }
},
""round"": {
""number"": { ""interpolate"": false },
""timeLeft"": { ""interpolate"": true },
""status"": { ""interpolate"": false }
}
}"));
Enable interpolation for values that should change smoothly, such as coordinates. Disable it for values that should be applied immediately, such as health, animation state, or an item identifier.
Create State on the Hostβ
Only the host can call setGlobalState():
- JavaScript
- Unity
let hostState = null;
function startHostLogic() {
hostState = gp.multiplayer.globalState ?? {
enemies: [],
items: [],
round: {
number: 1,
timeLeft: 60,
status: 'playing',
},
};
gp.multiplayer.setGlobalState(hostState);
}
function stopHostLogic() {
hostState = null;
}
gp.multiplayer.on('becameHost', startHostLogic);
gp.multiplayer.on('becamePeer', stopHostLogic);
GP_Data hostState;
void StartHostLogic()
{
hostState = GP_Multiplayer.globalState ?? new GP_Data(@"{
""enemies"": [],
""items"": [],
""round"": {
""number"": 1,
""timeLeft"": 60,
""status"": ""playing""
}
}");
GP_Multiplayer.setGlobalState(hostState);
}
void StopHostLogic() => hostState = null;
GP_Multiplayer.on("becameHost", StartHostLogic);
GP_Multiplayer.on("becamePeer", StopHostLogic);
In JavaScript, setGlobalState() stores a reference to the supplied object, so the host can mutate that object directly. In Unity, call setGlobalState() after changing the C# state model to pass its latest serialized value to the SDK.
- JavaScript
- Unity
function spawnEnemy() {
if (!gp.multiplayer.isHost || !hostState) {
return;
}
hostState.enemies.push({
id: crypto.randomUUID(),
type: 'slime',
x: 100,
y: 200,
hp: 50,
});
}
function damageEnemy(enemyId, damage) {
if (!gp.multiplayer.isHost || !hostState) {
return;
}
const enemy = hostState.enemies.find((item) => item.id === enemyId);
if (enemy) {
enemy.hp = Math.max(0, enemy.hp - damage);
}
}
void SpawnEnemy()
{
if (!GP_Multiplayer.isHost)
return;
worldState.enemies.Add(new EnemyState
{
id = Guid.NewGuid().ToString(),
type = "slime",
x = 100,
y = 200,
hp = 50
});
GP_Multiplayer.setGlobalState(
new GP_Data(JsonUtility.ToJson(worldState))
);
}
In JavaScript, you do not need to call setGlobalState() again after every push, splice, or field update. The host sends the referenced object at the selected tick rate. In Unity, submit the serialized state again after changing the C# model.
Update Shared Entitiesβ
Run authoritative shared-world logic only on the host:
- JavaScript
- Unity
function updateGlobalState(delta) {
if (!gp.multiplayer.isHost || !hostState) {
return;
}
const seconds = delta / 1000;
for (const enemy of hostState.enemies) {
enemy.x += 40 * seconds;
}
hostState.round.timeLeft = Math.max(
0,
hostState.round.timeLeft - seconds,
);
}
gp.multiplayer.onTick(updateGlobalState);
void UpdateGlobalState(float delta)
{
if (!GP_Multiplayer.isHost)
return;
float seconds = delta / 1000f;
foreach (EnemyState enemy in worldState.enemies)
enemy.x += 40f * seconds;
worldState.round.timeLeft = Mathf.Max(
0,
worldState.round.timeLeft - seconds
);
GP_Multiplayer.setGlobalState(
new GP_Data(JsonUtility.ToJson(worldState))
);
}
GP_Multiplayer.onTick(UpdateGlobalState);
Peers must not mutate gp.multiplayer.globalState. They receive an interpolated snapshot for rendering.
Read Global Stateβ
The latest global state is available to both host and peers:
- JavaScript
- Unity
function renderWorld() {
const globalState = gp.multiplayer.globalState;
if (!globalState) {
return;
}
for (const enemy of globalState.enemies) {
drawEnemy(enemy);
}
for (const item of globalState.items) {
drawItem(item);
}
}
GP_Data globalState = GP_Multiplayer.globalState;
if (globalState != null)
Debug.Log(globalState.Data);
Peers can react to changes with globalStateUpdated:
- JavaScript
- Unity
function onGlobalStateUpdated(globalState) {
updateRoundInterface(globalState.round);
}
gp.multiplayer.on('globalStateUpdated', onGlobalStateUpdated);
void OnGlobalStateUpdated(GP_Data globalState)
{
Debug.Log(globalState.Data);
}
GP_Multiplayer.on("globalStateUpdated", OnGlobalStateUpdated);
The event handler receives the global state object directly. The event is emitted on peers after interpolation; the host already owns the current state reference.
Handle Host Migrationβ
When a peer becomes the new host, gp.multiplayer.globalState contains its latest received snapshot. Pass that object to setGlobalState() so it becomes the new live host reference:
- JavaScript
- Unity
function startHostLogic() {
const latestState = gp.multiplayer.globalState;
hostState = latestState ?? {
enemies: [],
items: [],
round: {
number: 1,
timeLeft: 60,
status: 'playing',
},
};
gp.multiplayer.setGlobalState(hostState);
}
void StartHostLogic()
{
GP_Data latestState = GP_Multiplayer.globalState;
hostState = latestState ?? CreateInitialGlobalState();
GP_Multiplayer.setGlobalState(hostState);
}
This allows the shared world to continue from the latest known state instead of restarting after host migration.
After connecting, also handle the case where the role has already been selected:
- JavaScript
- Unity
await gp.multiplayer.connect({ channelId });
if (gp.multiplayer.isHost) {
startHostLogic();
}
await GP_Multiplayer.connect(new MultiplayerChannelQuery { channelId = channelId });
if (GP_Multiplayer.isHost)
StartHostLogic();
Keep the becameHost subscription because host migration can happen later.
Clean Upβ
- JavaScript
- Unity
gp.multiplayer.offTick(updateGlobalState);
gp.multiplayer.off('globalStateUpdated', onGlobalStateUpdated);
gp.multiplayer.off('becameHost', startHostLogic);
gp.multiplayer.off('becamePeer', stopHostLogic);
hostState = null;
GP_Multiplayer.offTick(UpdateGlobalState);
GP_Multiplayer.off("globalStateUpdated", OnGlobalStateUpdated);
GP_Multiplayer.off("becameHost", StartHostLogic);
GP_Multiplayer.off("becamePeer", StopHostLogic);
hostState = null;
Stay in Touchβ
Other documents of this chapter are available here. To get started with GamePush, see the Tutorials chapter.
GamePush Community Telegram: @gs_community.
For your suggestions e-mail: official@gamepush.com
We Wish you Success!