summer-world-chat 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. Add match chat to a Summer game: one World channel shared by everyone in the match. Hosted only.
When your agent uses it
- Adding in-game text chat, match chat or a chat box to a multiplayer Summer game
- Chat messages fail with text_not_allowed, rate_limited or unavailable, or chat history looks incomplete
- Deciding where player chat should live instead of sending it through Commands or State
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
A World channel is a text channel for the World (match) the player has joined. Every player in that World can open the same channel by key, such as"match". The platform owns everything that matters about it:
- who may read it;
- the text filter;
- rate limits;
- 24-hour retention;
- blocks between players.
Hosted only. Chat needs a joined hosted World and a Summer app whose Player
Host offers
player.channels. A queue reservation is not enough: open the
channel after enter() succeeds. In Local Play and editor runs,
check_open_readiness() and open_world_channel() fail with unavailable.
Build the “chat hidden” path first.
This skill extends the Courtyard game from multiplayer-project and
multiplayer-state.
Steps
1. Publish player names
Messages carry the author’s Summer user id (player_id), not a name. The
authority already knows every verified name. Publish them in the shared match
document (multiplayer-state): in authority/main.gd’s _join, before
accepting, add
match_doc.get("players", {}) into the chat node’s
names whenever the match document changes.
2. Add the chat node
client/chat.gd, added as a child Chat of Main in client/main.tscn:
$Chat.open() after enter() returns true. Wire it into the UI:
- Messages: a
LabelorRichTextLabelwithbbcode_enabled = false, refilled fromlines_changed. - Input: a
LineEditwhosetext_submittedcalls$Chat.send(text)and clears itself. - Status:
statusfeeds a status line; hide the whole box when chat is unavailable.
3. Rules the code above follows
- Render, don’t append.
history_changedmeans “replace the view fromget_messages()”. It also fires when a block or expiry removes messages, which must disappear from the screen. message_receivedis for sounds and badges only. It fires for newly seen messages, not for the initial or older history. Display still comes fromget_messages().- One
send_messagecall is one message. For an ambiguous retryable failure, callretry()on the same operation; it reuses the original idempotency key. A secondsend_messagewould post twice. text_not_allowedstored nothing. The filter refused it, and resending the same text fails again.rate_limited. About 120 messages a minute per player; tell them to slow down, don’t loop.- Sequence gaps are normal. Blocks, other recipients and expiry hide messages; never treat a gap as an error.
creator_systemmessages are from the game, never from a player or from Summer staff.
4. Test
Locally, check that the hidden path works and nothing errors:Checklist
- Local Play:
open()reports chat unavailable, the chat box is hidden, and there are no script errors. - The channel opens only after the player entered the World.
- Hosted: a message from one player appears for the other, with the right name.
- A filtered message shows “not sent” and never appears.
- After leaving and joining another match, chat reopens on a new handle.
- Text is shown as plain text:
[b]hi[/b]appears literally.
Common mistakes
See also
multiplayer-state: the match document that carries player namessummer-friends: direct messages with friendssummer-parties: party chat lives in the Summer appmultiplayer-testing: what Local Play can and can’t test
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-friends: Summer friends in a game: a consented friends list, profile and chat screens, direct messages. Hosted only.summer-parties: Let a Summer party play together: the leader presses Play, members follow into the same match. Hosted only.
Source:
library/skills/summer-world-chat/SKILL.md, rendered from summer-engine@4.3.0 on npm.
