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,521 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: multiplayer-project
|
|
3
|
+
description: "Set up a Summer multiplayer project: both entry scenes, World and queue files, joining, and a first Local Play run."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: multiplayer-and-networking
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Glob Edit Write summer_read_file summer_write_file summer_get_scene_tree summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics
|
|
9
|
+
paths: ["project.godot", "summer.build.json", "world.json", "source-domains.json", "runtime-manifest.json", "network/**", "client/**", "authority/**"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /multiplayer-project — the files every Summer multiplayer game needs
|
|
13
|
+
|
|
14
|
+
Step 1 of the multiplayer sequence (`multiplayer` explains the model). By the
|
|
15
|
+
end, Local Play starts a headless authority and two clients. Each client joins
|
|
16
|
+
through the game's own `Summer.client.join`, and every client sees one player
|
|
17
|
+
per joined player.
|
|
18
|
+
|
|
19
|
+
The example game is **Courtyard**. The later skills (`multiplayer-movement`,
|
|
20
|
+
`multiplayer-state`) continue it, so keep its names until the user's own game
|
|
21
|
+
replaces them. Every file below was run as shown with Local Play;
|
|
22
|
+
[files.md](files.md) has all of them complete, scenes included.
|
|
23
|
+
|
|
24
|
+
## 1. Ask, then state the plan
|
|
25
|
+
|
|
26
|
+
> 1. What is the game called? Its id is `game_<name>`: lowercase letters, digits, `_`, `.` or `-`.
|
|
27
|
+
> 2. How many players per match? Summer measures up to 16.
|
|
28
|
+
> 3. Which platforms: iOS, macOS, Windows?
|
|
29
|
+
|
|
30
|
+
State the plan before writing anything:
|
|
31
|
+
|
|
32
|
+
> I'll add Summer multiplayer: a client scene and a headless authority scene,
|
|
33
|
+
> a World and a queue for 2–8 players, a network composition, and Local Play
|
|
34
|
+
> to test it. Each folder holds one side: `client/`, `authority/`,
|
|
35
|
+
> `network/` (shared). OK?
|
|
36
|
+
|
|
37
|
+
For an existing single-player game, get this skeleton running first, then
|
|
38
|
+
move its gameplay under `client/`.
|
|
39
|
+
|
|
40
|
+
## 2. The layout
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
project.godot shared
|
|
44
|
+
summer.build.json the Build: game id, queues, Worlds, server preset
|
|
45
|
+
world.json the WorldDefinition: which scenes run where
|
|
46
|
+
runtime-manifest.json the server component's runtime descriptor
|
|
47
|
+
source-domains.json which folders are client, server or shared
|
|
48
|
+
network/ shared by both sides: composition, schemas, game.gd
|
|
49
|
+
client/ what players run: main scene, player scenes, UI
|
|
50
|
+
authority/ what the server runs: main scene, rules
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Client code and shared code must never load a file from `authority/`. The
|
|
54
|
+
summer.games export leaves `authority/` out of the players' pack. The
|
|
55
|
+
authority may load shared files.
|
|
56
|
+
|
|
57
|
+
## 3. project.godot
|
|
58
|
+
|
|
59
|
+
Add to the project's `project.godot`:
|
|
60
|
+
|
|
61
|
+
```ini
|
|
62
|
+
config_version=5
|
|
63
|
+
|
|
64
|
+
[application]
|
|
65
|
+
|
|
66
|
+
config/name="Courtyard"
|
|
67
|
+
run/main_scene="res://client/main.tscn"
|
|
68
|
+
run/main_scene.summer_client="res://client/main.tscn"
|
|
69
|
+
run/main_scene.summer_authority="res://authority/main.tscn"
|
|
70
|
+
|
|
71
|
+
[rendering]
|
|
72
|
+
|
|
73
|
+
textures/vram_compression/import_etc2_astc=true
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The `.summer_client` and `.summer_authority` overrides tell each exported pack
|
|
77
|
+
which scene to start. Leave the `[summer] version` line the editor writes.
|
|
78
|
+
|
|
79
|
+
## 4. The declarations
|
|
80
|
+
|
|
81
|
+
`summer.build.json`: the game id, the queue players join, and the World it
|
|
82
|
+
starts. Summer rejects unknown fields, so copy the shape exactly.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"schema": "summer.build.v2",
|
|
87
|
+
"gameId": "game_courtyard",
|
|
88
|
+
"executionMode": "hosted",
|
|
89
|
+
"targetPlatforms": ["ios", "macos"],
|
|
90
|
+
"project": { "directory": "." },
|
|
91
|
+
"server": { "exportPreset": "Courtyard Authority" },
|
|
92
|
+
"runtime": {
|
|
93
|
+
"protocolVersion": "1.0.0",
|
|
94
|
+
"interfaces": {
|
|
95
|
+
"multiplayer": 6, "session": 3, "lobby": 3, "presence": 3,
|
|
96
|
+
"config": 3, "party": 1, "matchSearch": 1
|
|
97
|
+
},
|
|
98
|
+
"scenes": ["res://client/main.tscn"],
|
|
99
|
+
"worldDefinitions": ["world.json"],
|
|
100
|
+
"queues": [
|
|
101
|
+
{
|
|
102
|
+
"name": "courtyard",
|
|
103
|
+
"minPlayers": 2,
|
|
104
|
+
"maxPlayers": 8,
|
|
105
|
+
"worldDefinition": "courtyard",
|
|
106
|
+
"policy": "fifo",
|
|
107
|
+
"transport": "enet"
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"tickRate": 60,
|
|
111
|
+
"config": { "keys": {} },
|
|
112
|
+
"presence": { "tokens": {} },
|
|
113
|
+
"lobby": { "fields": {} },
|
|
114
|
+
"server": { "tier": "small" }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `queues[].minPlayers` / `maxPlayers` bound a match. `worldDefinition` names
|
|
120
|
+
the `definition_id` in `world.json`.
|
|
121
|
+
- `server.exportPreset` names the server export preset (`multiplayer-publish`).
|
|
122
|
+
- `runtime.interfaces`, `config`, `presence` and `lobby` are required. Copy
|
|
123
|
+
them as shown.
|
|
124
|
+
|
|
125
|
+
`world.json`: what one World is. Keep every field; Local Play accepts less,
|
|
126
|
+
but publishing needs all of them.
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"schema": "summer.world-definition.v1",
|
|
131
|
+
"definition_id": "courtyard",
|
|
132
|
+
"lifetime": "match_scoped",
|
|
133
|
+
"topology": "dedicated",
|
|
134
|
+
"client_entry_point": "res://client/main.tscn",
|
|
135
|
+
"components": [
|
|
136
|
+
{
|
|
137
|
+
"component_id": "match",
|
|
138
|
+
"profile": "headless_engine",
|
|
139
|
+
"runtime_descriptor": "runtime-manifest.json",
|
|
140
|
+
"entry_point": "authority/main.tscn",
|
|
141
|
+
"owns": ["match"]
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"source_graph": "source-domains.json",
|
|
145
|
+
"network_compositions": ["res://network/composition.tres"],
|
|
146
|
+
"services": { "required": [], "optional": [] },
|
|
147
|
+
"settlement": { "mode": "required", "version": 1 },
|
|
148
|
+
"persistence": {
|
|
149
|
+
"mode": "none",
|
|
150
|
+
"recovery_point_seconds": 0,
|
|
151
|
+
"snapshot_schemas": [],
|
|
152
|
+
"migration_ids": []
|
|
153
|
+
},
|
|
154
|
+
"lifecycle": {
|
|
155
|
+
"reconnect_seconds": 30,
|
|
156
|
+
"drain_seconds": 5,
|
|
157
|
+
"transfer": "disabled",
|
|
158
|
+
"completion_owner": "world"
|
|
159
|
+
},
|
|
160
|
+
"placement": { "region_policy": "nearest" },
|
|
161
|
+
"limits_profile": "default"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`runtime-manifest.json` describes the server component:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"profile": "headless_engine",
|
|
170
|
+
"entry_point": "authority/main.tscn",
|
|
171
|
+
"export_preset": "Courtyard Authority"
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`source-domains.json` marks the folders. The export uses it to keep server
|
|
176
|
+
code off players' devices:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"schema": "summer.source-domain-graph.v1",
|
|
181
|
+
"roots": [
|
|
182
|
+
{ "domain": "shared", "paths": ["project.godot", "network", "world.json", "runtime-manifest.json"] },
|
|
183
|
+
{ "domain": "client", "paths": ["client"] },
|
|
184
|
+
{ "domain": "authority_engine", "paths": ["authority"] }
|
|
185
|
+
],
|
|
186
|
+
"dependencies": []
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 5. The network composition
|
|
191
|
+
|
|
192
|
+
Both sides load the same composition. It declares every stream the game
|
|
193
|
+
sends, and client and authority refuse each other when theirs differ. Start
|
|
194
|
+
with one Command stream; later skills add more. `network/composition.tres`:
|
|
195
|
+
|
|
196
|
+
```ini
|
|
197
|
+
[gd_resource type="SummerNetworkComposition" load_steps=6 format=3]
|
|
198
|
+
|
|
199
|
+
[sub_resource type="SummerNetworkField" id="CommandBytes"]
|
|
200
|
+
field_name = &"b"
|
|
201
|
+
type = 20
|
|
202
|
+
|
|
203
|
+
[sub_resource type="SummerNetworkSchema" id="CommandSchema"]
|
|
204
|
+
schema_id = &"courtyard.cmd.v1"
|
|
205
|
+
state_fields = Array[SummerNetworkField]([SubResource("CommandBytes")])
|
|
206
|
+
|
|
207
|
+
[sub_resource type="SummerNetworkField" id="ResultBytes"]
|
|
208
|
+
field_name = &"b"
|
|
209
|
+
type = 20
|
|
210
|
+
|
|
211
|
+
[sub_resource type="SummerNetworkSchema" id="ResultSchema"]
|
|
212
|
+
schema_id = &"courtyard.cmd.result.v1"
|
|
213
|
+
state_fields = Array[SummerNetworkField]([SubResource("ResultBytes")])
|
|
214
|
+
|
|
215
|
+
[sub_resource type="SummerNetworkOccurrenceStream" id="Commands"]
|
|
216
|
+
stream_id = &"courtyard.cmd"
|
|
217
|
+
command_scope = 2
|
|
218
|
+
command_tick_dispatch = true
|
|
219
|
+
command_timeout_msec = 8000
|
|
220
|
+
payload_schema = SubResource("CommandSchema")
|
|
221
|
+
result_schema = SubResource("ResultSchema")
|
|
222
|
+
|
|
223
|
+
[resource]
|
|
224
|
+
network_version = "1.0.0"
|
|
225
|
+
occurrence_streams = Array[SummerNetworkOccurrenceStream]([SubResource("Commands")])
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
- A Command's payload and result travel as JSON in a byte field (`type = 20`,
|
|
229
|
+
`TYPE_PACKED_BYTE_ARRAY`): a network `String` holds at most 256 bytes.
|
|
230
|
+
- `command_scope = 2` is `COMMAND_SCOPE_WORLD`.
|
|
231
|
+
- `command_tick_dispatch = true` makes the authority handle a Command after
|
|
232
|
+
the tick's player poses are installed, so its checks use current positions.
|
|
233
|
+
|
|
234
|
+
The player entity's schema is empty, because players move through State
|
|
235
|
+
groups (`multiplayer-movement`). `network/player_schema.tres`:
|
|
236
|
+
|
|
237
|
+
```ini
|
|
238
|
+
[gd_resource type="SummerNetworkSchema" format=3]
|
|
239
|
+
|
|
240
|
+
[resource]
|
|
241
|
+
schema_id = &"courtyard.player.v1"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## 6. Shared code
|
|
245
|
+
|
|
246
|
+
`network/game.gd` holds every id and builds the network nodes the same way
|
|
247
|
+
on both sides:
|
|
248
|
+
|
|
249
|
+
```gdscript
|
|
250
|
+
extends RefCounted
|
|
251
|
+
## Shared by the client and the authority: ids, streams and shared rules.
|
|
252
|
+
|
|
253
|
+
const GAME_ID := "game_courtyard"
|
|
254
|
+
const QUEUE := &"courtyard"
|
|
255
|
+
const CMD := &"courtyard.cmd"
|
|
256
|
+
const MAX_PLAYERS := 8
|
|
257
|
+
const COMPOSITION := preload("res://network/composition.tres")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
## Adds the World, an Entities root and the Spawner under `parent`. Both sides
|
|
261
|
+
## pass their own player archetype; the composition is the same file.
|
|
262
|
+
static func build_network(parent: Node, player: SummerEntityArchetype) -> SummerNetworkSpawner:
|
|
263
|
+
var world := SummerNetworkWorld.new()
|
|
264
|
+
world.name = "World"
|
|
265
|
+
parent.add_child(world)
|
|
266
|
+
var entities := Node3D.new()
|
|
267
|
+
entities.name = "Entities"
|
|
268
|
+
parent.add_child(entities)
|
|
269
|
+
var spawner := SummerNetworkSpawner.new()
|
|
270
|
+
spawner.name = "Spawner"
|
|
271
|
+
spawner.network_world_path = NodePath("../World")
|
|
272
|
+
spawner.entities_root_path = NodePath("../Entities")
|
|
273
|
+
spawner.archetypes = [player]
|
|
274
|
+
spawner.player_archetype = &"player" # one entity per joined player
|
|
275
|
+
spawner.network_composition = COMPOSITION
|
|
276
|
+
parent.add_child(spawner)
|
|
277
|
+
return spawner
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
## Commands and results travel as JSON bytes: a network String holds at most
|
|
281
|
+
## 256 bytes.
|
|
282
|
+
static func pack(document: Dictionary) -> Dictionary:
|
|
283
|
+
return {"b": JSON.stringify(document).to_utf8_buffer()}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
static func unpack(fields: Dictionary) -> Dictionary:
|
|
287
|
+
var parsed: Variant = JSON.parse_string((fields.get("b", PackedByteArray()) as PackedByteArray).get_string_from_utf8())
|
|
288
|
+
return parsed if parsed is Dictionary else {}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Each side passes its own **player archetype**: the scenes the Spawner spawns
|
|
292
|
+
for every joined player. The authority gets a model with no visuals. Clients
|
|
293
|
+
get an owner scene for their own player and an observer scene for everyone
|
|
294
|
+
else. Splitting them keeps client scenes out of the server and server scenes
|
|
295
|
+
out of the client.
|
|
296
|
+
|
|
297
|
+
## 7. The authority
|
|
298
|
+
|
|
299
|
+
The authority's player archetype, `authority/player_archetype.tres`, names
|
|
300
|
+
only an `authority_model_scene`: `authority/player_model.tscn`, a bare
|
|
301
|
+
`Node3D`. Both are in [files.md](files.md).
|
|
302
|
+
|
|
303
|
+
`authority/main.gd` initializes Summer, waits for its World, and answers the
|
|
304
|
+
`join` Command:
|
|
305
|
+
|
|
306
|
+
```gdscript
|
|
307
|
+
extends Node
|
|
308
|
+
## The match server. It runs headless and owns every rule.
|
|
309
|
+
|
|
310
|
+
const Game = preload("res://network/game.gd")
|
|
311
|
+
const PlayerArchetype = preload("res://authority/player_archetype.tres")
|
|
312
|
+
|
|
313
|
+
var spawner: SummerNetworkSpawner
|
|
314
|
+
var world: SummerNetworkWorld
|
|
315
|
+
var seat_of := {} # session_id -> seat, held until the Session leaves
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
func _ready() -> void:
|
|
319
|
+
spawner = Game.build_network(self, PlayerArchetype)
|
|
320
|
+
world = $World
|
|
321
|
+
spawner.entity_spawned.connect(_on_player_spawned)
|
|
322
|
+
spawner.command_received.connect(_on_command)
|
|
323
|
+
world.session_left.connect(_on_session_left)
|
|
324
|
+
var initialized: SummerResult = await Summer.initialize(Game.GAME_ID).get_result_or_completed_signal()
|
|
325
|
+
if not initialized.ok:
|
|
326
|
+
push_error(initialized.message)
|
|
327
|
+
return
|
|
328
|
+
if not world.is_ready():
|
|
329
|
+
await world.binding_ready
|
|
330
|
+
if not spawner.is_network_ready():
|
|
331
|
+
await spawner.network_ready
|
|
332
|
+
print("COURTYARD_AUTHORITY_READY")
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
## A seat is a player's place in this World for their whole stay, across
|
|
336
|
+
## reconnects. Returns -1 when the World is full.
|
|
337
|
+
func _seat(session: SummerSession) -> int:
|
|
338
|
+
if seat_of.has(session.session_id):
|
|
339
|
+
return seat_of[session.session_id]
|
|
340
|
+
var taken := seat_of.values()
|
|
341
|
+
for seat in Game.MAX_PLAYERS:
|
|
342
|
+
if seat not in taken:
|
|
343
|
+
seat_of[session.session_id] = seat
|
|
344
|
+
return seat
|
|
345
|
+
return -1
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
## The Spawner spawns one player entity per joined player, once that player's
|
|
349
|
+
## network is ready (and again after a reconnect).
|
|
350
|
+
func _on_player_spawned(entity: SummerNetworkEntity) -> void:
|
|
351
|
+
print("COURTYARD_PLAYER_SPAWNED seat=%d entity=%d" % [_seat(entity.session), entity.entity_id])
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
func _on_command(request: SummerNetworkCommandRequest) -> void:
|
|
355
|
+
var command := Game.unpack(request.payload)
|
|
356
|
+
match str(command.get("c", "")):
|
|
357
|
+
"join":
|
|
358
|
+
_join(request)
|
|
359
|
+
_:
|
|
360
|
+
request.refuse(&"unknown_command")
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
## A client sends "join" once its network is ready.
|
|
364
|
+
func _join(request: SummerNetworkCommandRequest) -> void:
|
|
365
|
+
var seat := _seat(request.get_session())
|
|
366
|
+
if seat < 0:
|
|
367
|
+
request.refuse(&"match_full")
|
|
368
|
+
return
|
|
369
|
+
request.accept({}, Game.pack({"seat": seat}))
|
|
370
|
+
print("COURTYARD_PLAYER_JOINED seat=%d" % seat)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
## The Session ended for good: free the seat and everything the player had.
|
|
374
|
+
func _on_session_left(session: SummerSession, reason: String) -> void:
|
|
375
|
+
var seat: int = seat_of.get(session.session_id, -1)
|
|
376
|
+
seat_of.erase(session.session_id)
|
|
377
|
+
print("COURTYARD_PLAYER_LEFT seat=%d reason=%s" % [seat, reason])
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`authority/main.tscn` is a plain `Node` with that script ([files.md](files.md)).
|
|
381
|
+
|
|
382
|
+
- **Never** read who sent a Command from its payload: `request.get_session()`
|
|
383
|
+
is verified. Don't name your own classes `SummerSession`, `SummerWorld` or
|
|
384
|
+
other engine class names.
|
|
385
|
+
- `session_disconnected` means the player's connection dropped and their seat
|
|
386
|
+
is held. `session_left` means they are gone for good. Free their things in
|
|
387
|
+
`session_left` only. Under Local Play it arrives when the World ends.
|
|
388
|
+
|
|
389
|
+
## 8. The client
|
|
390
|
+
|
|
391
|
+
The client's player archetype, `client/player_archetype.tres`, names an
|
|
392
|
+
`owner_projection_scene` and an `observer_projection_scene`:
|
|
393
|
+
|
|
394
|
+
```ini
|
|
395
|
+
[gd_resource type="SummerEntityArchetype" load_steps=4 format=3]
|
|
396
|
+
|
|
397
|
+
[ext_resource type="SummerNetworkSchema" path="res://network/player_schema.tres" id="1"]
|
|
398
|
+
[ext_resource type="PackedScene" path="res://client/player_owner.tscn" id="2"]
|
|
399
|
+
[ext_resource type="PackedScene" path="res://client/player_observer.tscn" id="3"]
|
|
400
|
+
|
|
401
|
+
[resource]
|
|
402
|
+
archetype_id = &"player"
|
|
403
|
+
network_schema = ExtResource("1")
|
|
404
|
+
owner_projection_scene = ExtResource("2")
|
|
405
|
+
observer_projection_scene = ExtResource("3")
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
`client/player_owner.tscn` is a `CharacterBody3D` with a capsule and the
|
|
409
|
+
camera. `client/player_observer.tscn` is a `Node3D` with a capsule and no
|
|
410
|
+
collision. Both are in [files.md](files.md).
|
|
411
|
+
|
|
412
|
+
`client/main.gd` joins the queue, waits until the network is ready, then
|
|
413
|
+
tells the authority it has arrived:
|
|
414
|
+
|
|
415
|
+
```gdscript
|
|
416
|
+
extends Node
|
|
417
|
+
## What each player runs: joins the match and shows every player.
|
|
418
|
+
|
|
419
|
+
const Game = preload("res://network/game.gd")
|
|
420
|
+
const PlayerArchetype = preload("res://client/player_archetype.tres")
|
|
421
|
+
|
|
422
|
+
var spawner: SummerNetworkSpawner
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
func _ready() -> void:
|
|
426
|
+
spawner = Game.build_network(self, PlayerArchetype)
|
|
427
|
+
# Connect signals like state_group_created here, before joining, or values
|
|
428
|
+
# that arrive during the join go unseen (multiplayer-movement adds them).
|
|
429
|
+
var initialized: SummerResult = await Summer.initialize(Game.GAME_ID).get_result_or_completed_signal()
|
|
430
|
+
if not initialized.ok:
|
|
431
|
+
push_error(initialized.message)
|
|
432
|
+
return
|
|
433
|
+
var join := Summer.client.join(SummerJoinTarget.queue(Game.QUEUE))
|
|
434
|
+
# Hosted matchmaking may ask the player to accept a match.
|
|
435
|
+
join.acceptance_required.connect(func(proposal: SummerMatchmakingProposal) -> void: proposal.accept())
|
|
436
|
+
var joined: SummerResult = await join.get_result_or_completed_signal()
|
|
437
|
+
if not joined.ok:
|
|
438
|
+
push_error("join failed: %s %s" % [joined.code, joined.message])
|
|
439
|
+
return
|
|
440
|
+
if not spawner.is_network_ready():
|
|
441
|
+
await spawner.network_ready
|
|
442
|
+
var entered := await command({"c": "join"})
|
|
443
|
+
if not entered.ok:
|
|
444
|
+
push_error("could not enter the match: %s" % entered.error)
|
|
445
|
+
return
|
|
446
|
+
print("COURTYARD_CLIENT_JOINED")
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
## Sends one Command and waits for the authority's answer.
|
|
450
|
+
func command(payload: Dictionary) -> Dictionary:
|
|
451
|
+
var admission := spawner.enqueue_command(Game.CMD, Game.pack(payload))
|
|
452
|
+
if not admission.is_enqueued():
|
|
453
|
+
return {"ok": false, "error": String(admission.get_code())}
|
|
454
|
+
var handle := admission.get_handle()
|
|
455
|
+
if not handle.is_terminal():
|
|
456
|
+
await handle.completed
|
|
457
|
+
if handle.get_outcome() == SummerNetworkCommandHandle.OUTCOME_ACCEPTED:
|
|
458
|
+
return {"ok": true, "error": "", "result": Game.unpack(handle.get_result())}
|
|
459
|
+
return {"ok": false, "error": String(handle.get_reason())}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
`client/main.tscn` holds the level (a floor and a light) and that script
|
|
463
|
+
([files.md](files.md)).
|
|
464
|
+
|
|
465
|
+
- Connect `state_group_created` and `entity_spawned` **before** joining, or
|
|
466
|
+
values that arrive during the join go unseen.
|
|
467
|
+
- `Summer.client.join` succeeding means the player has a seat. Send Commands
|
|
468
|
+
only after `spawner.network_ready`.
|
|
469
|
+
- Hosted, matchmaking may ask the player to accept a match. This skeleton
|
|
470
|
+
accepts it right away; `summer-matchmaking` shows a real prompt.
|
|
471
|
+
|
|
472
|
+
## 9. Run it with Local Play
|
|
473
|
+
|
|
474
|
+
Import once so new scripts register, then start two players.
|
|
475
|
+
|
|
476
|
+
With the Summer MCP tools:
|
|
477
|
+
|
|
478
|
+
```
|
|
479
|
+
summer_project_setting name="summer/local_play/players" value=2
|
|
480
|
+
summer_play
|
|
481
|
+
summer_get_diagnostics
|
|
482
|
+
summer_stop
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
In the editor, **Debug > Local Multiplayer** sets the same player count; then
|
|
486
|
+
press Play. From a terminal (`<summer>` is the Summer editor executable):
|
|
487
|
+
|
|
488
|
+
```sh
|
|
489
|
+
<summer> --headless --path . --import
|
|
490
|
+
<summer> --path . --summer-local-play 2 --summer-local-play-headless --summer-local-play-timeout 15
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Read the processes' output. Each line is prefixed with `[authority]`,
|
|
494
|
+
`[player-1]` or `[player-2]`:
|
|
495
|
+
|
|
496
|
+
- [ ] `[authority] COURTYARD_AUTHORITY_READY`
|
|
497
|
+
- [ ] `[authority] COURTYARD_PLAYER_SPAWNED` twice and `COURTYARD_PLAYER_JOINED` twice
|
|
498
|
+
- [ ] `COURTYARD_CLIENT_JOINED` from each player
|
|
499
|
+
- [ ] No `SCRIPT ERROR`, and no `[SummerNetworkSpawner] cannot attach` warning
|
|
500
|
+
- [ ] The last line, `SUMMER_LOCAL_PLAY_RESULT {...}`, has `"ok":true` and `"script_errors":0` for every process
|
|
501
|
+
|
|
502
|
+
`cannot attach` means the composition is invalid, and nothing networked will
|
|
503
|
+
work. Local Play still reports every client as joined, so look for the
|
|
504
|
+
warning yourself.
|
|
505
|
+
|
|
506
|
+
## Common mistakes
|
|
507
|
+
|
|
508
|
+
| Don't | Do | Why |
|
|
509
|
+
|---|---|---|
|
|
510
|
+
| `@rpc`, `MultiplayerSynchronizer`, `ENetMultiplayerPeer` | The Spawner, State groups and Commands | Unverified peers, no late join, no Summer hosting |
|
|
511
|
+
| `if multiplayer.is_server()` in one shared scene | Separate `client/` and `authority/` scenes | The engine starts the right scene on each side |
|
|
512
|
+
| Load an `authority/` file from client or shared code | Move what both sides need into `network/` | The export leaves `authority/` out of the players' pack |
|
|
513
|
+
| Build the composition differently on each side | One `network/composition.tres`, loaded by both | Different compositions refuse each other |
|
|
514
|
+
| Send a Command right after `join` succeeds | Await `spawner.network_ready` first | The authority does not know the client's network until then |
|
|
515
|
+
| Free a player's things on `session_disconnected` | Free them on `session_left` | A disconnected player keeps their seat and may reconnect |
|
|
516
|
+
| Trust "every client joined" alone | Also check for `cannot attach` and your own ready lines | A broken composition still joins |
|
|
517
|
+
|
|
518
|
+
## Next
|
|
519
|
+
|
|
520
|
+
`multiplayer-movement`: each player moves their own character, and everyone
|
|
521
|
+
else sees it move smoothly.
|