Skip to main content
πŸŽ‰ GP CONF 2026: WEB Games Market Conference πŸ‘Ύ September 23 | Details

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:

Properties:

Events:

Define a Global Schema​

Global state uses the same schema options as player state:

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 },
},
});

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():

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);

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.

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);
}
}

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:

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);

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:

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);
}
}

Peers can react to changes with globalStateUpdated:

function onGlobalStateUpdated(globalState) {
updateRoundInterface(globalState.round);
}

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:

function startHostLogic() {
const latestState = gp.multiplayer.globalState;

hostState = latestState ?? {
enemies: [],
items: [],
round: {
number: 1,
timeLeft: 60,
status: 'playing',
},
};

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:

await gp.multiplayer.connect({ channelId });

if (gp.multiplayer.isHost) {
startHostLogic();
}

Keep the becameHost subscription because host migration can happen later.

Clean Up​

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!