summer-engine 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +120 -0
- package/README.md +22 -7
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/setup.js +6 -19
- package/dist/cli/commands/skills.js +57 -32
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +20 -4
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/core/skills-registry.d.ts +9 -1
- package/dist/core/skills-registry.js +25 -2
- package/dist/installer/agent-config.d.ts +9 -4
- package/dist/installer/agent-config.js +194 -392
- package/dist/installer/agent-table.d.ts +119 -0
- package/dist/installer/agent-table.js +619 -0
- package/dist/installer/setup.js +4 -2
- package/dist/installer/skill-locations.d.ts +8 -17
- package/dist/installer/skill-locations.js +22 -101
- package/dist/installer/version-check.d.ts +5 -1
- package/dist/installer/version-check.js +38 -28
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/README.md +48 -19
- package/integrations/amp/README.md +12 -0
- package/integrations/amp/manifest-target.json +3 -0
- package/integrations/antigravity/README.md +12 -0
- package/integrations/antigravity/manifest-target.json +3 -0
- package/integrations/claude-desktop/README.md +12 -0
- package/integrations/claude-desktop/manifest-target.json +3 -0
- package/integrations/cline/README.md +8 -9
- package/integrations/cline-cli/README.md +12 -0
- package/integrations/cline-cli/manifest-target.json +3 -0
- package/integrations/copilot-jetbrains/README.md +12 -0
- package/integrations/copilot-jetbrains/manifest-target.json +3 -0
- package/integrations/crush/README.md +12 -0
- package/integrations/crush/manifest-target.json +3 -0
- package/integrations/github-copilot/README.md +8 -8
- package/integrations/goose/README.md +12 -0
- package/integrations/goose/manifest-target.json +3 -0
- package/integrations/grok-build/README.md +12 -0
- package/integrations/grok-build/manifest-target.json +3 -0
- package/integrations/hermes/README.md +12 -0
- package/integrations/hermes/manifest-target.json +3 -0
- package/integrations/junie/README.md +12 -0
- package/integrations/junie/manifest-target.json +3 -0
- package/integrations/kilo-code/README.md +6 -10
- package/integrations/kimi-code/README.md +12 -0
- package/integrations/kimi-code/manifest-target.json +3 -0
- package/integrations/kiro/README.md +12 -0
- package/integrations/kiro/manifest-target.json +3 -0
- package/integrations/lm-studio/README.md +8 -8
- package/integrations/mistral-vibe/README.md +12 -0
- package/integrations/mistral-vibe/manifest-target.json +3 -0
- package/integrations/opencode/README.md +9 -12
- package/integrations/qoder/README.md +12 -0
- package/integrations/qoder/manifest-target.json +3 -0
- package/integrations/qwen-code/README.md +12 -0
- package/integrations/qwen-code/manifest-target.json +3 -0
- package/integrations/roo-code/README.md +8 -7
- package/integrations/rovo-dev/README.md +12 -0
- package/integrations/rovo-dev/manifest-target.json +3 -0
- package/integrations/trae/README.md +12 -0
- package/integrations/trae/manifest-target.json +3 -0
- package/integrations/visual-studio/README.md +12 -0
- package/integrations/visual-studio/manifest-target.json +3 -0
- package/integrations/vscode-copilot/README.md +9 -9
- package/integrations/warp/README.md +12 -0
- package/integrations/warp/manifest-target.json +3 -0
- package/integrations/windsurf/README.md +8 -9
- package/integrations/zed/README.md +12 -0
- package/integrations/zed/manifest-target.json +3 -0
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +264 -0
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +3 -3
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +264 -0
- package/registry/generated/skills-registry.json +901 -206
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-store
|
|
3
|
+
description: "Sell Summer items for Sparks in a multiplayer game: shop, checkout, owned items, authority entitlement checks."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: multiplayer-and-networking
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Edit Write summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics
|
|
9
|
+
paths: ["**/*.gd", ".summer/test-users.json"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /summer-store — sell items, check what players own
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Players pay with **Sparks**, Summer's currency. Your game never touches money,
|
|
17
|
+
balances or approval:
|
|
18
|
+
|
|
19
|
+
1. The client lists your published offers with `Summer.client.store.list_items()`.
|
|
20
|
+
2. The player presses Buy. The client asks for a checkout with offer ids and quantities, never prices.
|
|
21
|
+
3. The Summer app shows its own purchase sheet. The player approves or declines there.
|
|
22
|
+
4. Summer charges the Sparks and issues the item into the player's **inventory**.
|
|
23
|
+
5. The game sees the item in `Summer.client.items` (client) and `Summer.authority.items` (authority).
|
|
24
|
+
|
|
25
|
+
Three ids, kept apart:
|
|
26
|
+
|
|
27
|
+
| Id | Where it comes from | Use it for |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| Offer id | `item.price.offer_id` in the store listing | `SummerCheckoutLine.create(offer_id, quantity)` |
|
|
30
|
+
| Definition id | Your item definition in the Summer catalog | "Does this player own a hat?" |
|
|
31
|
+
| Owned item id | One owned copy (`item.get_owned_item_id()`) | Pointing at a specific owned copy |
|
|
32
|
+
|
|
33
|
+
The rule that makes it cheat-proof: **the authority decides what a player may
|
|
34
|
+
use by reading that player's inventory itself.** A client saying "I bought it"
|
|
35
|
+
proves nothing.
|
|
36
|
+
|
|
37
|
+
**What runs where:**
|
|
38
|
+
|
|
39
|
+
| Surface | Store and checkout | Inventory |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Hosted | Need the Summer app's `player.store.read@1` / `player.store.checkout@1` | Needs `player.items@1` |
|
|
42
|
+
| Local Play | `unavailable` | Works from a fixture: empty unless `.summer/test-users.json` lists item templates (step 5) |
|
|
43
|
+
| Guests, older Summer apps | Readiness reports `unavailable` | Readiness reports `unavailable` |
|
|
44
|
+
|
|
45
|
+
Items and offers are created in your game's Studio pages (`summer_open` target
|
|
46
|
+
`game`). Code never creates items.
|
|
47
|
+
|
|
48
|
+
This skill extends the Courtyard game from `skill/multiplayer-project` and
|
|
49
|
+
`skill/multiplayer-state`.
|
|
50
|
+
|
|
51
|
+
## Steps
|
|
52
|
+
|
|
53
|
+
### 1. The client shop
|
|
54
|
+
|
|
55
|
+
`client/shop.gd`, a child `Shop` of `Main`. In `client/main.gd`'s `_ready()`, call
|
|
56
|
+
`$Shop.start()` right after `Summer.initialize` succeeds:
|
|
57
|
+
|
|
58
|
+
```gdscript
|
|
59
|
+
extends Node
|
|
60
|
+
## A shop for Summer items, paid in Sparks. Summer shows its own purchase
|
|
61
|
+
## sheet; the game never sees money and never grants an item itself.
|
|
62
|
+
|
|
63
|
+
signal shelf_changed(rows: Array) # [{name, sparks, offer_id}]
|
|
64
|
+
signal checkouts_changed(busy: bool, rows: Array)
|
|
65
|
+
signal status(text: String)
|
|
66
|
+
|
|
67
|
+
var store: SummerClientStore
|
|
68
|
+
var items: SummerClientItems
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Main calls this right after Summer.initialize succeeds: a child's _ready
|
|
72
|
+
## runs before that, while Summer.client is still null.
|
|
73
|
+
func start() -> void:
|
|
74
|
+
store = Summer.client.store
|
|
75
|
+
items = Summer.client.items
|
|
76
|
+
# Connect before reading retained statuses; a normal (not deferred)
|
|
77
|
+
# connection is how the SDK knows a result was delivered.
|
|
78
|
+
store.checkout_status_changed.connect(func(_status: SummerCheckoutStatus) -> void: _render_checkouts())
|
|
79
|
+
items.refreshed.connect(_on_items_changed)
|
|
80
|
+
items.item_replaced.connect(func(_change: SummerInventoryChange) -> void: _on_items_changed())
|
|
81
|
+
items.item_removed.connect(func(_change: SummerInventoryChange) -> void: _on_items_changed())
|
|
82
|
+
_render_checkouts()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## Opening the shop screen.
|
|
86
|
+
func open() -> void:
|
|
87
|
+
var readiness := store.check_browse_readiness()
|
|
88
|
+
if not readiness.ok:
|
|
89
|
+
status.emit("The shop is not available here.") # hide the shop button
|
|
90
|
+
return
|
|
91
|
+
var page := store.list_items(50)
|
|
92
|
+
var listed: SummerResult = await page.get_result_or_completed_signal()
|
|
93
|
+
if not listed.ok:
|
|
94
|
+
status.emit("The shop could not load.")
|
|
95
|
+
return
|
|
96
|
+
var rows := []
|
|
97
|
+
for item in page.items:
|
|
98
|
+
if item.price != null and item.price.offer_id != "":
|
|
99
|
+
rows.append({"name": item.name, "sparks": item.price.sparks_amount, "offer_id": item.price.offer_id})
|
|
100
|
+
shelf_changed.emit(rows)
|
|
101
|
+
if items.check_refresh_readiness().ok:
|
|
102
|
+
items.refresh()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## The Buy button. One call is one purchase: never call it again to retry.
|
|
106
|
+
func buy(offer_id: String) -> void:
|
|
107
|
+
if not store.check_checkout_readiness().ok:
|
|
108
|
+
status.emit("Purchases are not available here.")
|
|
109
|
+
return
|
|
110
|
+
if store.has_pending_checkouts():
|
|
111
|
+
return
|
|
112
|
+
var lines: Array[SummerCheckoutLine] = [SummerCheckoutLine.create(offer_id, 1)]
|
|
113
|
+
var operation := store.request_checkout(lines)
|
|
114
|
+
_render_checkouts()
|
|
115
|
+
await operation.get_result_or_completed_signal()
|
|
116
|
+
_render_checkouts(operation)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
func _render_checkouts(operation: SummerCheckoutOperation = null) -> void:
|
|
120
|
+
var view := SummerCheckoutObservation.capture(store, operation)
|
|
121
|
+
var rows := []
|
|
122
|
+
for checkout in view.statuses:
|
|
123
|
+
rows.append({"id": checkout.request_id, "state": checkout.state, "error": checkout.error_code})
|
|
124
|
+
checkouts_changed.emit(view.has_pending_checkouts(), rows)
|
|
125
|
+
if view.submission_failure != null:
|
|
126
|
+
status.emit("The purchase did not start: %s" % view.submission_failure.code)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
## Ownership comes only from the inventory, never from a checkout result.
|
|
130
|
+
func owns(definition_id: String) -> bool:
|
|
131
|
+
return items.is_projection_loaded() and items.has_owned_definition(definition_id)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
func _on_items_changed() -> void:
|
|
135
|
+
status.emit("Inventory updated.")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Bind `checkouts_changed` to the Buy buttons: disable them while `busy` is
|
|
139
|
+
true. Update rows by `id`; a status can be delivered again after a crash, so
|
|
140
|
+
rendering must be idempotent. Show each checkout state:
|
|
141
|
+
|
|
142
|
+
| `state` | Show |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `awaiting_approval`, `pending` | "Waiting for Summer…" |
|
|
145
|
+
| `purchased` | "Bought!" The item arrives through the inventory signals, possibly a moment later |
|
|
146
|
+
| `declined`, `expired` | Nothing, or "Cancelled" |
|
|
147
|
+
| `rejected` with `error_code` `insufficient_funds` | "Not enough Sparks" (players get Sparks in the Summer app) |
|
|
148
|
+
| `rejected`, other codes | "The purchase was not completed" |
|
|
149
|
+
| `manual_review` | "We're confirming this purchase." Not done, not failed |
|
|
150
|
+
|
|
151
|
+
### 2. Unlock from the inventory only
|
|
152
|
+
|
|
153
|
+
Unlock cosmetics in menus with `owns(definition_id)`, and refresh the menu on
|
|
154
|
+
`refreshed`, `item_replaced` and `item_removed`. Never unlock from a
|
|
155
|
+
`purchased` status: the receipt proves the debit, the inventory proves
|
|
156
|
+
ownership, including after a reinstall or on another device. An empty inventory
|
|
157
|
+
before `is_projection_loaded()` is "still loading", not "owns nothing".
|
|
158
|
+
|
|
159
|
+
### 3. The authority checks entitlement
|
|
160
|
+
|
|
161
|
+
Anything that changes the match because of an item goes through a Command, and
|
|
162
|
+
the authority reads that Session's inventory. Add `"equip"` to the `match` in
|
|
163
|
+
`authority/main.gd`'s `_on_command`:
|
|
164
|
+
|
|
165
|
+
```gdscript
|
|
166
|
+
"equip":
|
|
167
|
+
_equip(request, str(command.get("definition", "")))
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```gdscript
|
|
171
|
+
## A player asks to wear a hat. The authority reads that Session's inventory
|
|
172
|
+
## itself; a client saying "I own it" proves nothing.
|
|
173
|
+
func _equip(request: SummerNetworkCommandRequest, definition_id: String) -> void:
|
|
174
|
+
var session := request.get_session()
|
|
175
|
+
var read := Summer.authority.items.inventory_for_session(session)
|
|
176
|
+
var result: SummerResult = await read.get_result_or_completed_signal()
|
|
177
|
+
if not result.ok:
|
|
178
|
+
request.refuse(&"inventory_unavailable") # retryable: try again later
|
|
179
|
+
return
|
|
180
|
+
for item in read.items:
|
|
181
|
+
if item.is_owned() and item.get_definition_id() == definition_id:
|
|
182
|
+
var hats: Dictionary = match_doc.get("hats", {})
|
|
183
|
+
hats[session.player.user_id] = definition_id
|
|
184
|
+
match_doc["hats"] = hats
|
|
185
|
+
match_group.reset(Game.pack(match_doc))
|
|
186
|
+
request.accept({}, Game.pack({"equipped": definition_id}))
|
|
187
|
+
return
|
|
188
|
+
request.refuse(&"not_owned")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The client sends `await command({"c": "equip", "definition": definition_id})`
|
|
192
|
+
and every client draws hats from `match_doc.hats`. Other players see the hat
|
|
193
|
+
only because the authority published it.
|
|
194
|
+
|
|
195
|
+
`Summer.authority.items.inventory_changed(session, change)` hints that a
|
|
196
|
+
player's inventory changed during the match. To drop an item that was
|
|
197
|
+
refunded mid-match, re-read with `inventory_for_session` (or
|
|
198
|
+
`refresh_inventory`) on that hint.
|
|
199
|
+
|
|
200
|
+
### 4. Rules and why
|
|
201
|
+
|
|
202
|
+
- **Offer ids and quantities only.** Never send a price, an amount or a buyer. The platform prices everything, and a client-side price is what a cheater edits.
|
|
203
|
+
- **One `request_checkout` is one purchase.** Each call gets a new identity. Never call it again because a result is slow; `is_outcome_unknown()` means it may already be charged, and the status will still arrive.
|
|
204
|
+
- **Connect `checkout_status_changed` normally, before reading statuses.** A synchronous handler is how the SDK knows the result was delivered; a deferred-only handler leaves it undelivered.
|
|
205
|
+
- **`pending` and `manual_review` are not failures.** Don't show success or failure until a terminal state.
|
|
206
|
+
- **Carts:** 1–16 different offers, at most 64 units, as one atomic purchase.
|
|
207
|
+
- **Hide what's unavailable.** `check_browse_readiness()` and `check_checkout_readiness()` return a `SummerResult`; show the shop only when they're `ok`.
|
|
208
|
+
|
|
209
|
+
### 5. Test
|
|
210
|
+
|
|
211
|
+
**Local Play.** The store and checkout report `unavailable`, so the hidden-shop
|
|
212
|
+
path is what you test. Inventory and the authority check work from a fixture:
|
|
213
|
+
create `.summer/test-users.json` with item templates.
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"schema": "summer.local-fixture.v1",
|
|
218
|
+
"fixture_id": "courtyard",
|
|
219
|
+
"fixture_version": 1,
|
|
220
|
+
"seed": 1,
|
|
221
|
+
"world_definition": "world.json",
|
|
222
|
+
"service_outcomes": [],
|
|
223
|
+
"catalog_item_templates": ["hat"],
|
|
224
|
+
"personas": [
|
|
225
|
+
{"persona_id": "alice", "display_name": "Alice", "participant_role": "player", "eligible_for_outcome": true, "session_lifetime": "match_bound"},
|
|
226
|
+
{"persona_id": "bob", "display_name": "Bob", "participant_role": "player", "eligible_for_outcome": true, "session_lifetime": "match_bound"}
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
What the fixture gives you:
|
|
232
|
+
|
|
233
|
+
- Every persona owns one item per template.
|
|
234
|
+
- Ids are local stand-ins. Bob's hat has owned item id `local-item:bob:0`, definition id `local-definition:courtyard:0`, and `item_template_version_id` `"hat"`.
|
|
235
|
+
- A check against your real catalog definition id is therefore `false` in Local Play. To exercise the authority path locally, equip with the local definition id the client reads from `items.get_owned_items()`.
|
|
236
|
+
|
|
237
|
+
**Staging.** Test real purchases there; Summer staging uses sandbox payments.
|
|
238
|
+
Walk through every outcome:
|
|
239
|
+
|
|
240
|
+
- decline the sheet;
|
|
241
|
+
- buy with too few Sparks;
|
|
242
|
+
- buy, then kill the game while the purchase is pending and relaunch: the result must arrive exactly once.
|
|
243
|
+
|
|
244
|
+
## Checklist
|
|
245
|
+
|
|
246
|
+
- [ ] Local Play: the shop button is hidden, and there are no script errors.
|
|
247
|
+
- [ ] Local Play with the fixture: `items.refresh()` succeeds, the owned item is listed, `equip` is accepted for it and refused with `not_owned` for anything else.
|
|
248
|
+
- [ ] No code path sends a price or grants an item from a checkout status.
|
|
249
|
+
- [ ] Buy is disabled while `has_pending_checkouts()` is true.
|
|
250
|
+
- [ ] `manual_review` shows "confirming", not success or failure.
|
|
251
|
+
- [ ] Staging: a purchase shows up in the inventory, and the equipped item appears for every player.
|
|
252
|
+
|
|
253
|
+
## Common mistakes
|
|
254
|
+
|
|
255
|
+
| Don't | Do | Why |
|
|
256
|
+
| --- | --- | --- |
|
|
257
|
+
| Unlock on `purchased` | Unlock from `Summer.client.items` | Ownership lives in the inventory; receipts can replay |
|
|
258
|
+
| Let the client tell the authority what it owns | `Summer.authority.items.inventory_for_session(session)` | Client claims are what cheaters forge |
|
|
259
|
+
| Send prices or amounts | Offer id + quantity only | The platform prices every purchase |
|
|
260
|
+
| Call `request_checkout` again on a slow result | Wait for its status; check `is_outcome_unknown()` | The first request may already be charged |
|
|
261
|
+
| Connect the status signal deferred or after reading statuses | Connect normally, first | Deferred handlers don't count as delivered; statuses can be missed |
|
|
262
|
+
| Treat `manual_review` as failed | Show "confirming" | The platform hasn't decided yet |
|
|
263
|
+
| Invent your own paid currency | Sell Summer items for Sparks | Summer can only protect, refund and pay out Sparks purchases |
|
|
264
|
+
| Assume the shop exists everywhere | Check readiness, hide when unavailable | Local Play, guests and older Summer apps have no store |
|
|
265
|
+
|
|
266
|
+
## See also
|
|
267
|
+
|
|
268
|
+
- `skill/multiplayer-state`: Commands and the match document used for equipped items
|
|
269
|
+
- `skill/multiplayer-testing`: Local Play fixtures and personas
|
|
270
|
+
- `skill/summer-player-data`: saving a player's loadout between matches
|
|
271
|
+
- `skill/summer-analytics`: measuring the shop
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
id: skill/summer-store
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
summary: "Sell Summer items for Sparks in a multiplayer game: shop, checkout, owned items, authority entitlement checks."
|
|
5
|
+
use_when:
|
|
6
|
+
- "adding a shop, in-game purchases, cosmetics or skins sold for Sparks to a Summer game"
|
|
7
|
+
- "checking on the authority that a player really owns an item before letting them use or equip it"
|
|
8
|
+
- "checkout shows pending, manual_review, insufficient_funds or unavailable, or bought items don't appear"
|
|
9
|
+
facets:
|
|
10
|
+
lifecycle:
|
|
11
|
+
- build
|
|
12
|
+
domains:
|
|
13
|
+
- multiplayer
|
|
14
|
+
- gameplay
|
|
15
|
+
modalities:
|
|
16
|
+
- scripts
|
|
17
|
+
compatibility:
|
|
18
|
+
engine: ">=4.6"
|
|
19
|
+
related:
|
|
20
|
+
skills:
|
|
21
|
+
- skill/multiplayer
|
|
22
|
+
- skill/multiplayer-state
|
|
23
|
+
- skill/multiplayer-testing
|
|
24
|
+
- skill/summer-player-data
|
|
25
|
+
- skill/summer-analytics
|
|
26
|
+
source: official
|
|
27
|
+
license: MIT
|
|
28
|
+
status: preview
|
|
29
|
+
recommended: false
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-world-chat
|
|
3
|
+
description: "Add match chat to a Summer game: one World channel shared by everyone in the match. Hosted only."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: multiplayer-and-networking
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Edit Write summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics
|
|
9
|
+
paths: ["**/*.gd", "**/*.tscn"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /summer-world-chat — chat with everyone in the match
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
A **World channel** is a text channel for the World (match) the player has
|
|
17
|
+
joined. Every player in that World can open the same channel by key, such as
|
|
18
|
+
`"match"`. The platform owns everything that matters about it:
|
|
19
|
+
|
|
20
|
+
- who may read it;
|
|
21
|
+
- the text filter;
|
|
22
|
+
- rate limits;
|
|
23
|
+
- 24-hour retention;
|
|
24
|
+
- blocks between players.
|
|
25
|
+
|
|
26
|
+
Your game owns only the chat UI. Messages never travel through your network
|
|
27
|
+
composition, and the authority neither sees nor writes them.
|
|
28
|
+
|
|
29
|
+
| Fact | Value |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| Text | Plain Unicode, 1 to 4000 characters. Always render it as plain text, never as markup |
|
|
32
|
+
| Channel keys | 1–64 of `A-Z a-z 0-9 _ . -`, starting with a letter or digit; at most 64 channels per World |
|
|
33
|
+
| History | Best effort, not a full log. A new handle shows the newest 2 messages; `load_older()` grows the window to at most 16 |
|
|
34
|
+
| Lifetime | A handle belongs to one World admission. After leaving or joining another World, open a new one |
|
|
35
|
+
| Not included | Direct messages (`skill/summer-friends`), party chat (the Summer app) and messages written by your server |
|
|
36
|
+
|
|
37
|
+
**Hosted only.** Chat needs a joined hosted World and a Summer app whose Player
|
|
38
|
+
Host offers `player.channels`. A queue reservation is not enough: open the
|
|
39
|
+
channel after `enter()` succeeds. In Local Play and editor runs,
|
|
40
|
+
`check_open_readiness()` and `open_world_channel()` fail with `unavailable`.
|
|
41
|
+
Build the "chat hidden" path first.
|
|
42
|
+
|
|
43
|
+
This skill extends the Courtyard game from `skill/multiplayer-project` and
|
|
44
|
+
`skill/multiplayer-state`.
|
|
45
|
+
|
|
46
|
+
## Steps
|
|
47
|
+
|
|
48
|
+
### 1. Publish player names
|
|
49
|
+
|
|
50
|
+
Messages carry the author's Summer user id (`player_id`), not a name. The
|
|
51
|
+
authority already knows every verified name. Publish them in the shared match
|
|
52
|
+
document (`skill/multiplayer-state`): in `authority/main.gd`'s `_join`, before
|
|
53
|
+
accepting, add
|
|
54
|
+
|
|
55
|
+
```gdscript
|
|
56
|
+
var players: Dictionary = match_doc.get("players", {})
|
|
57
|
+
players[session.player.user_id] = session.player.display_name
|
|
58
|
+
match_doc["players"] = players
|
|
59
|
+
match_group.reset(Game.pack(match_doc))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
On the client, copy `match_doc.get("players", {})` into the chat node's
|
|
63
|
+
`names` whenever the match document changes.
|
|
64
|
+
|
|
65
|
+
### 2. Add the chat node
|
|
66
|
+
|
|
67
|
+
`client/chat.gd`, added as a child `Chat` of `Main` in `client/main.tscn`:
|
|
68
|
+
|
|
69
|
+
```gdscript
|
|
70
|
+
extends Node
|
|
71
|
+
## Match chat: one World channel that every player in this match shares.
|
|
72
|
+
|
|
73
|
+
signal lines_changed(lines: PackedStringArray)
|
|
74
|
+
signal status(text: String)
|
|
75
|
+
|
|
76
|
+
var chat: SummerWorldChannel
|
|
77
|
+
var names := {} # player user id -> display name, from your match state
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Call after the player has entered the match.
|
|
81
|
+
func open() -> void:
|
|
82
|
+
var channels := Summer.client.channels
|
|
83
|
+
var ready := channels.check_open_readiness()
|
|
84
|
+
if not ready.ok:
|
|
85
|
+
status.emit("Chat is not available here.") # hide the chat box
|
|
86
|
+
return
|
|
87
|
+
var opened := channels.open_world_channel("match")
|
|
88
|
+
var result: SummerResult = await opened.get_result_or_completed_signal()
|
|
89
|
+
if not result.ok:
|
|
90
|
+
status.emit("Chat is not available here.")
|
|
91
|
+
return
|
|
92
|
+
chat = opened.get_channel()
|
|
93
|
+
if chat == null:
|
|
94
|
+
return # the player already left this World
|
|
95
|
+
chat.history_changed.connect(_render)
|
|
96
|
+
chat.history_failed.connect(func(_failure: SummerResult) -> void: status.emit("Chat history is unavailable. Tap to retry."))
|
|
97
|
+
chat.closed.connect(func(_reason: String) -> void:
|
|
98
|
+
chat = null
|
|
99
|
+
_render())
|
|
100
|
+
_render() # the first history may already be here
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## The Retry tap after history_failed.
|
|
104
|
+
func retry_history() -> void:
|
|
105
|
+
if chat != null:
|
|
106
|
+
chat.refresh()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
func send(text: String) -> void:
|
|
110
|
+
if chat == null or text.strip_edges().is_empty():
|
|
111
|
+
return
|
|
112
|
+
var sent := chat.send_message(text)
|
|
113
|
+
var result: SummerResult = await sent.get_result_or_completed_signal()
|
|
114
|
+
if not result.ok and result.retryable and result.code != &"rate_limited":
|
|
115
|
+
# An ambiguous failure may have been accepted. Retry the same send,
|
|
116
|
+
# never send_message again: that would be a second message.
|
|
117
|
+
sent = sent.retry()
|
|
118
|
+
result = await sent.get_result_or_completed_signal()
|
|
119
|
+
if result.ok:
|
|
120
|
+
return
|
|
121
|
+
match result.code:
|
|
122
|
+
&"text_not_allowed":
|
|
123
|
+
status.emit("That message was not sent.")
|
|
124
|
+
&"rate_limited":
|
|
125
|
+
status.emit("You are sending messages too fast.")
|
|
126
|
+
_:
|
|
127
|
+
status.emit("Message not sent.")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## The visible window always comes from get_messages(), never from appending.
|
|
131
|
+
func _render() -> void:
|
|
132
|
+
var lines := PackedStringArray()
|
|
133
|
+
if chat != null and chat.is_history_current():
|
|
134
|
+
for message in chat.get_messages():
|
|
135
|
+
var author := "Game" if message.author_kind == &"creator_system" else String(names.get(message.player_id, "Player"))
|
|
136
|
+
lines.append("%s: %s" % [author, message.text])
|
|
137
|
+
lines_changed.emit(lines)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Call `$Chat.open()` after `enter()` returns `true`. Wire it into the UI:
|
|
141
|
+
|
|
142
|
+
- **Messages:** a `Label` or `RichTextLabel` with `bbcode_enabled = false`, refilled from `lines_changed`.
|
|
143
|
+
- **Input:** a `LineEdit` whose `text_submitted` calls `$Chat.send(text)` and clears itself.
|
|
144
|
+
- **Status:** `status` feeds a status line; hide the whole box when chat is unavailable.
|
|
145
|
+
|
|
146
|
+
### 3. Rules the code above follows
|
|
147
|
+
|
|
148
|
+
- **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.
|
|
149
|
+
- **`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()`.
|
|
150
|
+
- **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.
|
|
151
|
+
- **`text_not_allowed` stored nothing.** The filter refused it, and resending the same text fails again.
|
|
152
|
+
- **`rate_limited`.** About 120 messages a minute per player; tell them to slow down, don't loop.
|
|
153
|
+
- **Sequence gaps are normal.** Blocks, other recipients and expiry hide messages; never treat a gap as an error.
|
|
154
|
+
- **`creator_system` messages are from the game**, never from a player or from Summer staff.
|
|
155
|
+
|
|
156
|
+
### 4. Test
|
|
157
|
+
|
|
158
|
+
Locally, check that the hidden path works and nothing errors:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
summer_project_setting name="summer/local_play/players" value=2
|
|
162
|
+
summer_play
|
|
163
|
+
summer_get_diagnostics
|
|
164
|
+
summer_stop
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Then test real chat on Summer staging with two accounts in one match.
|
|
168
|
+
|
|
169
|
+
## Checklist
|
|
170
|
+
|
|
171
|
+
- [ ] Local Play: `open()` reports chat unavailable, the chat box is hidden, and there are no script errors.
|
|
172
|
+
- [ ] The channel opens only after the player entered the World.
|
|
173
|
+
- [ ] Hosted: a message from one player appears for the other, with the right name.
|
|
174
|
+
- [ ] A filtered message shows "not sent" and never appears.
|
|
175
|
+
- [ ] After leaving and joining another match, chat reopens on a new handle.
|
|
176
|
+
- [ ] Text is shown as plain text: `[b]hi[/b]` appears literally.
|
|
177
|
+
|
|
178
|
+
## Common mistakes
|
|
179
|
+
|
|
180
|
+
| Don't | Do | Why |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| Send chat through your own Commands or State | `Summer.client.channels` | Summer filters, rate-limits, applies blocks and expires text; your State does none of that |
|
|
183
|
+
| Open the channel right after `Summer.initialize` | Open after `enter()` succeeds | Only a joined World admission can open it |
|
|
184
|
+
| Append each message to a log | Re-render from `get_messages()` on `history_changed` | Blocked and expired text must disappear |
|
|
185
|
+
| Call `send_message` again after a timeout | `operation.retry()` | A new call is a second message |
|
|
186
|
+
| Render with BBCode enabled | Plain `Label`, or `bbcode_enabled = false` | Player text is never markup |
|
|
187
|
+
| Keep a handle across matches | Open a new channel per World | Handles never follow the player to another World |
|
|
188
|
+
| Show `player_id` | Map it to a name your authority publishes | Ids are not names |
|
|
189
|
+
|
|
190
|
+
## See also
|
|
191
|
+
|
|
192
|
+
- `skill/multiplayer-state`: the match document that carries player names
|
|
193
|
+
- `skill/summer-friends`: direct messages with friends
|
|
194
|
+
- `skill/summer-parties`: party chat lives in the Summer app
|
|
195
|
+
- `skill/multiplayer-testing`: what Local Play can and can't test
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
id: skill/summer-world-chat
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
summary: "Add match chat to a Summer game: one World channel shared by everyone in the match. Hosted only."
|
|
5
|
+
use_when:
|
|
6
|
+
- "adding in-game text chat, match chat or a chat box to a multiplayer Summer game"
|
|
7
|
+
- "chat messages fail with text_not_allowed, rate_limited or unavailable, or chat history looks incomplete"
|
|
8
|
+
- "deciding where player chat should live instead of sending it through Commands or State"
|
|
9
|
+
facets:
|
|
10
|
+
lifecycle:
|
|
11
|
+
- build
|
|
12
|
+
domains:
|
|
13
|
+
- multiplayer
|
|
14
|
+
- ui
|
|
15
|
+
modalities:
|
|
16
|
+
- scripts
|
|
17
|
+
- ui
|
|
18
|
+
compatibility:
|
|
19
|
+
engine: ">=4.6"
|
|
20
|
+
related:
|
|
21
|
+
skills:
|
|
22
|
+
- skill/multiplayer
|
|
23
|
+
- skill/multiplayer-state
|
|
24
|
+
- skill/multiplayer-testing
|
|
25
|
+
- skill/summer-friends
|
|
26
|
+
- skill/summer-parties
|
|
27
|
+
source: official
|
|
28
|
+
license: MIT
|
|
29
|
+
status: preview
|
|
30
|
+
recommended: false
|