Skip to main content
multiplayer-project 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. Set up a Summer multiplayer project: entry scenes, World and queue files, joining, a first Local Play.

When your agent uses it

  • Making a Summer game playable online or co-op: the first multiplayer setup
  • Adding multiplayer to a Summer game: the client and authority scenes, summer.build.json, world.json and joining a queue
  • A Summer multiplayer project has no summer.build.json or world.json yet, or Local Play cannot find the World
  • Laying out a multiplayer project in client/, authority/ and network/ folders

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 1 of the multiplayer sequence (multiplayer explains the model). By the end, Local Play starts a headless authority and two clients. Each client joins through the game’s own Summer.client.join, and every client sees one player per joined player. The example game is Courtyard. The later skills (multiplayer-movement, multiplayer-state) continue it, so keep its names until the user’s own game replaces them. Every file below was run as shown with Local Play; files.md has all of them complete, scenes included.

1. Ask, then state the plan

  1. What is the game called? Its id is game_<name>: lowercase letters, digits, _, . or -.
  2. How many players per match? Summer measures up to 16.
  3. Which platforms: iOS, macOS, Windows?
State the plan before writing anything:
I’ll add Summer multiplayer: a client scene and a headless authority scene, a World and a queue for 2–8 players, a network composition, and Local Play to test it. Each folder holds one side: client/, authority/, network/ (shared). OK?
For an existing single-player game, get this skeleton running first, then move its gameplay under client/.

2. The layout

Client code and shared code must never load a file from authority/. The summer.games export leaves authority/ out of the players’ pack. The authority may load shared files.

3. project.godot

Add to the project’s project.godot:
The .summer_client and .summer_authority overrides tell each exported pack which scene to start. Summer starts the hosted server with no scene argument, so run/main_scene.summer_authority is the only thing that makes server.pck start the authority. It applies only when the server export preset carries the custom feature summer_authority (multiplayer-publish). Without it the server starts the client scene, never reports ready, and every online join fails with world_start_timeout. Local Play names the scene itself, so it works either way. Leave the [summer] version line the editor writes.

4. The declarations

summer.build.json: the game id, the queue players join, and the World it starts. Summer rejects unknown fields, so copy the shape exactly.
  • gameId is the game’s id in Summer Studio, for your publishing tools. The summer.games export leaves it out of the bundle, so game code never reads it: both sides call Summer.initialize() with no ID (sections 7 and 8).
  • queues[].minPlayers / maxPlayers bound a match. worldDefinition names the definition_id in world.json.
  • server.exportPreset names the server export preset (multiplayer-publish).
  • runtime.interfaces, config, presence and lobby are required. Copy them as shown.
world.json: what one World is. Keep every field; Local Play accepts less, but publishing needs all of them.
runtime-manifest.json describes the server component:
source-domains.json marks the folders. The export uses it to keep server code off players’ devices:

5. The network composition

Both sides load the same composition. It declares every stream the game sends, and client and authority refuse each other when theirs differ. Start with one Command stream; later skills add more. network/composition.tres:
  • A Command’s payload and result travel as JSON in a byte field (type = 20, TYPE_PACKED_BYTE_ARRAY): a network String holds at most 256 bytes.
  • command_scope = 2 is COMMAND_SCOPE_WORLD.
  • command_tick_dispatch = true makes the authority handle a Command after the tick’s player poses are installed, so its checks use current positions.
The player entity’s schema is empty, because players move through State groups (multiplayer-movement). network/player_schema.tres:

6. Shared code

network/game.gd holds every id and builds the network nodes the same way on both sides:
Each side passes its own player archetype: the scenes the Spawner spawns for every joined player. The authority gets a model with no visuals. Clients get an owner scene for their own player and an observer scene for everyone else. Splitting them keeps client scenes out of the server and server scenes out of the client.

7. The authority

The authority’s player archetype, authority/player_archetype.tres, names only an authority_model_scene: authority/player_model.tscn, a bare Node3D. Both are in files.md. authority/main.gd initializes Summer, waits for its World, and answers the join Command:
authority/main.tscn is a plain Node with that script (files.md).
  • Summer.initialize() takes no Game ID. Summer takes the game’s identity from where it runs: Local Play, the hosted server or the Summer Games app. Never pass a literal, a placeholder or a fallback ID: any ID that differs from the hosted one fails with game_identity_mismatch, and the authority stops before it reports ready.
  • Never read who sent a Command from its payload: request.get_session() is verified. Don’t name your own classes SummerSession, SummerWorld or other engine class names.
  • session_disconnected means the player’s connection dropped and their seat is held. session_left means they are gone for good. Free their things in session_left only. Under Local Play it arrives when the World ends.

8. The client

The client’s player archetype, client/player_archetype.tres, names an owner_projection_scene and an observer_projection_scene:
client/player_owner.tscn is a CharacterBody3D with a capsule and the camera. client/player_observer.tscn is a Node3D with a capsule and no collision. Both are in files.md. client/main.gd joins the queue, waits until the network is ready, then tells the authority it has arrived:
client/main.tscn holds the level (a floor and a light) and that script (files.md).
  • Connect state_group_created and entity_spawned before joining, or values that arrive during the join go unseen.
  • Summer.client.join succeeding means the player has a seat. Send Commands only after spawner.network_ready.
  • Hosted, matchmaking may ask the player to accept a match. This skeleton accepts it right away; summer-matchmaking shows a real prompt.

9. Run it with Local Play

Import once so new scripts register, then start two players. With the Summer MCP tools:
In the editor, Debug > Local Multiplayer sets the same player count; then press Play. From a terminal (<summer> is the Summer editor executable):
Read the processes’ output. Each line is prefixed with [authority], [player-1] or [player-2]:
  • [authority] COURTYARD_AUTHORITY_READY
  • [authority] COURTYARD_PLAYER_SPAWNED twice and COURTYARD_PLAYER_JOINED twice
  • COURTYARD_CLIENT_JOINED from each player
  • No SCRIPT ERROR, and no [SummerNetworkSpawner] cannot attach warning
  • The last line, SUMMER_LOCAL_PLAY_RESULT {...}, has "ok":true and "script_errors":0 for every process
cannot attach means the composition is invalid, and nothing networked will work. Local Play still reports every client as joined, so look for the warning yourself.

Common mistakes

Next

multiplayer-movement: each player moves their own character, and everyone else sees it move smoothly.

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-state: Authority-owned state in Summer multiplayer: shared and private State groups, Commands, Events.
  • multiplayer-testing: Test a Summer multiplayer game with Local Play: several players, bad network, bots, a passing run.
  • multiplayer-publish: Ship a hosted Summer multiplayer game: the bundle, server preset, pre-upload checks, a new Build.
  • 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-world-saves: Keep a persistent Summer World between restarts: checkpoint saves, restore before join, the final save.

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