/multiplayer — how Summer multiplayer works
Read this first, whatever the user asked for: "multiplayer", "co-op", "PvP", "online", "play with friends", "lobby", "peer-to-peer", "host a game", "netcode", "sync players". It explains the model in one page and sends you to the skill that builds the next piece.
The picture
A Summer multiplayer game is one project with two entry scenes:
- the client scene, which every player runs: input, camera, visuals, UI;
- the authority scene, which runs headless as the match server and owns the rules.
texteach player's device Summer hosting (or Local Play) ┌──────────────────────┐ my pose ┌──────────────────────────────┐ │ client scene │ ─────────────► │ authority scene = one World │ │ moves its own player │ "collect" │ checks every pose and request│ │ draws everyone else │ ─────────────► │ owns score, doors, items... │ │ │ ◄───────────── │ │ └──────────────────────┘ state, events └──────────────────────────────┘
- A running match is a World. Summer starts one authority for it; the game
declares what a World is in
world.json(a WorldDefinition). - A player joins with
Summer.client.join(...)and gets a verified Session: their seat in that World, with a verified player identity. - Players find a World through a queue in
summer.build.json. Hosted, Summer's matchmaking fills it; locally, Local Play starts the authority and N clients on your machine. - The same code runs locally and hosted. No
if server, no local-only branches, no IP addresses.
There is no player-hosted server, so there is no host migration, and a player quitting never ends the match for the others.
How data moves
Both sides load one network composition (network/composition.tres). It
declares every stream the game uses, and a Spawner node attaches it to the
World:
| You want | Use | Who writes it | Skill |
|---|---|---|---|
| My character moves | Owner-written State group, one per player | that player's client; the authority checks each pose | multiplayer-movement |
| Everyone sees the score, a door, the round timer | Authority-written State group, World audience | the authority | multiplayer-state |
| Only I see my wallet, hand, inventory | Authority-written State group, target-Session audience | the authority | multiplayer-state |
| A player wants to buy, open, collect, ready up | Command | client asks, authority accepts or refuses | multiplayer-state |
| A one-off effect (sparkle, sound, hit flash) | Event | the authority | multiplayer-state |
State is retained, so a late joiner gets the current value. Commands carry the sender's verified Session. Events are fire-and-forget.
The rules
- Each client moves its own character. Movement feels instant on every connection. The authority accepts or refuses each pose, and a refused pose snaps the player back.
- The authority owns every other fact. Clients never change shared state; they send a Command and show the result.
- Identity comes from the Session, never from a field a client sent.
- Never use the stock high-level multiplayer API in a Summer game: no
@rpc,MultiplayerSynchronizer,MultiplayerSpawner,ENetMultiplayerPeer,multiplayer.is_server()or peer-ID branches. Their sender ids are transport peers, not verified players. Late joiners miss state, and the game cannot use Summer hosting, matchmaking or identity. - Test under a bad network before calling anything done: Local Play
with latency, jitter and loss (
multiplayer-testing).
Words
| Word | Meaning |
|---|---|
| Game | Your game on Summer; its id is gameId in summer.build.json. Summer.initialize() takes no ID: Summer knows which game is running |
| World | One running match or shared place, simulated by one authority |
| WorldDefinition | world.json: which scenes run the client and the authority, lifetime, persistence |
| Queue | How players find a World; declared in summer.build.json |
| Session | One player's verified seat in a World |
| Composition | The network contract both sides load: State streams, Command and Event streams |
| State group | One retained value (a pose, a scoreboard); one writer, an audience |
| Command | A request from a client, answered by the authority |
| Event | A one-off message from the authority |
| Local Play | Runs the authority and N clients locally, with optional network emulation |
Build it in this order
Each skill continues the same small game, so their code fits together:
multiplayer-project: the files, both entry scenes, joining, and a first Local Play run.multiplayer-movement: each player moves their own character; others see it smoothly; the authority checks every pose.multiplayer-state: score, doors, private inventories, Commands and Events.multiplayer-testing: Local Play under latency and loss, bots, and what a passing run must show.multiplayer-publish: export the summer.games bundle and upload it as a Build.release-readiness: before players get a version, prove the server starts its authority scene and a real online join works.
Add Summer services
| Skill | What it adds | Works in Local Play? |
|---|---|---|
summer-matchmaking | Queues, match sizes, teams, the accept prompt, join failures | Yes, without the accept prompt |
summer-match-results | Ending a match: winners, losers, rating changes | Yes |
summer-leaderboards | Ratings and Game-wide leaderboards | Partly; reading boards needs hosting |
summer-parties | Friends queue together as a party | No, hosted only |
summer-friends | The player's Summer friends and direct messages | No, hosted only |
summer-player-data | Progress that follows the player to any World | Yes, stored on disk |
summer-world-saves | Worlds that persist between sessions | Yes, in memory |
summer-world-chat | Text chat inside a World | No, hosted only |
summer-store | Selling items for Sparks, owned items | Items yes; the store is hosted only |
summer-analytics | Game events for your analytics dashboard | No, hosted only |
What people ask for, and what Summer does
| They ask for | On Summer |
|---|---|
| "One player hosts", peer-to-peer | A headless authority per match, started by Summer or by Local Play |
| Host migration | Not needed: no player is the authority |
| Join by IP, invite code, server browser | Not available. Players join a queue; friends join as a party (summer-parties) |
| A lobby | A queue with minPlayers/maxPlayers and an accept prompt (summer-matchmaking) |
| "Sync the player's position" | An owner-written pose group (multiplayer-movement) |
| "Tell everyone the door opened" | An authority-written State group (multiplayer-state) |
| "The client asks the host to buy something" | A Command validated with the verified Session (multiplayer-state) |
| Save progress | Player data for the player, World saves for the place (summer-player-data, summer-world-saves) |
Start
Check what exists: summer.build.json, world.json, network/,
authority/, client/.
- None of them: start with
multiplayer-project. - They exist: pick the skill for the piece you need from the tables above.
- The game already uses
@rpcorMultiplayerSynchronizer: tell the user it needs moving to Summer's networking. Then followmultiplayer-projectand port one system at a time: movement first, then state.
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.
| In this skill | Command |
|---|---|
summer_get_scene_tree | summer engine get-scene-tree |
summer_get_script_errors | summer engine get-script-errors |
summer_project_setting | summer engine project-setting |


