Skip to main content
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:
Swap 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.
Your game owns only the chat UI. Messages never travel through your network composition, and the authority neither sees nor writes them. 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
On the client, copy 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:
Call $Chat.open() after enter() returns true. Wire it into the UI:
  • Messages: a Label or RichTextLabel with bbcode_enabled = false, refilled from lines_changed.
  • Input: a LineEdit whose text_submitted calls $Chat.send(text) and clears itself.
  • Status: status feeds a status line; hide the whole box when chat is unavailable.

3. Rules the code above follows

  • Render, don’t append. history_changed means “replace the view from get_messages()”. It also fires when a block or expiry removes messages, which must disappear from the screen.
  • message_received is for sounds and badges only. It fires for newly seen messages, not for the initial or older history. Display still comes from get_messages().
  • One send_message call is one message. For an ambiguous retryable failure, call retry() on the same operation; it reuses the original idempotency key. A second send_message would post twice.
  • text_not_allowed stored 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_system messages are from the game, never from a player or from Summer staff.

4. Test

Locally, check that the hidden path works and nothing errors:
Then test real chat on Summer staging with two accounts in one match.

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 names
  • summer-friends: direct messages with friends
  • summer-parties: party chat lives in the Summer app
  • multiplayer-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; 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.
  • 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.