Installs into .claude/skills of the current project.
Are you the author of Multiplayer?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/summerengine-multiplayer-summer)
---
name: multiplayer
description: "Start here for Summer multiplayer: how authority, clients, Worlds and Sessions fit, and what comes next."
license: MIT
compatibility: [Cursor, Claude Code, Windsurf, Codex]
category: multiplayer-and-networking
user-invocable: true
allowed-tools: Read Grep Glob summer_get_scene_tree summer_get_script_errors summer_project_setting
paths: ["**/*.gd", "**/*.tscn", "project.godot", "summer.build.json", "world.json"]
---
# /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.
```text
each 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
1. **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.
2. **The authority owns every other fact.** Clients never change shared
state; they send a Command and show the result.
3. **Identity comes from the Session**, never from a field a client sent.
4. **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.
5. **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:
1. **`multiplayer-project`**: the files, both entry scenes, joining, and a
first Local Play run.
2. **`multiplayer-movement`**: each player moves their own character; others
see it smoothly; the authority checks every pose.
3. **`multiplayer-state`**: score, doors, private inventories, Commands and
Events.
4. **`multiplayer-testing`**: Local Play under latency and loss, bots, and
what a passing run must show.
5. **`multiplayer-publish`**: export the summer.games bundle and upload it as
a Build.
6. **`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 `@rpc` or `MultiplayerSynchronizer`:** tell the user
it needs moving to Summer's networking. Then follow `multiplayer-project`
and port one system at a time: movement first, then state.
<!-- shell-commands: generated from the operation catalog by npm run generate:registry; do not edit by hand -->
## 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@4.3.0` 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` |
<!-- /shell-commands -->