multiplayer-testing 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. Test a Summer multiplayer game with Local Play: several players, bad network, bots, a passing run.
When your agent uses it
- Running or testing a Summer multiplayer game locally with several players (Local Play)
- Testing multiplayer under latency, jitter or packet loss, or with bots
- A multiplayer run says ok but nothing networked works, or deciding whether a multiplayer change is done
Install
summer setup installs every skill for your agent. To add only this one:
claude-code for your agent, such as codex or cursor. See Skills for every option.
The skill
Step 4 of the multiplayer sequence (multiplayer explains the model).
Local Play runs a Summer multiplayer game on one machine. It starts the
World’s authority scene headless and N clients, and each client joins through
the game’s own Summer.client.join. No account, server or extra tools are
needed. It can also make the network bad on purpose, which is where
multiplayer bugs show.
1. Start Local Play
Pick whichever the session has. Summer MCP tools (they set project settings, then play):<summer> is the Summer editor executable). Import once after
adding scripts so their classes register:
2. Make the network bad
Loopback has no latency, so it hides every timing bug. Always run once with these settings; they apply to every client:project.godot while testing:
NETWORK_EMULATION round_trip_ms=100 ... when it applies. Try 200 ms
too: that is a player on another continent.
3. Read the run
Every output line is prefixed by its process:[authority], [player-1],
[player-2]. The last line is the summary:
-
"ok":true, and every process has"script_errors":0. - The game’s own ready lines appear: the authority’s, and one per client after its
joinCommand (COURTYARD_AUTHORITY_READY,COURTYARD_CLIENT_JOINEDin the Courtyard example). Print one in your game too. - No
[SummerNetworkSpawner] cannot attachwarning on any process. - No
SCRIPT ERRORlines.
"ok":true alone only proves that clients got seats. If the composition is
invalid, the Spawner logs cannot attach as a warning and nothing networked
works, yet every client still counts as joined. The smoke flag
(--summer-local-play-smoke) proves the same thing and no more.
With MCP, summer_get_diagnostics reports runtime errors from the debugger.
Read it after every play; summer_get_console alone misses them.
4. Drive the game with bots
To test gameplay without hands, give the game a bot mode. Arguments after-- reach every process, and {client} becomes 1, 2, … for players and 0
for the authority:
client/bot.gd. It presses the same input actions a player
does, so it tests the real controls:
client/main.gd only when the argument is there, after the
player has joined:
-
[player-1] BOT_MOVEDand[player-2] BOT_SAWreport the same position (the observer is up to 100 ms behind while moving, then equal). -
[player-1] BOT_COLLECT {"ok":true...}and both players print the new score.
client/: files under test/ or other debug folders are
left out of the players’ pack, so the shipped game could not load them.
Headless clients have no real window. Guard calls that need one (mouse
capture, window size) with DisplayServer.get_name() != "headless".
5. What Local Play cannot show
- Hosted-only services answer with an error code locally: parties, friends, World chat, the store, client leaderboards and analytics. Their skills list the codes. Make the UI hide or explain them; never crash on them.
- Matchmaking’s accept prompt. Local Play seats players directly: no proposal, one World per queue.
- A player leaving for good. A client that quits or calls
Summer.client.leave()only disconnects.session_disconnectedfires and their seat is held.session_leftarrives when the World ends; hosted, it arrives when Summer ends the Session. - Which scene the hosted server starts. Local Play starts the authority
scene by name. Summer starts the exported
server.pckwith no scene, so a server preset without thesummer_authorityfeature still passes here and then runs the title screen hosted, where every join fails withworld_start_timeout. Run the bundle’sserver.pckwith no scene (multiplayer-publish, step 4d). - A wrong Game ID. Local Play has no hosted identity, so
Summer.initialize("some-id")passes here and fails hosted withgame_identity_mismatch. CallSummer.initialize()with no ID; only a real online join after publishing proves it. - Real devices, real accounts, real distances. Publish to staging for that
(
multiplayer-publish).
release-readiness: it covers these
gaps on the .zip you upload.
Checklist for any multiplayer change
- Ran Local Play with at least 2 players, and once with 100 ms / 10 ms / 1 %.
- Every process: no
SCRIPT ERROR, nocannot attach,script_errors0. - The game’s own ready lines appear for the authority and every client.
- Your own player responds instantly and never snaps while moving normally.
- Other players move smoothly and end where their owners are.
- Shared state matches on every client; private state reaches only its owner.
- A refused Command changes nothing and shows its reason.
Common mistakes
Next
multiplayer-publish: export the summer.games bundle and upload it as a Build.
From a shell
No Summer MCP tools in this session? Run each one as a command; whensummer --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.
Related skills
multiplayer: Start here for Summer multiplayer: how authority, clients, Worlds and Sessions fit, and what comes next.multiplayer-project: Set up a Summer multiplayer project: entry scenes, World and queue files, joining, a first Local Play.multiplayer-movement: Movement in Summer multiplayer: owners move instantly, others glide, the authority checks each pose.multiplayer-state: Authority-owned state in Summer multiplayer: shared and private State groups, Commands, Events.multiplayer-publish: Ship a hosted Summer multiplayer game: the bundle, server preset, pre-upload checks, a new Build.release-readiness: Before releasing a Summer game version, prove players can play it: server, online join, progress, files.summer-friends: Summer friends in a game: a consented friends list, profile and chat screens, direct messages. Hosted only.summer-match-results: End a Summer match: report each player’s win, loss or draw, show results on clients, then close the World.summer-matchmaking: Get players into matches with Summer matchmaking: queues, teams, accept prompts, join progress and failures.summer-parties: Let a Summer party play together: the leader presses Play, members follow into the same match. Hosted only.summer-player-data: Save a player’s progress so it follows them to every match: authority load and commit, autosave, safe retries.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.
Source:
library/skills/multiplayer-testing/SKILL.md, rendered from summer-engine@4.3.0 on npm.
