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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sprite-sheet
|
|
3
|
-
description:
|
|
3
|
+
description: "Generate animated 2D character sprites laid out as a sprite sheet — walk cycle, attack, idle, death — wired into AnimatedSprite2D / SpriteFrames."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: 2d-assets
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-analytics
|
|
3
|
+
description: "Send game events to Summer analytics from the authority or client: fire-and-forget, never blocking gameplay."
|
|
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_play summer_stop summer_get_diagnostics summer_open
|
|
9
|
+
paths: ["**/*.gd"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /summer-analytics — game events you can chart later
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Summer analytics stores named events, such as `level_completed` or
|
|
17
|
+
`coin_collected`, for the creator to query in Studio. There are two ways in:
|
|
18
|
+
|
|
19
|
+
| Captured from | Call | What it means |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| The player's client | `Summer.client.analytics.capture(name, properties)` | An **untrusted** observation from that player's device. Summer attaches the player, game and build; you never pass ids. |
|
|
22
|
+
| The authority | `Summer.authority.analytics.capture(name, properties)` | An observation about this World's runtime. It describes the server, not a player or a match. |
|
|
23
|
+
|
|
24
|
+
Analytics is for **looking at** your game, never for running it:
|
|
25
|
+
|
|
26
|
+
- Capture returns immediately.
|
|
27
|
+
- Gameplay never waits for capture and never depends on its result.
|
|
28
|
+
- **Capture is never retried.** An `unavailable` result may already have stored the event, and capturing again records a second event.
|
|
29
|
+
|
|
30
|
+
**Where it works:** hosted only. Under Local Play both calls complete with
|
|
31
|
+
`ok == false` and code `unavailable`, and nothing is stored. Also print each
|
|
32
|
+
event while testing, so you can check it in the logs.
|
|
33
|
+
|
|
34
|
+
## Steps
|
|
35
|
+
|
|
36
|
+
### 1. Choose a small set of events
|
|
37
|
+
|
|
38
|
+
Name events after things that happened, in the past tense:
|
|
39
|
+
`match_started`, `coin_collected`, `tutorial_step_completed`.
|
|
40
|
+
|
|
41
|
+
- **Names**:
|
|
42
|
+
- 1 to 64 characters, matching `^[A-Za-z][A-Za-z0-9_.:-]{0,63}$`.
|
|
43
|
+
- Case-sensitive, and never starting with `summer.`.
|
|
44
|
+
- **Never put changing values in names** (`coin_7_collected`). Put them in properties: `{"coin": 7}`.
|
|
45
|
+
- New event names are rate-limited per player and per game per day.
|
|
46
|
+
- Known names keep working after that limit.
|
|
47
|
+
- **Properties** are a JSON `Dictionary`:
|
|
48
|
+
- at most 64 scalar values;
|
|
49
|
+
- nesting at most 4 deep;
|
|
50
|
+
- property names up to 128 bytes;
|
|
51
|
+
- one event at most 16 KiB.
|
|
52
|
+
- **Never send personal data or secrets.** Names such as `email`, `password`, `phone`, `address`, `ip_address` and `*_token` are rejected at any depth, as is anything starting with `$summer_`.
|
|
53
|
+
|
|
54
|
+
Capture authoritative facts on the authority, where the game decided them.
|
|
55
|
+
Capture interface facts on the client: menu opened, tutorial step shown,
|
|
56
|
+
settings changed.
|
|
57
|
+
|
|
58
|
+
### 2. Add a fire-and-forget helper
|
|
59
|
+
|
|
60
|
+
Authority, in `authority/main.gd`:
|
|
61
|
+
|
|
62
|
+
```gdscript
|
|
63
|
+
## Fire-and-forget: gameplay never waits on analytics, and a failure is
|
|
64
|
+
## logged once, never retried (a retry would count the event twice).
|
|
65
|
+
var _analytics_warned := false
|
|
66
|
+
|
|
67
|
+
func _track(event: String, properties: Dictionary = {}) -> void:
|
|
68
|
+
print("ANALYTICS ", event, " ", JSON.stringify(properties))
|
|
69
|
+
var capture := Summer.authority.analytics.capture(event, properties)
|
|
70
|
+
var result: SummerResult = await capture.get_result_or_completed_signal()
|
|
71
|
+
if not result.ok and not _analytics_warned:
|
|
72
|
+
_analytics_warned = true
|
|
73
|
+
push_warning("analytics unavailable: %s" % result.code)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Client, in `client/main.gd`, the same shape with the client facade:
|
|
77
|
+
|
|
78
|
+
```gdscript
|
|
79
|
+
var _analytics_warned := false
|
|
80
|
+
|
|
81
|
+
func _track(event: String, properties: Dictionary = {}) -> void:
|
|
82
|
+
print("ANALYTICS ", event, " ", JSON.stringify(properties))
|
|
83
|
+
var capture := Summer.client.analytics.capture(event, properties)
|
|
84
|
+
var result: SummerResult = await capture.get_result_or_completed_signal()
|
|
85
|
+
if not result.ok and not _analytics_warned:
|
|
86
|
+
_analytics_warned = true
|
|
87
|
+
push_warning("analytics unavailable: %s" % result.code)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Call it without awaiting
|
|
91
|
+
|
|
92
|
+
Call `_track(...)` as a statement and do **not** `await` it, so the caller
|
|
93
|
+
continues immediately. For example, in the authority's `_collect`, after the
|
|
94
|
+
coin is applied:
|
|
95
|
+
|
|
96
|
+
```gdscript
|
|
97
|
+
_track("coin_collected", {"coin": coin, "players": seat_of.size()})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
On the client, after a menu choice:
|
|
101
|
+
|
|
102
|
+
```gdscript
|
|
103
|
+
_track("menu_opened", {"from": "pause"})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 4. Read the results
|
|
107
|
+
|
|
108
|
+
Open the game's analytics in Studio: `summer_open` with target `game` and
|
|
109
|
+
params `gameId` (your game's id) and `section: "analytics"`. Events show up
|
|
110
|
+
there after a short delay.
|
|
111
|
+
|
|
112
|
+
## Result codes
|
|
113
|
+
|
|
114
|
+
| Code | Meaning | What to do |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `unavailable` | No analytics service here (Local Play, an old Host), or a lost answer | Nothing. Do not capture again. |
|
|
117
|
+
| `capacity_exceeded` | More than 32 captures are pending | Capture less often; batch counts into one event |
|
|
118
|
+
| `analytics_storage_limit_exceeded` | The game's analytics quota is used up | Stop capturing this event type; final for now |
|
|
119
|
+
| `not_configured` | An older Supervisor without authority analytics | Nothing |
|
|
120
|
+
|
|
121
|
+
Every failure is final: none of them asks you to capture again.
|
|
122
|
+
|
|
123
|
+
## Checklist
|
|
124
|
+
|
|
125
|
+
- [ ] Every capture is a plain `_track(...)` call; no gameplay code awaits it.
|
|
126
|
+
- [ ] Event names are fixed strings; values live in properties.
|
|
127
|
+
- [ ] No personal data, tokens or free-typed player text in properties.
|
|
128
|
+
- [ ] Authority-decided facts (won, collected, bought) are captured on the authority.
|
|
129
|
+
- [ ] Under Local Play the game plays normally while captures report `unavailable`.
|
|
130
|
+
|
|
131
|
+
## Common mistakes
|
|
132
|
+
|
|
133
|
+
| Don't | Do | Why |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `await _track(...)` in gameplay code | Call it and move on | A slow or failing capture would stall the game |
|
|
136
|
+
| Capture again after `unavailable` | Log once and drop it | It may already be stored; a second capture double-counts |
|
|
137
|
+
| `coin_%d_collected` names | `coin_collected` with `{"coin": 7}` | New names are rate-limited; values belong in properties |
|
|
138
|
+
| Trust client events for rewards or rankings | Decide on the authority; capture there too | Client events are untrusted observations |
|
|
139
|
+
| Put emails, ids or tokens in properties | Summer attaches player and game identity itself | Blocked names are rejected; personal data must not be collected |
|
|
140
|
+
| Make gameplay depend on analytics working | Treat it as optional | It is unavailable locally and may be unavailable hosted |
|
|
141
|
+
| Start names with `summer.` | Your own names | That namespace is reserved |
|
|
142
|
+
|
|
143
|
+
## See also
|
|
144
|
+
|
|
145
|
+
- `skill/multiplayer` — where the client and the authority run
|
|
146
|
+
- `skill/multiplayer-state` — the authority's Command handlers, where most events happen
|
|
147
|
+
- `skill/summer-match-results` — results and ratings, which Summer records itself
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: skill/summer-analytics
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
summary: "Send game events to Summer analytics from the authority or client: fire-and-forget, never blocking gameplay."
|
|
5
|
+
use_when:
|
|
6
|
+
- "adding analytics, telemetry or event tracking to a Summer game: level completed, coin collected, tutorial funnel"
|
|
7
|
+
- "deciding what to track on the client versus the authority, and how to name analytics events and properties"
|
|
8
|
+
- "analytics capture fails with unavailable, capacity_exceeded or analytics_storage_limit_exceeded"
|
|
9
|
+
facets:
|
|
10
|
+
lifecycle:
|
|
11
|
+
- build
|
|
12
|
+
domains:
|
|
13
|
+
- multiplayer
|
|
14
|
+
- scripting
|
|
15
|
+
modalities:
|
|
16
|
+
- scripts
|
|
17
|
+
compatibility:
|
|
18
|
+
engine: ">=4.6"
|
|
19
|
+
related:
|
|
20
|
+
skills:
|
|
21
|
+
- skill/multiplayer
|
|
22
|
+
- skill/multiplayer-state
|
|
23
|
+
- skill/summer-match-results
|
|
24
|
+
- skill/summer-store
|
|
25
|
+
source: official
|
|
26
|
+
license: MIT
|
|
27
|
+
status: preview
|
|
28
|
+
recommended: false
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-friends
|
|
3
|
+
description: "Summer friends in a game: a consented friends list, profile and chat screens, direct messages. 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", "summer.build.json"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /summer-friends — friends, profiles and direct messages
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Friendships belong to the player's Summer account. The Summer app owns the
|
|
17
|
+
social graph itself:
|
|
18
|
+
|
|
19
|
+
- friend requests;
|
|
20
|
+
- blocking;
|
|
21
|
+
- the friends list screen;
|
|
22
|
+
- profiles;
|
|
23
|
+
- chat.
|
|
24
|
+
|
|
25
|
+
Your game gets two client-only facades.
|
|
26
|
+
|
|
27
|
+
**`Summer.client.friends`**
|
|
28
|
+
- Two screens need **no consent**: `show_friends()` and `show_chat(user_id)`. They ask the Summer app to show its own screen over the game.
|
|
29
|
+
- `show_profile(user_id)` is the same. The profile includes add friend, message, invite to party and report, so it is the social feature most games need.
|
|
30
|
+
- **With consent**, it reads the list: each friend's name, handle and whether they are `offline`, `online`, `in_this_game` or `in_other_game`. Another game is never named.
|
|
31
|
+
|
|
32
|
+
**`Summer.client.messages`**
|
|
33
|
+
- Reads and sends the player's direct messages, with a separate consent for each kind of access.
|
|
34
|
+
- Prefer `show_chat(user_id)` unless chat is central to your game.
|
|
35
|
+
|
|
36
|
+
Reading friends or messages needs two things:
|
|
37
|
+
|
|
38
|
+
1. **The Build declares the permission** in `summer.build.json`.
|
|
39
|
+
2. **The player grants it** in the Summer app's consent sheet, which your game asks for with `request_access`.
|
|
40
|
+
|
|
41
|
+
Declining never blocks play. A game must work without these permissions.
|
|
42
|
+
|
|
43
|
+
**Availability:**
|
|
44
|
+
|
|
45
|
+
- **Engine release.** This needs an engine that includes Summer Engine PR #508. On engines without it, `Summer.client.friends` does not exist and scripts that use it fail to parse. Check the class reference for `SummerClientFriends` before using this skill.
|
|
46
|
+
- **Hosted only.** `is_available()` is `false` in Local Play and editor runs, for guests, for accounts the platform keeps out of social features (`age_restricted`, under 13), and in Summer apps without `player.friends@1` (friends) or `player.messaging@1` (messages).
|
|
47
|
+
- **Overlays.** The screens need the Summer app to offer friends and parties; otherwise they fail with `capability_unavailable` or `ui_unavailable`.
|
|
48
|
+
|
|
49
|
+
## Steps
|
|
50
|
+
|
|
51
|
+
### 1. Declare the permissions you use
|
|
52
|
+
|
|
53
|
+
Add a top-level `permissions` array to `summer.build.json`, next to `schema`
|
|
54
|
+
and `gameId`. List only what the game uses; each entry is an upper bound on
|
|
55
|
+
what you may ask for.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"permissions": ["social.friends.read"],
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Permission | Allows |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `social.friends.read` | `get_friends()`, statuses, the `changed` signal |
|
|
64
|
+
| `social.messages.read` | `list_conversations`, `list_messages`, `list_messages_after` |
|
|
65
|
+
| `social.messages.send` | `send` |
|
|
66
|
+
| `social.messages.read_state.write` | `mark_read` |
|
|
67
|
+
|
|
68
|
+
Unknown or duplicate entries are rejected when you publish. Without a
|
|
69
|
+
declaration, `get_access()` reports `undeclared`, and `request_access()` fails
|
|
70
|
+
at once with `permission_undeclared`.
|
|
71
|
+
|
|
72
|
+
### 2. The friends node
|
|
73
|
+
|
|
74
|
+
`client/friends.gd`, a child `Friends` of `Main`. In `client/main.gd`'s `_ready()`, call
|
|
75
|
+
`$Friends.start()` right after `Summer.initialize` succeeds:
|
|
76
|
+
|
|
77
|
+
```gdscript
|
|
78
|
+
extends Node
|
|
79
|
+
## The player's Summer friends, with their consent, and the Summer app's
|
|
80
|
+
## friend screens over the game.
|
|
81
|
+
|
|
82
|
+
signal friends_changed(rows: Array) # [{user_id, name, status}]
|
|
83
|
+
signal status(text: String)
|
|
84
|
+
|
|
85
|
+
var friends: SummerClientFriends
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Main calls this right after Summer.initialize succeeds: a child's _ready
|
|
89
|
+
## runs before that, while Summer.client is still null.
|
|
90
|
+
func start() -> void:
|
|
91
|
+
friends = Summer.client.friends
|
|
92
|
+
friends.changed.connect(_render)
|
|
93
|
+
friends.access_changed.connect(func(_state: StringName) -> void: _render())
|
|
94
|
+
_render()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## The Friends button: asks for consent once, then shows the list.
|
|
98
|
+
func open() -> void:
|
|
99
|
+
if not friends.is_available():
|
|
100
|
+
status.emit("Friends are not available here.")
|
|
101
|
+
return
|
|
102
|
+
if friends.get_access() != &"granted":
|
|
103
|
+
var decision: SummerResult = await friends.request_access().get_result_or_completed_signal()
|
|
104
|
+
if not decision.ok:
|
|
105
|
+
status.emit("Your friends list stays private.")
|
|
106
|
+
return
|
|
107
|
+
_render()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
func _render() -> void:
|
|
111
|
+
var rows := []
|
|
112
|
+
for friend in friends.get_friends():
|
|
113
|
+
rows.append({"user_id": friend.user_id, "name": friend.display_name, "status": friend.status})
|
|
114
|
+
friends_changed.emit(rows)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## The Summer app's profile for any other player this game knows. No consent needed.
|
|
118
|
+
func show_profile(user_id: String) -> void:
|
|
119
|
+
var shown: SummerResult = await friends.show_profile(user_id).get_result_or_completed_signal()
|
|
120
|
+
if not shown.ok:
|
|
121
|
+
status.emit("That screen is not available right now.")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## The Summer app's direct-message thread with a friend, over the game.
|
|
125
|
+
func show_chat(user_id: String) -> void:
|
|
126
|
+
var shown: SummerResult = await friends.show_chat(user_id).get_result_or_completed_signal()
|
|
127
|
+
if not shown.ok:
|
|
128
|
+
status.emit("That screen is not available right now.")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
How it behaves:
|
|
132
|
+
|
|
133
|
+
- **When the list is read again.** With a handler on `changed`, the engine re-reads the list on every change hint, after a consent decision, on `refresh()`, and about every 60 seconds while the game is in front. Never poll yourself.
|
|
134
|
+
- **Statuses.** Show `in_this_game` friends first. `unknown` means presence couldn't be read; it is not `offline`.
|
|
135
|
+
- **Rows.** Key them by `user_id`; each read replaces the `SummerFriend` objects.
|
|
136
|
+
- **Profiles of other players.** For the people in the current match, use the user ids your authority publishes (the `players` map in `skill/summer-world-chat`, step 1) and call `show_profile(user_id)`. Your own id fails with `invalid_argument`.
|
|
137
|
+
- **Inviting a friend into a match.** Use the party invite sheet (`skill/summer-parties`).
|
|
138
|
+
|
|
139
|
+
### 3. Direct messages (only if chat is central)
|
|
140
|
+
|
|
141
|
+
```gdscript
|
|
142
|
+
## Sends one direct message, asking for the send permission once.
|
|
143
|
+
func send_message(user_id: String, text: String) -> void:
|
|
144
|
+
var messages := Summer.client.messages
|
|
145
|
+
if not messages.is_available():
|
|
146
|
+
return
|
|
147
|
+
if messages.get_access(&"social.messages.send") != &"granted":
|
|
148
|
+
var decision: SummerResult = await messages.request_access(PackedStringArray(["social.messages.send"])).get_result_or_completed_signal()
|
|
149
|
+
if not decision.ok:
|
|
150
|
+
return
|
|
151
|
+
var sent := messages.send(user_id, text)
|
|
152
|
+
var result: SummerResult = await sent.get_result_or_completed_signal()
|
|
153
|
+
if result.ok:
|
|
154
|
+
print("sent #%d" % sent.message.sequence)
|
|
155
|
+
elif result.code == &"text_not_allowed":
|
|
156
|
+
status.emit("That message was not sent.")
|
|
157
|
+
elif result.code == &"target_unavailable":
|
|
158
|
+
status.emit("You can't message this player.")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Reading a thread works the same way:
|
|
162
|
+
|
|
163
|
+
- **Read a page.** Ask for `social.messages.read`, then `messages.list_messages(user_id)`. Read `op.messages`, newest page first; pass `op.next_cursor` for older pages.
|
|
164
|
+
- **Catch up.** After `messages_changed`, call `list_messages_after(user_id, last_sequence)`. Today `messages_changed` fires only after a stream resync, so also reload when the chat screen opens.
|
|
165
|
+
- **Mark read.** `mark_read(user_id, sequence)` needs `social.messages.read_state.write`. It marks the thread read on every device.
|
|
166
|
+
- **Send rules.** `send` takes 1–4000 characters and no control characters except newline and tab. Each call is one message; the engine never sends it twice.
|
|
167
|
+
|
|
168
|
+
### 4. Failure codes
|
|
169
|
+
|
|
170
|
+
| Code | Meaning | Show |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| `permission_not_requested`, `permission_declined`, `permission_revoked` | The player hasn't granted it | A "Show friends" button that calls `request_access()` again later |
|
|
173
|
+
| `permission_undeclared` | `summer.build.json` lacks it | Fix the declaration (step 1) |
|
|
174
|
+
| `capability_unavailable` | Local Play, guest or older Summer app | Hide the feature |
|
|
175
|
+
| `age_restricted` | Under-13 account | Hide the feature |
|
|
176
|
+
| `ui_unavailable` | The app can't show a sheet now | "Try again" |
|
|
177
|
+
| `operation_in_progress` (retryable) | Another Summer sheet (party or friends) is open | Wait |
|
|
178
|
+
| `text_not_allowed` | Filtered; nothing stored | "Not sent" |
|
|
179
|
+
| `target_unavailable` | Not a friend, blocked, or gone | "You can't message this player" |
|
|
180
|
+
| `rate_limited`, `unavailable` (retryable) | Busy, or the Player Host is unreachable | Retry later, never in a loop |
|
|
181
|
+
|
|
182
|
+
### 5. Test
|
|
183
|
+
|
|
184
|
+
Local Play has no accounts, so check the hidden path locally:
|
|
185
|
+
|
|
186
|
+
- `is_available()` is `false`;
|
|
187
|
+
- buttons are hidden;
|
|
188
|
+
- there are no script errors.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
summer_project_setting name="summer/local_play/players" value=2
|
|
192
|
+
summer_play
|
|
193
|
+
summer_get_diagnostics
|
|
194
|
+
summer_stop
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Then test on Summer staging with two accounts that are friends: grant, decline
|
|
198
|
+
and revoke access in the Summer app, and watch `access_changed`.
|
|
199
|
+
|
|
200
|
+
## Checklist
|
|
201
|
+
|
|
202
|
+
- [ ] The engine's class reference lists `SummerClientFriends` (Summer Engine PR #508 or later).
|
|
203
|
+
- [ ] `summer.build.json` declares exactly the permissions the code asks for.
|
|
204
|
+
- [ ] Local Play: friends features are hidden, with no script errors.
|
|
205
|
+
- [ ] Declining consent leaves the game fully playable, and the button can ask again later.
|
|
206
|
+
- [ ] Hosted: a friend playing the game shows `in_this_game`; `show_profile` opens for another player in the match.
|
|
207
|
+
- [ ] No polling timer: updates come from `changed`.
|
|
208
|
+
|
|
209
|
+
## Common mistakes
|
|
210
|
+
|
|
211
|
+
| Don't | Do | Why |
|
|
212
|
+
| --- | --- | --- |
|
|
213
|
+
| Ask for consent at startup | Ask when the player opens a friends feature | A consent sheet without context gets declined |
|
|
214
|
+
| Gate play on friends access | Treat it as optional | Declining must never block the game |
|
|
215
|
+
| Build friend requests, blocking or a profile screen | `show_friends()`, `show_profile()`, `show_chat()` | The Summer app owns them, with no consent needed |
|
|
216
|
+
| Treat `unknown` as `offline` | Show it as unknown | It means presence couldn't be read |
|
|
217
|
+
| Poll `refresh()` on a timer | Connect `changed` | The engine already re-reads while a handler is connected |
|
|
218
|
+
| Declare every permission "just in case" | Declare only what you use | The consent sheet lists each declared permission |
|
|
219
|
+
| Use friends to put players in one match | `skill/summer-parties` | Parties are how players play together |
|
|
220
|
+
|
|
221
|
+
## See also
|
|
222
|
+
|
|
223
|
+
- `skill/summer-parties`: playing together, the invite sheet
|
|
224
|
+
- `skill/summer-world-chat`: chat inside a match
|
|
225
|
+
- `skill/multiplayer-publish`: `summer.build.json` and publishing a Build
|
|
226
|
+
- `skill/multiplayer-testing`: what Local Play can and can't test
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: skill/summer-friends
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
summary: "Summer friends in a game: a consented friends list, profile and chat screens, direct messages. Hosted only."
|
|
5
|
+
use_when:
|
|
6
|
+
- "showing a friends list, who is online or which friends are playing this Summer game"
|
|
7
|
+
- "opening a player's Summer profile, friends list or chat screen from inside a game"
|
|
8
|
+
- "reading or writing a player's direct messages, or a friends call fails with permission_undeclared or permission_declined"
|
|
9
|
+
facets:
|
|
10
|
+
lifecycle:
|
|
11
|
+
- build
|
|
12
|
+
domains:
|
|
13
|
+
- multiplayer
|
|
14
|
+
- ui
|
|
15
|
+
modalities:
|
|
16
|
+
- scripts
|
|
17
|
+
compatibility:
|
|
18
|
+
engine: ">=4.6"
|
|
19
|
+
related:
|
|
20
|
+
skills:
|
|
21
|
+
- skill/multiplayer
|
|
22
|
+
- skill/multiplayer-testing
|
|
23
|
+
- skill/summer-parties
|
|
24
|
+
- skill/summer-world-chat
|
|
25
|
+
source: official
|
|
26
|
+
license: MIT
|
|
27
|
+
status: preview
|
|
28
|
+
recommended: false
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-leaderboards
|
|
3
|
+
description: "Leaderboards in a Summer game: queue ratings from matches, and Game rankings your authority scores."
|
|
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_read_file summer_replace_text summer_write_file summer_get_script_errors summer_play summer_stop summer_get_diagnostics
|
|
9
|
+
paths: ["summer.build.json", "client/**/*.gd", "authority/**/*.gd"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /summer-leaderboards — ratings, rankings and the top 50
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Summer keeps two kinds of score board. Pick by where the number comes from:
|
|
17
|
+
|
|
18
|
+
| Board | The number is | Declared as | Written by | Read with |
|
|
19
|
+
|---|---|---|---|---|
|
|
20
|
+
| **Queue rating** | A skill rating (Elo, MMR) that moves with every match in a rated queue | A queue with `"policy": "rating"` | Summer, when the authority concludes a match | `Summer.client.ratings`, `Summer.authority.ratings` |
|
|
21
|
+
| **Game ranking** | Any score your game defines: total coins, best time, levels cleared | `runtime.rankings` | Your authority, with compare-and-set | `Summer.client.rankings`, `Summer.authority.rankings` |
|
|
22
|
+
|
|
23
|
+
Players only ever read their **own** rating and score, plus the public top 50,
|
|
24
|
+
which shows display names, never account ids. Nobody can write a score from a
|
|
25
|
+
client.
|
|
26
|
+
|
|
27
|
+
Where it works: leaderboards, standings and rankings are read through the
|
|
28
|
+
Summer app that hosts the player, so they work in **hosted** games only.
|
|
29
|
+
Under Local Play they fail with a typed code (step 5); the authority's
|
|
30
|
+
`ratings.load` returns a deterministic test value. Build the screens so they
|
|
31
|
+
degrade gracefully, and check the real numbers on a hosted staging build.
|
|
32
|
+
|
|
33
|
+
This skill extends the Courtyard game: its authority concludes matches as in
|
|
34
|
+
`skill/summer-match-results`.
|
|
35
|
+
|
|
36
|
+
## Steps
|
|
37
|
+
|
|
38
|
+
### 1. Declare the boards
|
|
39
|
+
|
|
40
|
+
In `summer.build.json`, a rated queue gets a rating, and `runtime.rankings`
|
|
41
|
+
declares Game rankings:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
"queues": [
|
|
45
|
+
{
|
|
46
|
+
"name": "duel",
|
|
47
|
+
"minPlayers": 2,
|
|
48
|
+
"maxPlayers": 2,
|
|
49
|
+
"worldDefinition": "courtyard",
|
|
50
|
+
"policy": "rating",
|
|
51
|
+
"ratingSystemRef": "elo_v1",
|
|
52
|
+
"teams": { "count": 2, "size": 1 },
|
|
53
|
+
"transport": "enet"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"rankings": [
|
|
57
|
+
{ "name": "coins", "initialRating": 0 }
|
|
58
|
+
]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `ratingSystemRef`: `elo_v1` lets Summer compute rating moves from win, loss
|
|
62
|
+
and draw. `authority_v1` makes your authority report each player's move
|
|
63
|
+
(step 4). Optional `initialRating` is a newcomer's rating (default 1200).
|
|
64
|
+
- A ranking `name` is lower-case letters and digits separated by single `.`,
|
|
65
|
+
`_` or `-`, at most 64 characters; at most 32 rankings. `initialRating`
|
|
66
|
+
defaults to 0.
|
|
67
|
+
- To make a rated queue and a ranking one board, give the queue
|
|
68
|
+
`"rankingRef": "<ranking name>"` and drop the queue's `initialRating`. A
|
|
69
|
+
queue and a ranking may not share a name without `rankingRef`.
|
|
70
|
+
|
|
71
|
+
### 2. Show a queue's top 50 and the player's standing
|
|
72
|
+
|
|
73
|
+
On a client, after Summer is initialized (no match needed):
|
|
74
|
+
|
|
75
|
+
```gdscript
|
|
76
|
+
## Fills a leaderboard screen for a rated queue. Hosted games only.
|
|
77
|
+
func show_queue_board(queue: StringName) -> void:
|
|
78
|
+
var board := Summer.client.ratings.leaderboard(queue)
|
|
79
|
+
var read: SummerResult = await board.get_result_or_completed_signal()
|
|
80
|
+
if not read.ok:
|
|
81
|
+
show_board_unavailable(read)
|
|
82
|
+
return
|
|
83
|
+
for entry: SummerLeaderboardEntry in board.entries:
|
|
84
|
+
add_board_row(entry.rank, entry.display_name, entry.value, entry.you)
|
|
85
|
+
var standing := Summer.client.ratings.standing(queue)
|
|
86
|
+
if (await standing.get_result_or_completed_signal()).ok:
|
|
87
|
+
if standing.rank > 0:
|
|
88
|
+
show_place("#%d of %d" % [standing.rank, standing.players])
|
|
89
|
+
elif standing.rated:
|
|
90
|
+
show_place("Top %.1f%%" % (100.0 - standing.percentile))
|
|
91
|
+
else:
|
|
92
|
+
show_place("Play a rated match to get a rank")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- `entries` holds up to 50 rows, best first; `you` marks the player's own row.
|
|
96
|
+
- `standing.rank` is set only inside the top 1000; otherwise show the
|
|
97
|
+
percentile. Before the first rated match `rated` is false and `value` is the
|
|
98
|
+
queue's `initialRating`.
|
|
99
|
+
- `Summer.client.ratings.get_current(queue)` returns the last known
|
|
100
|
+
`SummerRatingSnapshot` (`value`, `games`, `version`) without a network read,
|
|
101
|
+
or `null`. `refresh(queue)` reads it again; `rating_changed(queue)` fires
|
|
102
|
+
when it moves.
|
|
103
|
+
- One read of each kind is in flight at a time; calling again while one is
|
|
104
|
+
pending returns the same operation.
|
|
105
|
+
|
|
106
|
+
After a match, `Summer.client.ratings.rating_committed` delivers the
|
|
107
|
+
player's own before-and-after rating (see `skill/summer-match-results`).
|
|
108
|
+
|
|
109
|
+
### 3. Write a Game ranking from the authority
|
|
110
|
+
|
|
111
|
+
Only the authority writes rankings, by the player's stable id, with
|
|
112
|
+
**compare-and-set**: read the score and its version, write the new absolute
|
|
113
|
+
value against that version, and read again if someone else wrote first. In
|
|
114
|
+
`authority/main.gd`, after the match concluded:
|
|
115
|
+
|
|
116
|
+
```gdscript
|
|
117
|
+
## Adds this match's coins to each player's Game ranking "coins".
|
|
118
|
+
## Compare-and-set: read the version, write against it, re-read on conflict.
|
|
119
|
+
func _record_coins() -> void:
|
|
120
|
+
for session_id: String in wallets:
|
|
121
|
+
var player: SummerPlayer = sessions[session_id].player
|
|
122
|
+
await _add_score(&"coins", player.user_id, int(wallets[session_id]))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
func _add_score(ranking: StringName, player_id: String, points: int) -> void:
|
|
126
|
+
for attempt in 3:
|
|
127
|
+
var before := Summer.authority.rankings.get_score(ranking, player_id)
|
|
128
|
+
var read: SummerResult = await before.get_result_or_completed_signal()
|
|
129
|
+
if not read.ok:
|
|
130
|
+
return # hosted only: Local Play has no ranking service
|
|
131
|
+
var write := Summer.authority.rankings.set_score(ranking, player_id, before.rating.version, before.rating.value + points, before.rating.games + 1)
|
|
132
|
+
var wrote: SummerResult = await write.get_result_or_completed_signal()
|
|
133
|
+
if wrote.ok or wrote.code != &"ranking_version_conflict":
|
|
134
|
+
return
|
|
135
|
+
# Someone else wrote first: read again and recompute.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`sessions` maps each `session_id` to the `SummerSession` the authority keeps
|
|
139
|
+
in `_on_session_joined` (`skill/summer-match-results`, step 1). The engine never
|
|
140
|
+
retries a write. After a failure other than a conflict, read the score before
|
|
141
|
+
deciding to write again: the first write may have committed.
|
|
142
|
+
|
|
143
|
+
Clients read their own ranking with `Summer.client.rankings.get_score("coins")`,
|
|
144
|
+
`standing("coins")` (full rank and player count; rank 0 before the first
|
|
145
|
+
score) and `leaderboard("coins")` (top 50, zero-score players included). These
|
|
146
|
+
work in a standalone game too, before any match.
|
|
147
|
+
|
|
148
|
+
### 4. Own a co-op queue's rating (`authority_v1`)
|
|
149
|
+
|
|
150
|
+
Elo rates two sides against each other. A co-op mode, where a group plays
|
|
151
|
+
against the content, declares `"ratingSystemRef": "authority_v1"` and reports
|
|
152
|
+
every player's move when the match concludes:
|
|
153
|
+
|
|
154
|
+
```gdscript
|
|
155
|
+
## A co-op run against content rated `content_rating`; `cleared` says whether
|
|
156
|
+
## the group made it. Moves each player's rating like Elo against the content.
|
|
157
|
+
func conclude_run(players: Array[SummerSession], content_rating: int, cleared: bool) -> void:
|
|
158
|
+
var outcomes: Dictionary[SummerSession, SummerAuthorityMatch.MatchOutcome] = {}
|
|
159
|
+
var deltas: Dictionary[SummerSession, int] = {}
|
|
160
|
+
for session in players:
|
|
161
|
+
var read := Summer.authority.ratings.load(session)
|
|
162
|
+
if not (await read.get_result_or_completed_signal()).ok:
|
|
163
|
+
return # rating_unavailable: this queue keeps no rating
|
|
164
|
+
var expected := 1.0 / (1.0 + pow(10.0, (content_rating - read.rating.value) / 400.0))
|
|
165
|
+
deltas[session] = roundi(32 * ((1.0 if cleared else 0.0) - expected))
|
|
166
|
+
outcomes[session] = SummerAuthorityMatch.MATCH_OUTCOME_WIN if cleared else SummerAuthorityMatch.MATCH_OUTCOME_LOSS
|
|
167
|
+
var concluded: SummerResult = await Summer.authority.match.conclude(outcomes, "run_complete", deltas).get_result_or_completed_signal()
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- Each delta is an `int` within ±1000, one for **every** participant.
|
|
171
|
+
Deltas on a non-`authority_v1` queue, or a missing one, fail the
|
|
172
|
+
conclusion with `invalid_request` and commit nothing; conclude again with
|
|
173
|
+
corrected arguments.
|
|
174
|
+
- `Summer.authority.ratings.load(session)` is for the authority's own logic.
|
|
175
|
+
Never show one player's rating to other players; the leaderboard is the
|
|
176
|
+
public view.
|
|
177
|
+
- `Summer.authority.ratings.leaderboard("dungeon")` reads the same top 50,
|
|
178
|
+
unmarked, for an in-World trophy wall.
|
|
179
|
+
|
|
180
|
+
### 5. Degrade gracefully where boards aren't available
|
|
181
|
+
|
|
182
|
+
```gdscript
|
|
183
|
+
func show_board_unavailable(result: SummerResult) -> void:
|
|
184
|
+
match String(result.code):
|
|
185
|
+
"rating_unavailable":
|
|
186
|
+
show_place("This mode has no ranking")
|
|
187
|
+
_:
|
|
188
|
+
show_place("Leaderboard unavailable right now" if result.retryable else "Leaderboards appear in the Summer app")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Under Local Play these calls fail without a Summer account behind them:
|
|
192
|
+
|
|
193
|
+
| Call | Under Local Play |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `Summer.client.ratings.leaderboard`, `standing`, `refresh` | Fails with `unavailable` (retryable) |
|
|
196
|
+
| `Summer.client.ratings.get_current` | `null` |
|
|
197
|
+
| `Summer.client.rankings.get_score`, `standing`, `leaderboard` | Fails with `unavailable` (not retryable) |
|
|
198
|
+
| `Summer.authority.rankings.get_score`, `set_score` | Fails with `service_error` |
|
|
199
|
+
| `Summer.authority.ratings.load` | Works: a deterministic test rating (system `deterministic-v1`) |
|
|
200
|
+
| `Summer.authority.ratings.leaderboard` | Works: an empty list |
|
|
201
|
+
| `Summer.authority.match.conclude` | Works: a deterministic settlement with test rating changes |
|
|
202
|
+
|
|
203
|
+
Hide or grey out leaderboard buttons when a read fails; never block the game
|
|
204
|
+
on them.
|
|
205
|
+
|
|
206
|
+
## Checklist
|
|
207
|
+
|
|
208
|
+
- [ ] Every board is declared: rated queues with `ratingSystemRef`, rankings under `runtime.rankings`.
|
|
209
|
+
- [ ] The leaderboard screen marks the player's own row and shows rank or percentile.
|
|
210
|
+
- [ ] Ranking writes happen only on the authority, by `player.user_id`, with compare-and-set and a conflict re-read.
|
|
211
|
+
- [ ] `authority_v1` queues conclude with a delta for every participant; other queues send none.
|
|
212
|
+
- [ ] Every read handles failure, and the game stays playable without boards.
|
|
213
|
+
- [ ] Real numbers checked on a hosted staging build.
|
|
214
|
+
|
|
215
|
+
## Common mistakes
|
|
216
|
+
|
|
217
|
+
| Don't | Do | Why |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| Write scores from the client | `Summer.authority.rankings.set_score` | Clients can only read their own score; the authority is trusted |
|
|
220
|
+
| `set_score` without the version you read | Read with `get_score`, pass `before.rating.version` | It is compare-and-set; a stale version fails `ranking_version_conflict` |
|
|
221
|
+
| Blindly retry a failed write | Read again, then decide | The first write may have committed; the engine doesn't replay it |
|
|
222
|
+
| Send rating deltas on an `elo_v1` or `fifo` queue | Deltas only on `authority_v1`, one per participant | Otherwise the conclusion fails `invalid_request` |
|
|
223
|
+
| Show other players' ratings from `authority.ratings.load` | Show the leaderboard | Ratings are private to each player |
|
|
224
|
+
| Name a ranking like a queue without `rankingRef` | Distinct names, or `rankingRef` to share one board | Summer rejects the manifest |
|
|
225
|
+
| Treat a Local Play failure as a bug | Expect `unavailable` or `service_error` locally; test boards hosted | Boards are read through the hosting Summer app |
|
|
226
|
+
|
|
227
|
+
## See also
|
|
228
|
+
|
|
229
|
+
- `skill/summer-match-results` — concluding matches, which moves ratings
|
|
230
|
+
- `skill/summer-matchmaking` — declaring rated queues and teams
|
|
231
|
+
- `skill/summer-player-data` — saving per-player progress that isn't a ranking
|