Skip to main content
multiplayer-state is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Authority-owned state in Summer multiplayer: shared and private State groups, Commands, Events.

When your agent uses it

  • Score, health, doors, rounds, inventories or wallets in a Summer multiplayer game, and who owns them
  • Players need to collect, buy, use, open or trade things and the authority must validate it
  • Clients desync, cheat or double-spend; sending one-off effects to every player

Install

summer setup installs every skill for your agent. To add only this one:
Swap claude-code for your agent, such as codex or cursor. See Skills for every option.

The skill

Step 3 of the multiplayer sequence. It continues Courtyard from multiplayer-movement: players walk to coins, collect them, and see a shared scoreboard, a private wallet and a sparkle. files.md has every file this step changes, complete, as it ran under Local Play.

How it works

Except for each player’s own pose, the authority owns every fact: score, health, doors, inventories, rounds, who won. Clients never change shared state. They ask with a Command; the authority checks the request against the sender’s verified Session and the current state, changes the state, and answers. State is retained: a player who joins late receives the current value. Events are not. Before writing code, list the game’s facts in this table and confirm it with the user.

1. Declare the streams

Add three streams to network/composition.tres: Match (shared document), Mine (private document) and Fx (an Event):
and list them in the [resource]:
Update load_steps in the first line. files.md has the complete file.
  • Documents travel as JSON in a byte field (type = 20). A network String holds at most 256 bytes, which a document outgrows fast.
  • Audiences: max_expansion and max_audience_count default to 0, which reaches nobody. Use the player count for World audiences and 1 for a private one.
  • Event streams set kind = 2, command_scope = 0, event_scope = 2 (World), event_delivery = 2 (Transient), a transient_ttl_ticks, and max_outcome_history = 0 and command_timeout_msec = 0. Those two default to non-zero. With the defaults, the Spawner logs cannot attach and the whole network stays down.
Add the stream ids to network/game.gd:

2. The authority decides

authority/main.gd:
The rules that matter:
  • Identity comes from request.get_session(), never from the payload.
  • Positions come from the accepted pose group, never from the payload. Commands set command_tick_dispatch, so the tick’s poses are installed before the handler runs.
  • Check every precondition before changing anything: joined, item still there, in reach, can afford it.
  • Refuse with a stable reason id (&"too_far"). The client turns it into text.
  • Every action carries a request id. Keep each player’s answers, and answer a retry with the stored result. A Command that timed out on the client may already have been applied, and a blind resend would apply it twice.
  • Change state only through groups. reset(document) replaces the value for everyone in its audience, late joiners included.
  • Retire a player’s private group in session_left. Authority-written groups outlive a disconnect, so a reconnecting player gets their wallet back.

3. The client asks and shows

client/main.gd keeps the documents the authority publishes and asks for coins. Add the two documents:
connect Events with the other signals in _ready():
route the two document streams in _on_group_created, and add the handlers:
  • state_group_created fires with the group’s first value already installed. Read it with group.get_state() right there; state_installed reports only later changes.
  • The client never edits match_doc or mine. It shows what the authority published. For instant feedback, show a pending state (“collecting…”) until the Command answers.
  • JSON numbers come back as floats ("coins": 1.0). Convert with int() before showing or comparing.
  • Use Events for effects only. A Transient Event can be lost on a bad network; anything that must arrive belongs in State.

4. Test it

Run Local Play with 2 players and network emulation (multiplayer-testing):
  • Both clients receive the shared document; each receives only its own private document.
  • An accepted Command changes the shared document on every client.
  • A refused Command changes nothing and returns its reason id (too_far, coin_gone).
  • Sending a Command again with the same request id returns the same answer and applies once.
  • A client that joins late sees the current scores at once.

Common mistakes

Next

multiplayer-testing: bots and a bad network. Then multiplayer-publish.

From a shell

No Summer MCP tools in this session? Run each one as a command; when summer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.

Files in this skill

  • multiplayer: Start here for Summer multiplayer: how authority, clients, Worlds and Sessions fit, and what comes next.
  • multiplayer-movement: Movement in Summer multiplayer: owners move instantly, others glide, the authority checks each pose.
  • multiplayer-testing: Test a Summer multiplayer game with Local Play: several players, bad network, bots, a passing run.
  • summer-player-data: Save a player’s progress so it follows them to every match: authority load and commit, autosave, safe retries.
  • summer-match-results: End a Summer match: report each player’s win, loss or draw, show results on clients, then close the World.
  • multiplayer-project: Set up a Summer multiplayer project: entry scenes, World and queue files, joining, a first Local Play.
  • summer-analytics: Send game events to Summer analytics from the authority or client: fire-and-forget, never blocking gameplay.
  • summer-store: Sell Summer items for Sparks in a multiplayer game: shop, checkout, owned items, authority entitlement checks.
  • summer-world-chat: Add match chat to a Summer game: one World channel shared by everyone in the match. Hosted only.
  • summer-world-saves: Keep a persistent Summer World between restarts: checkpoint saves, restore before join, the final save.

Source: library/skills/multiplayer-state/SKILL.md, rendered from summer-engine@4.3.0 on npm.