Skip to main content
summer-match-results 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. End a Summer match: report each player’s win, loss or draw, show results on clients, then close the World.

When your agent uses it

  • A multiplayer Summer game needs a winner, a game-over screen or a match that ends
  • Reporting match outcomes, abandons or rating changes from the authority
  • Players disconnect, reconnect or leave mid-match and the authority must handle it
  • The World should shut down after a match, or a persistent World runs several matches

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

Overview

A match ends when the authority says so. It decides the outcome for every player, then:
  1. Concludes the match: Summer.authority.match.conclude(outcomes, reason). Summer settles it once and returns a settlement, including each player’s rating change on a rated queue.
  2. Summer delivers each player their own result on the client (Summer.client.ratings.match_result), and later their committed rating (rating_committed). The authority never relays results or ratings.
  3. Completes the World: Summer.authority.world.complete() for a match-scoped World. Clients are disconnected and go back to your menu.
Where it works: all of this runs under Local Play with a deterministic settlement, so the whole flow is testable locally. Rating values are real only when hosted. This skill extends the Courtyard game (multiplayer-project and multiplayer-state): the first player to collect WIN_COINS coins wins. With a team queue (summer-matchmaking), their whole team wins.

Steps

1. Track who is in the match

In authority/main.gd, keep every Session the engine admitted, and the players who left. Add these variables:
In _ready(), next to the other World signals (skip this line if summer-player-data already connected it):
If the game already has _on_session_joined (summer-player-data adds one), put the line at its start instead of defining it twice. At the top of _on_session_left:
Keep a Session in sessions after it leaves: it stays on the match roster until the match is delivered, and it still needs an outcome. Presence signals on the authority’s SummerNetworkWorld: Hosted, a seat waits lifecycle.reconnect_seconds (world.json) before Summer ends the Session. Under Local Play a quitting player only disconnects; session_left arrives when the World ends.

2. Decide the outcome and conclude

At the end of _collect, after request.accept(...):
Then add:
Give match_doc a "winner": "" entry so clients can show the winner’s name. The rules conclude enforces:
  • One outcome for every outcome-eligible player admitted into the match, including players who already left (MATCH_OUTCOME_ABANDON). A missing or extra player fails with invalid_roster; spectators are skipped.
  • Keys must be the exact SummerSession objects the engine delivered (session_joined, or request.get_session() in a Command). Copies, such as the Sessions in Summer.authority.context.participants, fail with invalid_roster.
  • Outcomes are MATCH_OUTCOME_WIN, MATCH_OUTCOME_LOSS, MATCH_OUTCOME_DRAW and MATCH_OUTCOME_ABANDON. On a team queue give every member of a team the same outcome; Summer scores the sides it formed.
  • It settles once. Retrying with identical arguments replays the same settlement (concluding.replayed is true); different arguments are refused. A rejected conclusion (invalid_request) commits nothing, so you may conclude again with corrected arguments.
  • rating_deltas, the third argument, is only for authority_v1 queues (summer-leaderboards).
The settlement lists each player’s committed rating changes:
Use it for authority logic (logs, rewards). Players get their own numbers directly from Summer, step 3.

3. Show the result on each client

In client/main.gd’s _ready(), connect the result signals right after Summer.initialize succeeds and before joining (Summer.client is null until then), and handle the end of the World:
show_status and the Hud/Status label come from summer-matchmaking.
  • match_result arrives when the authority concludes. Its rating_state is RATING_STATE_PENDING on a rated queue, or RATING_STATE_NONE.
  • rating_committed follows when Summer committed the rating, with result.rating.before, after and delta. Hosted only.
  • Summer.client.ratings.last_result and results keep recent results, so a results screen opened later can read them.
  • Drive the results screen from match_result, not from a disconnect. Give it a “Back to menu” button that awaits Summer.client.leave(), then offers “Play again” (find_match again).
  • Hosted, completing the World ends every Session and Summer stops the server, so clients get server_disconnected. Under Local Play the World completes but clients stay connected until they leave or Local Play stops.

4. Close the World

  • Match-scoped World ("lifetime": "match_scoped", the project skill’s default): conclude, then Summer.authority.world.complete(reason). This permanently ends the World; Summer stops the server. Completing before a successful conclusion fails with settlement_required.
  • Persistent or multi-match World: conclude, then Summer.authority.match.complete_delivery(). Match-bound Sessions end, World-bound Sessions stay, and the World keeps serving. Never call world.complete() on a World that should keep running.
  • draining(deadline_unix) on the World means Summer is shutting the server down (maintenance). Conclude or save before the deadline (summer-world-saves).
A match that never concludes holds its players until the World is stopped; every match needs an ending, including “everyone else left”.

5. Test it with Local Play

Make one player win. Expect, in order: the authority’s conclusion succeeds, with a settlement of deterministic test ratings (system deterministic-v1); every client receives match_result with its own outcome and rating_state RATING_STATE_PENDING on a rated queue; about 3 seconds later the authority logs session_left with reason match_complete for each player and world.complete() succeeds. rating_committed does not fire locally, and clients stay connected until they leave.

Checklist

  • There is exactly one place that ends the match, guarded so it runs once.
  • Outcomes cover every outcome-eligible participant, including those who left (MATCH_OUTCOME_ABANDON).
  • Team queues give a whole team the same outcome.
  • A retryable conclusion failure is retried with identical arguments.
  • Clients show match_result, and rating_committed when it arrives.
  • Match-scoped Worlds call world.complete() after concluding; persistent Worlds call match.complete_delivery().
  • Local Play shows the result on every client, and “Back to menu” leaves cleanly.

Common mistakes

See also

  • summer-matchmaking — queues and teams that the outcome refers to
  • summer-leaderboards — ratings and authority_v1 rating moves
  • summer-world-saves — persistent Worlds that outlive a match
  • multiplayer-testing — running bots through a whole match

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.
  • multiplayer: Start here for Summer multiplayer: how authority, clients, Worlds and Sessions fit, and what comes next.
  • multiplayer-state: Authority-owned state in Summer multiplayer: shared and private State groups, Commands, Events.
  • summer-matchmaking: Get players into matches with Summer matchmaking: queues, teams, accept prompts, join progress and failures.
  • summer-leaderboards: Leaderboards in a Summer game: queue ratings from matches, and Game rankings your authority scores.
  • summer-world-saves: Keep a persistent Summer World between restarts: checkpoint saves, restore before join, the final save.
  • multiplayer-testing: Test a Summer multiplayer game with Local Play: several players, bad network, bots, a passing run.
  • summer-analytics: Send game events to Summer analytics from the authority or client: fire-and-forget, never blocking gameplay.

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