summer-player-data 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. Save a player’s progress so it follows them to every match: authority load and commit, autosave, safe retries.
When your agent uses it
- Saving player progress, coins, unlocks, levels or settings in a multiplayer Summer game so they persist between matches
- A player’s progress resets every match, or saves fail with save_id_conflict, superseded or operation_in_progress
- Showing a player’s saved progress in the menu before they join a match
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
Overview
Player data is what Summer remembers about one player in one game: coins, unlocks, level, settings. It belongs to the player, not to a match. A match World ends and its state is gone. The player’s data comes back in the next match, on any server and any device.- Only the authority writes it. Clients never write player data.
- Two slots per player, written separately:
public: the authority reads and writes it. The player’s own client can read it too, for example to show coins in the menu.secret: only the authority reads or writes it, for hidden values such as anti-cheat counters or hidden ratings. It never reaches a client.
- Nobody else can read either slot.
- Each slot is one JSON object of at most 64 KiB.
- Allowed values:
bool,int, finitefloat,String,ArrayandDictionarywithStringkeys, nested at most 16 deep. Vector3,Colorandnullfail before anything is sent.- Whole numbers load back as
int.
- Allowed values:
Local Play’s store keeps the latest value plus five earlier revisions, with
the same validation and
save_id rules as hosted. Delete a persona’s folder
to start that player fresh. Only hosted play proves durability.
This skill extends the game from multiplayer-project and
multiplayer-state: the authority’s _join Command handler, the
private mine State group, and the wallets it keeps.
Steps
1. Decide what to save
Save only what must outlive the match, and save it as a plain document:
Keep values that must change together in one slot: there is no
transaction across the two slots.
2. Load when the player arrives
The authority starts loading onsession_joined, before the client’s
join Command arrives. Add to authority/main.gd:
_ready(), next to the other world signals, before Summer.initialize
(connect session_joined only once if summer-match-results already
did):
_on_session_joined already exists (summer-match-results adds
one), put this body at its end instead of defining it twice.
loaded.secret_record.data holds the secret slot. A slot that was never
written has exists == false and empty data.
3. Make join wait for the load
In _join, right after the seat check:
client/main.gd, retry join while the answer is
loading:
profile_unavailable means the load failed. Show “Couldn’t load your
progress”, and do not let the player play on a blank profile that would
overwrite their real one.
4. Mark changes, autosave, flush on leave
Wherever progress changes (here, after the wallet changes in_collect):
_on_session_left:
- Autosave, not only save-on-leave. A crash delivers no signal. With a 5 s autosave a crash loses at most 5 s.
- One commit per player and slot at a time. A second concurrent commit fails with
operation_in_progress. - A new
save_idfor each new save, the samesave_idfor a retry.- Resending a
save_idwith the same data returns the original revision (commit.replayed). - The same
save_idwith different data fails withplayer_data_save_id_conflict. - Build the id from the time the Session joined plus a counter. Session ids are not enough: Local Play reuses them on every run.
- Resending a
player_data_supersededis final. The player joined a newer World, and only their newest Session may write.drainingfires before the World shuts down. Commits are still accepted until the stop deadline, so flush everyone then.- Under Local Play a player who quits only disconnects. Their seat is held and
session_leftarrives when the World ends, so the autosave is what saves them.
commit_secret in the same way for the secret slot. Each slot has its own
revisions and save_id space.
5. Show saved progress in the menu (optional)
The client reads its own public slot, even before joining a match:6. Keep the publish checks passing
The source-graph check inmultiplayer-publish (step 4b) flags every
call ending in load( without a string literal inside, so
Summer.authority.player_data.load(session) and
Summer.client.player_data.load() report
SUMMER_COMPOSITION_DYNAMIC_PATH_UNDECLARED. They load no file. Declare one
dependency edge per file that makes such a call, in source-domains.json:
7. Test persistence: run Local Play twice
Run the same session twice. Local Play personas (player-1, player-2)
keep their files between runs, so run 2 must start where run 1 ended.
<summer> --path . --summer-local-play 2 --summer-local-play-headless --summer-local-play-timeout 20 -- --bot={client},
twice (see multiplayer-testing for bot mode). Print markers such as
print("PROFILE_LOADED coins=", ...) and print("SAVED ", save_id, " revision=", commit.revision) while testing, then check them.
Files: ~/Library/Application Support/Godot/app_userdata/<project name>/summer_local_player_data/<game id>/<persona>/public.json
on macOS (the project’s user:// folder elsewhere). Delete a persona’s
folder to start over.
Checklist
- Run 1: a player earns something, and the authority logs a successful save within the autosave interval.
- Run 2: the authority loads the saved value at join; the client menu shows it.
- A load failure refuses
joinwithprofile_unavailable; nothing is written over the old data. - No
player_data_save_id_conflictin the logs, even on the second run. - The saved document stays well under 64 KiB at its largest. Test the maximum.
Common mistakes
See also
multiplayer-state— the wallet and private State group this skill savessummer-world-saves— saving a whole persistent World instead of one playermultiplayer-testing— bots and repeated Local Play runssummer-leaderboards— scores other players can see
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-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.summer-world-saves: Keep a persistent Summer World between restarts: checkpoint saves, restore before join, the final save.summer-leaderboards: Leaderboards in a Summer game: queue ratings from matches, and Game rankings your authority scores.summer-store: Sell Summer items for Sparks in a multiplayer game: shop, checkout, owned items, authority entitlement checks.
Source:
library/skills/summer-player-data/SKILL.md, rendered from summer-engine@4.3.0 on npm.
