Network sync: shared state (server-validated actions)
Syncs a shared, versioned state through server-validated actions, with no simulation tick: turn-based and board games, card games with hidden hands, party games, lobbies and persistent data.
Network sync: shared state (server-validated actions) (net.state) #
What it is for #
Syncs a shared, versioned state through server-validated actions, with no simulation tick: turn-based and board games, card games with hidden hands, party games, lobbies and persistent data.
It needs net.transport, game.engine; installing it brings them in.
Turn it on #
Install the kit in a project, then change a setting (the values shown are its defaults):
gripforge_gamekit_install { project_id: "gkp_…", id: "net.state" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "net.state",
config: { "preset": "none", "initial": null } }
First example #
<!-- test: kits/net_state/tests/net_state.rs::an_action_travels_as_a_command_and_answers_its_id_a_refusal_answers_null --> This is the body of the passing test an_action_travels_as_a_command_and_answers_its_id_a_refusal_answers_null; the helper functions it calls are defined at the top of the same test file.
#[test]
fn an_action_travels_as_a_command_and_answers_its_id_a_refusal_answers_null() {
let mut s = online(json!({}));
answer(&mut s, "c:act", json!(21));
assert_eq!(
call(&mut s, KIT, "act", json!(["draw", { "n": 2 }])),
json!(21)
);
let sent = commands(&mut s);
let act = sent.iter().find(|c| c["c"] == "act").unwrap();
assert_eq!(act["type"], "draw");
assert_eq!(act["args"], json!({ "n": 2 }));
// No arguments: an empty object travels.
call(&mut s, KIT, "act", json!(["pass"]));
let sent = commands(&mut s);
assert_eq!(sent.last().unwrap()["args"], json!({}));
call(&mut s, "probe.core", "refuse", json!(["act"]));
assert_eq!(call(&mut s, KIT, "act", json!(["draw", {}])), Json::Null);
assert!(logged(&s, "net.state: act draw: act refused"));
}
Settings and defaults #
Descriptions come from the kit manifest and stay in English.
| Setting | Type | Default | Range or values | Meaning |
|---|---|---|---|---|
preset | string | "none" | none, board-game, card-game, party-lobby, rooms, simultaneous-duel | Ready-made option set applied between the defaults and this config (defaults ← preset ← your keys, merged key by key): board-game… |
initial | object | — | Initial state document (a JSON object). | |
actions | object | — | Action types: {type: {args: schema, turn?, phase?, who?, when: [conditions], effects: [effects], endTurn?, hook?, secret?}} (see… | |
visibility | array | — | Hidden information: [{path: "/hands/*", to: owner | none | [seats], ownerField?, reveal?: conditions, others: hide | count |… | |
turns | object | {"enabled": false, "timeoutMs": 0, "autoStart… | ||
seats | object | {"max": 8, "rejoinByName": true, "takeOver": … | ||
simultaneous | object | {"enabled": false, "choicesPath": "/choices",… | Simultaneous decisions (secret choices resolved together). | |
end | object | {"onLeave": false, "joinTimeoutMs": 30000, "p… | The terminal state of a match: set once by the end command, a hook answer with end, or a player leaving (onLeave). | |
rooms | object | {"enabled": false, "challengeTtlMs": 30000, "… | Rooms service rules (see the README). | |
history | object | {"max": 64} | ||
undo | object | {"enabled": false, "max": 8, "by": "actor"} | ||
crdt | object | — | CRDT fields without arbitration: {"/path": "counter" | "lww" | "set"}. | |
crdtLimits | object | {"maxSetTags": 1024, "maxValueBytes": 1024, "… | Bounds of the CRDT fields (server and clients). | |
timers | object | — | Named timers: {name: {effects: [...]}} run when the timer (started by the timer effect or command) expires. | |
maxTimers | integer | 32 | 1–1024 | Named timers running at once (a new one beyond is ignored). |
hookUnknown | boolean | false | Undeclared action types go to the server hook instead of being refused. | |
hookTimeoutMs | integer | 5000 | 50–120000 | An action the hook does not answer in time is rejected (hook_timeout). |
rosterPath | string | "/players" | Where the seats are mirrored in the state ([{name, on}]); empty = off. | |
phasePath | string | "/phase" | The phase the phase conditions and effects use. | |
maxStateBytes | integer | 262144 | 1024–16777216 | An action that would grow the state beyond this is rejected (too_big). |
maxArgsBytes | integer | 1024 | 16–16384 | Largest action arguments (JSON bytes; clients check it before sending). |
maxEffects | integer | 256 | 1–100000 | Effects one action (or timer, or turn default) may run, nested ones included. |
maxEffectCount | integer | 10000 | 1–1000000 | Largest count of a deal / draw effect. |
actionsPerSecond | number | 10 | 0.1–1000 | Per peer; beyond, actions are rejected (rate). |
actionsBurst | 0 | Per-peer action bucket (0 = auto: 2 × actionsPerSecond). | ||
maxQueuedActions | integer | 256 | 1–65536 | Server: actions waiting for the hook, all peers together; beyond, rejected (busy). |
maxQueuedActionsPerPeer | integer | 16 | 1–4096 | Server: the same per peer. |
maxPatchBytes | integer | 48000 | 1024–16777216 | A patch larger than this is replaced by the full view (chunked by chunkBytes); ≤ transport maxMessageBytes. |
maxPendingActions | integer | 64 | 1–4096 | Client: actions in flight (sent, not yet accepted or rejected). |
maxLogEntries | integer | 4096 | 16–1000000 | Client: history entries kept. |
seed | integer | 0 | 0–4294967295 | Seed of shuffle / random (0 = random per match). |
crdtResendMs | integer | 250 | 20–60000 | Client CRDT writes are resent this often until the server has them. |
crdtSyncMs | integer | 1000 | 50–60000 | Server re-broadcast period of the CRDT states (anti-entropy). |
chunkBytes | integer | 32768 | 1024–1048576 | Full states larger than this travel in chunks (≤ transport maxMessageBytes). |
playerName | string | — | Web runtime: the player name sent in the handshake (used by rejoinByName). |
Services, events, variables #
- Service
net.sync: The project replication model (one per project: the net.* sync kits conflict with each other). - Service
net.state: act(type, args) / state() / turn() / history() / undo() / crdt(); patch, accepted, rejected, turn, timer events; server hook, snapshots. - Events emitted:
net.state.sync(v,seat) — The full view arrived (join, resume, resync).net.state.patch(v,ops) — The view changed (compact JSON ops).net.state.accepted(id,v) — An action of this client was applied.net.state.rejected(id,why) — An action of this client was refused (rule reason, hook reason, rate, disconnected).net.turn(seat,mine,n,round,remainingMs,active) — The turn changed.net.timer(name,remainingMs,expired) — A timer started, was cancelled or expired (turnfor the turn timer).
- Variables:
net.status(string) — Network statusnet.rtt(number) — Ping (ms)net.peers(number) — Players onlinenet.turn_seat(number) — Seat to playnet.my_turn(boolean) — My turnnet.turn_ms(number) — Turn time leftnet.state_version(number) — State version
Hooks and modding #
This kit declares no hook and no collection to mod.
Engines #
Declared targets: web, godot, unity, unreal. gripforge kit api net.state lists the exact settings and services of your version.
Versions #
Kit version 1.3.0.