summer-engine 3.1.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 +102 -0
- package/README.md +18 -3
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/skills.js +20 -2
- 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 +18 -2
- 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/installer/agent-config.js +5 -3
- package/dist/installer/agent-table.d.ts +16 -0
- package/dist/installer/agent-table.js +52 -17
- 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/amp/README.md +1 -1
- package/integrations/crush/README.md +1 -1
- package/integrations/grok-build/README.md +1 -1
- package/integrations/hermes/README.md +1 -1
- package/integrations/kimi-code/README.md +1 -1
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/vscode-copilot/README.md +1 -1
- package/integrations/warp/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- 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 +36 -14
- 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 +1 -1
- 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 +36 -14
- package/registry/generated/skills-registry.json +421 -124
- 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,373 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: peer-to-peer-multiplayer
|
|
3
|
-
description: Use when starting a multiplayer Summer game from scratch with peer-to-peer host authority. Build the network architecture top-down so authoritative state, routing rules, and real-time rendering are not bolted on later. Use before writing game logic, not after. Trigger on "multiplayer", "peer-to-peer", "p2p", "co-op", "host", "multiplayer architecture", "add multiplayer".
|
|
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 summer_get_scene_tree summer_inspect_node summer_create_scene summer_add_node summer_set_prop summer_connect_signal summer_save_scene summer_get_script_errors summer_project_setting
|
|
9
|
-
paths: ["**/*.gd", "**/*.tscn", "**/project.godot"]
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# /peer-to-peer-multiplayer — Build a P2P Game From the Ground Up
|
|
13
|
-
|
|
14
|
-
## Overview
|
|
15
|
-
|
|
16
|
-
**Multiplayer is architecture, not a feature.** Bolting it onto a single-player game is 5× harder than starting multiplayer-first. This skill walks the four layers in order. Skip any of them and you'll spend the rest of development paying for it in cheat-vulnerable state, desync bugs, and "why is the other player teleporting" reports.
|
|
17
|
-
|
|
18
|
-
The four layers, top-down:
|
|
19
|
-
|
|
20
|
-
1. **NetworkManager** — the autoload that owns peer IDs, connection lifecycle, message routing.
|
|
21
|
-
2. **Authoritative state** — the decision matrix for *which fields the host owns vs. which the client renders*.
|
|
22
|
-
3. **Three routing patterns** — host→client, client→host, client→client. Each has different rules.
|
|
23
|
-
4. **Real-time rendering** — prediction + interpolation so the local player feels responsive while remote state catches up.
|
|
24
|
-
|
|
25
|
-
Build them in that order. Don't write a single piece of game logic until layers 1 + 2 exist.
|
|
26
|
-
|
|
27
|
-
## Step 0 — Should it be P2P at all?
|
|
28
|
-
|
|
29
|
-
Ask the user before anything else:
|
|
30
|
-
|
|
31
|
-
> Are players connecting through Steam friends / Epic friends / a private invite link, or do they need matchmaking at scale (1000s of concurrent matches)?
|
|
32
|
-
|
|
33
|
-
| Answer | Architecture |
|
|
34
|
-
|---|---|
|
|
35
|
-
| Friend-invite / small lobby (≤8 players) | **P2P with host authority.** This skill. |
|
|
36
|
-
| Matchmaking + anti-cheat + scale | **Dedicated client-server.** Different skill (`/host-authoritative-state`). |
|
|
37
|
-
| MMO / persistent world | **Custom server.** Out of scope for this skill. |
|
|
38
|
-
|
|
39
|
-
If P2P is right, continue. Otherwise, stop and route to the right skill.
|
|
40
|
-
|
|
41
|
-
## Layer 1 — NetworkManager
|
|
42
|
-
|
|
43
|
-
A single autoload that owns the network. Nothing else in the game touches `MultiplayerAPI` directly.
|
|
44
|
-
|
|
45
|
-
May I create `autoloads/network_manager.gd` and register it as a Summer Engine autoload?
|
|
46
|
-
|
|
47
|
-
```gdscript
|
|
48
|
-
class_name NetworkManager
|
|
49
|
-
extends Node
|
|
50
|
-
|
|
51
|
-
# ── Signals (all game systems listen here, not to MultiplayerAPI directly) ──
|
|
52
|
-
signal connection_succeeded
|
|
53
|
-
signal connection_failed(reason: String)
|
|
54
|
-
signal peer_joined(peer_id: int)
|
|
55
|
-
signal peer_left(peer_id: int)
|
|
56
|
-
signal session_ended
|
|
57
|
-
|
|
58
|
-
const DEFAULT_PORT := 7777
|
|
59
|
-
const MAX_PEERS := 8
|
|
60
|
-
|
|
61
|
-
var is_host: bool = false
|
|
62
|
-
var local_peer_id: int = 0
|
|
63
|
-
var peers: Dictionary = {} # peer_id -> { name, ready, ... }
|
|
64
|
-
|
|
65
|
-
func host(port: int = DEFAULT_PORT) -> Error:
|
|
66
|
-
var peer := ENetMultiplayerPeer.new()
|
|
67
|
-
var err := peer.create_server(port, MAX_PEERS)
|
|
68
|
-
if err != OK:
|
|
69
|
-
connection_failed.emit("create_server failed: %s" % error_string(err))
|
|
70
|
-
return err
|
|
71
|
-
multiplayer.multiplayer_peer = peer
|
|
72
|
-
is_host = true
|
|
73
|
-
local_peer_id = 1
|
|
74
|
-
_wire_signals()
|
|
75
|
-
connection_succeeded.emit()
|
|
76
|
-
return OK
|
|
77
|
-
|
|
78
|
-
func join(address: String, port: int = DEFAULT_PORT) -> Error:
|
|
79
|
-
var peer := ENetMultiplayerPeer.new()
|
|
80
|
-
var err := peer.create_client(address, port)
|
|
81
|
-
if err != OK:
|
|
82
|
-
connection_failed.emit("create_client failed: %s" % error_string(err))
|
|
83
|
-
return err
|
|
84
|
-
multiplayer.multiplayer_peer = peer
|
|
85
|
-
is_host = false
|
|
86
|
-
_wire_signals()
|
|
87
|
-
return OK
|
|
88
|
-
|
|
89
|
-
func leave() -> void:
|
|
90
|
-
if multiplayer.multiplayer_peer:
|
|
91
|
-
multiplayer.multiplayer_peer.close()
|
|
92
|
-
multiplayer.multiplayer_peer = null
|
|
93
|
-
is_host = false
|
|
94
|
-
local_peer_id = 0
|
|
95
|
-
peers.clear()
|
|
96
|
-
session_ended.emit()
|
|
97
|
-
|
|
98
|
-
func _wire_signals() -> void:
|
|
99
|
-
multiplayer.peer_connected.connect(_on_peer_connected)
|
|
100
|
-
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
|
|
101
|
-
multiplayer.connected_to_server.connect(_on_connected_to_server)
|
|
102
|
-
multiplayer.connection_failed.connect(_on_connection_failed)
|
|
103
|
-
multiplayer.server_disconnected.connect(_on_server_disconnected)
|
|
104
|
-
|
|
105
|
-
func _on_peer_connected(id: int) -> void:
|
|
106
|
-
peers[id] = { "name": "Player %d" % id, "ready": false }
|
|
107
|
-
peer_joined.emit(id)
|
|
108
|
-
|
|
109
|
-
func _on_peer_disconnected(id: int) -> void:
|
|
110
|
-
peers.erase(id)
|
|
111
|
-
peer_left.emit(id)
|
|
112
|
-
|
|
113
|
-
func _on_connected_to_server() -> void:
|
|
114
|
-
local_peer_id = multiplayer.get_unique_id()
|
|
115
|
-
connection_succeeded.emit()
|
|
116
|
-
|
|
117
|
-
func _on_connection_failed() -> void:
|
|
118
|
-
connection_failed.emit("connection refused or timed out")
|
|
119
|
-
|
|
120
|
-
func _on_server_disconnected() -> void:
|
|
121
|
-
leave()
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Register as autoload: `summer_project_setting(key="autoload/NetworkManager", value="*res://autoloads/network_manager.gd")`. The leading `*` makes it a singleton.
|
|
125
|
-
|
|
126
|
-
**Why this shape:** every system in the game listens to `NetworkManager.peer_joined` / `peer_left`, never to `multiplayer.peer_connected` directly. When you swap transports (ENet → WebRTC → Steam P2P) later, you change one file.
|
|
127
|
-
|
|
128
|
-
## Layer 2 — Authoritative State
|
|
129
|
-
|
|
130
|
-
For every piece of state in your game, answer one question: **if a malicious client lies about this, does it break the game?**
|
|
131
|
-
|
|
132
|
-
- **Yes** → host owns it. Clients send intent; host decides. Replicated to clients read-only.
|
|
133
|
-
- **No** (cosmetic / pure local feel) → client owns it. Replicated for visual sync only.
|
|
134
|
-
|
|
135
|
-
The canonical decision matrix:
|
|
136
|
-
|
|
137
|
-
| State | Owner | Reasoning |
|
|
138
|
-
|---|---|---|
|
|
139
|
-
| health, mana, stamina | **host** | damage logic must not be cheatable |
|
|
140
|
-
| score, kills, objectives | **host** | competitive integrity |
|
|
141
|
-
| inventory contents | **host** | items would dupe |
|
|
142
|
-
| ability cooldowns | **host** | spam-cast attack vector |
|
|
143
|
-
| current weapon equipped | **host** | tied to inventory |
|
|
144
|
-
| position (player) | **client-predicted, host-reconciled** | input latency feels awful otherwise |
|
|
145
|
-
| velocity | **client-predicted, host-reconciled** | same |
|
|
146
|
-
| look direction (camera yaw/pitch) | **client** | cosmetic, host doesn't need it |
|
|
147
|
-
| animation state | **client** | visual; can lead the truth |
|
|
148
|
-
| footstep sounds | **client** | host doesn't care |
|
|
149
|
-
| muzzle flash, particles | **client** | host doesn't care |
|
|
150
|
-
| chat messages | **host-relayed** | for moderation hooks |
|
|
151
|
-
|
|
152
|
-
Build a State Layer that codifies this. May I create `autoloads/game_state.gd`?
|
|
153
|
-
|
|
154
|
-
```gdscript
|
|
155
|
-
class_name GameState
|
|
156
|
-
extends Node
|
|
157
|
-
|
|
158
|
-
# Host-authoritative dictionaries. Keyed by peer_id where applicable.
|
|
159
|
-
var health: Dictionary = {} # peer_id -> int
|
|
160
|
-
var score: Dictionary = {} # peer_id -> int
|
|
161
|
-
var inventory: Dictionary = {} # peer_id -> Array[ItemData]
|
|
162
|
-
|
|
163
|
-
# Local-only mirrors (read for rendering; never written by clients).
|
|
164
|
-
var local_health_view: int = 100
|
|
165
|
-
|
|
166
|
-
func _ready() -> void:
|
|
167
|
-
NetworkManager.peer_joined.connect(_on_peer_joined)
|
|
168
|
-
NetworkManager.peer_left.connect(_on_peer_left)
|
|
169
|
-
|
|
170
|
-
# ── Host-only mutators (called via RPC from client requests) ──
|
|
171
|
-
func _host_apply_damage(target: int, amount: int) -> void:
|
|
172
|
-
if not NetworkManager.is_host: return
|
|
173
|
-
var new_hp = max(0, health.get(target, 100) - amount)
|
|
174
|
-
health[target] = new_hp
|
|
175
|
-
_broadcast_health(target, new_hp)
|
|
176
|
-
if new_hp == 0:
|
|
177
|
-
_broadcast_death(target)
|
|
178
|
-
|
|
179
|
-
@rpc("authority", "call_remote", "reliable")
|
|
180
|
-
func _broadcast_health(peer: int, hp: int) -> void:
|
|
181
|
-
if peer == NetworkManager.local_peer_id:
|
|
182
|
-
local_health_view = hp
|
|
183
|
-
|
|
184
|
-
@rpc("authority", "call_remote", "reliable")
|
|
185
|
-
func _broadcast_death(peer: int) -> void:
|
|
186
|
-
pass # play death anim, etc.
|
|
187
|
-
|
|
188
|
-
# ── Client-side request (intent only — host decides) ──
|
|
189
|
-
func client_request_damage(target: int, amount: int) -> void:
|
|
190
|
-
rpc_id(1, "_host_handle_damage_request", target, amount)
|
|
191
|
-
|
|
192
|
-
@rpc("any_peer", "call_remote", "reliable")
|
|
193
|
-
func _host_handle_damage_request(target: int, amount: int) -> void:
|
|
194
|
-
if not NetworkManager.is_host: return
|
|
195
|
-
var sender := multiplayer.get_remote_sender_id()
|
|
196
|
-
# validate: did sender's hitbox actually overlap target this frame?
|
|
197
|
-
# if validation passes, apply. otherwise, drop silently.
|
|
198
|
-
if _validate_hit(sender, target):
|
|
199
|
-
_host_apply_damage(target, amount)
|
|
200
|
-
|
|
201
|
-
func _validate_hit(_attacker: int, _target: int) -> bool:
|
|
202
|
-
return true # implement: distance check, line-of-sight, cooldown, etc.
|
|
203
|
-
|
|
204
|
-
func _on_peer_joined(id: int) -> void:
|
|
205
|
-
if NetworkManager.is_host:
|
|
206
|
-
health[id] = 100
|
|
207
|
-
score[id] = 0
|
|
208
|
-
inventory[id] = []
|
|
209
|
-
_broadcast_health(id, 100)
|
|
210
|
-
|
|
211
|
-
func _on_peer_left(id: int) -> void:
|
|
212
|
-
if NetworkManager.is_host:
|
|
213
|
-
health.erase(id)
|
|
214
|
-
score.erase(id)
|
|
215
|
-
inventory.erase(id)
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
**Key invariants enforced by this shape:**
|
|
219
|
-
- Every host-authoritative field has a single mutator function on the host.
|
|
220
|
-
- Clients call `client_request_*` to request a change; the host validates and broadcasts.
|
|
221
|
-
- Clients never directly modify `health` / `score` / `inventory` even on their own peer.
|
|
222
|
-
- `@rpc("authority", ...)` ensures only the host can call broadcast functions.
|
|
223
|
-
- `@rpc("any_peer", ...)` allows any client to call request functions, but the function checks `NetworkManager.is_host` and validates the request.
|
|
224
|
-
|
|
225
|
-
## Layer 3 — The Three Routing Patterns
|
|
226
|
-
|
|
227
|
-
Every networked function fits one of three patterns. Pick the right one or you'll have bugs.
|
|
228
|
-
|
|
229
|
-
### Pattern A: Host → Client (state broadcast)
|
|
230
|
-
|
|
231
|
-
State the host owns, replicated read-only to clients.
|
|
232
|
-
|
|
233
|
-
```gdscript
|
|
234
|
-
@rpc("authority", "call_remote", "reliable")
|
|
235
|
-
func _broadcast_score(peer: int, value: int) -> void:
|
|
236
|
-
score[peer] = value
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
**Rules:**
|
|
240
|
-
- `authority` mode = only host can call this (anyone else's call is ignored).
|
|
241
|
-
- `call_remote` = doesn't run on the caller (host doesn't need to call its own update).
|
|
242
|
-
- `reliable` = must arrive (state corruption otherwise).
|
|
243
|
-
|
|
244
|
-
### Pattern B: Client → Host (intent / request)
|
|
245
|
-
|
|
246
|
-
Client wants something to happen. Host decides yes or no.
|
|
247
|
-
|
|
248
|
-
```gdscript
|
|
249
|
-
@rpc("any_peer", "call_remote", "reliable")
|
|
250
|
-
func _host_handle_use_item_request(item_id: String) -> void:
|
|
251
|
-
if not NetworkManager.is_host: return
|
|
252
|
-
var sender := multiplayer.get_remote_sender_id()
|
|
253
|
-
if _can_use(sender, item_id):
|
|
254
|
-
_host_apply_use_item(sender, item_id)
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**Rules:**
|
|
258
|
-
- `any_peer` = any client can call.
|
|
259
|
-
- First line is always `if not NetworkManager.is_host: return`. Defensive.
|
|
260
|
-
- Always validate (`_can_use`). Trusting client input is the #1 multiplayer cheat vector.
|
|
261
|
-
- Use `multiplayer.get_remote_sender_id()` — clients can't lie about who sent the request.
|
|
262
|
-
|
|
263
|
-
### Pattern C: Client → Client (cosmetic relay)
|
|
264
|
-
|
|
265
|
-
Visual-only events. Host relays but doesn't validate.
|
|
266
|
-
|
|
267
|
-
```gdscript
|
|
268
|
-
@rpc("any_peer", "call_remote", "unreliable")
|
|
269
|
-
func _peer_play_emote(emote_id: String) -> void:
|
|
270
|
-
# everyone plays the emote on the sender's player
|
|
271
|
-
var sender := multiplayer.get_remote_sender_id()
|
|
272
|
-
_play_emote_visual(sender, emote_id)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
**Rules:**
|
|
276
|
-
- `unreliable` is fine for cosmetic stuff (animations, particles, footstep SFX).
|
|
277
|
-
- No validation needed — worst case is a player's emote desyncs by a frame.
|
|
278
|
-
- Save bandwidth: keep payloads tiny.
|
|
279
|
-
|
|
280
|
-
**Anti-pattern:** mixing patterns in one function. A function that broadcasts state AND handles client intent is going to have bugs. Split.
|
|
281
|
-
|
|
282
|
-
## Layer 4 — Real-time Rendering
|
|
283
|
-
|
|
284
|
-
The local player needs to feel instant. Remote players need to look smooth despite network jitter. Two techniques:
|
|
285
|
-
|
|
286
|
-
### Client-side prediction (for the local player's input)
|
|
287
|
-
|
|
288
|
-
```gdscript
|
|
289
|
-
# Local player runs movement immediately on input. Host runs the SAME function
|
|
290
|
-
# authoritatively. If they diverge, the client snaps to host state.
|
|
291
|
-
|
|
292
|
-
func _physics_process(delta: float) -> void:
|
|
293
|
-
if not _is_local_authority(): return
|
|
294
|
-
var input := _read_input()
|
|
295
|
-
velocity = _compute_velocity(input, velocity, delta)
|
|
296
|
-
move_and_slide()
|
|
297
|
-
if NetworkManager.local_peer_id != 1: # not the host
|
|
298
|
-
_send_input_to_host(input)
|
|
299
|
-
|
|
300
|
-
@rpc("any_peer", "call_remote", "unreliable_ordered")
|
|
301
|
-
func _send_input_to_host(_input: InputState) -> void:
|
|
302
|
-
if not NetworkManager.is_host: return
|
|
303
|
-
# host runs the SAME _compute_velocity to validate + reconcile
|
|
304
|
-
pass
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### Interpolation (for remote players' positions)
|
|
308
|
-
|
|
309
|
-
Don't snap remote players to incoming positions — interpolate.
|
|
310
|
-
|
|
311
|
-
```gdscript
|
|
312
|
-
var _network_position_buffer: Array[Vector3] = []
|
|
313
|
-
var _network_position_timestamps: Array[float] = []
|
|
314
|
-
|
|
315
|
-
func _physics_process(_delta: float) -> void:
|
|
316
|
-
if _is_local_authority(): return # local handled by prediction
|
|
317
|
-
_interpolate_to_buffer()
|
|
318
|
-
|
|
319
|
-
@rpc("authority", "call_remote", "unreliable_ordered")
|
|
320
|
-
func _receive_remote_position(pos: Vector3, t: float) -> void:
|
|
321
|
-
_network_position_buffer.append(pos)
|
|
322
|
-
_network_position_timestamps.append(t)
|
|
323
|
-
while _network_position_buffer.size() > 4:
|
|
324
|
-
_network_position_buffer.pop_front()
|
|
325
|
-
_network_position_timestamps.pop_front()
|
|
326
|
-
|
|
327
|
-
func _interpolate_to_buffer() -> void:
|
|
328
|
-
if _network_position_buffer.size() < 2: return
|
|
329
|
-
var render_time := Time.get_ticks_msec() / 1000.0 - 0.1 # 100ms interp delay
|
|
330
|
-
# find the two buffer entries that bracket render_time, lerp between them
|
|
331
|
-
# (full impl: ~30 lines)
|
|
332
|
-
pass
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## Common mistakes (anti-patterns)
|
|
336
|
-
|
|
337
|
-
| Mistake | What goes wrong | Fix |
|
|
338
|
-
|---|---|---|
|
|
339
|
-
| Putting health on the client and trusting it | Trivial cheats: edit memory, infinite HP. | Move all damage logic to host. Client requests, host decides. |
|
|
340
|
-
| Calling `multiplayer.peer_connected.connect()` directly in 5 different scripts | Connection logic scattered. Refactor pain. | Always go through `NetworkManager` signals. |
|
|
341
|
-
| `@rpc("any_peer")` with no `is_host` check inside | Client can broadcast as if host. State corruption. | First line of every `any_peer` handler is `if not is_host: return`. |
|
|
342
|
-
| Reliable RPC for cosmetic stuff (footsteps, particles) | Bandwidth blowup. | Use `unreliable` for cosmetic. Reserve `reliable` for state. |
|
|
343
|
-
| Sending position 60×/sec via reliable RPC | Network spam, dropped packets cause stutters. | `unreliable_ordered` at 20–30 Hz + interpolation buffer on receivers. |
|
|
344
|
-
| Adding multiplayer after the game's built | Single-player code assumed direct state writes everywhere. Massive refactor. | Use this skill BEFORE writing game logic. |
|
|
345
|
-
| Forgetting host-leaves handling | Host disconnects → game freezes. | `multiplayer.server_disconnected` → graceful end-session in `NetworkManager.leave()`. |
|
|
346
|
-
|
|
347
|
-
## When to NOT use this skill
|
|
348
|
-
|
|
349
|
-
- **Single-player game with leaderboards.** No multiplayer needed.
|
|
350
|
-
- **Async multiplayer** (turn-based via REST API). This skill assumes real-time.
|
|
351
|
-
- **Dedicated server with anti-cheat.** Use `/host-authoritative-state`.
|
|
352
|
-
- **Already-shipped game getting multiplayer retrofit.** Sorry — you're in for a refactor. This skill helps for the redesign but not the migration.
|
|
353
|
-
|
|
354
|
-
## Collaborative protocol
|
|
355
|
-
|
|
356
|
-
This skill writes multiple new files (`autoloads/network_manager.gd`, `autoloads/game_state.gd`, plus updates to `project.godot` for the autoload registration). Always ask before each phase:
|
|
357
|
-
|
|
358
|
-
> May I create the NetworkManager autoload?
|
|
359
|
-
> May I create the GameState autoload and register both in project.godot?
|
|
360
|
-
> May I add the prediction/interpolation logic to your existing player scene?
|
|
361
|
-
|
|
362
|
-
Don't apply all four layers in one shot — checkpoint after each layer so the user can verify before adding the next.
|
|
363
|
-
|
|
364
|
-
## Want a working starter?
|
|
365
|
-
|
|
366
|
-
No template ships with this exact architecture yet — it's deliberately built fresh for each project because the State Layer's authority decisions are game-specific (a stealth game's "visibility" and a shooter's "ammo count" both want host authority but for different reasons). Run this skill from scratch.
|
|
367
|
-
|
|
368
|
-
When the `template-co-op-3d` template ships in the registry, this skill will reference it as a starter — for now it's hand-rolled.
|
|
369
|
-
|
|
370
|
-
## See also
|
|
371
|
-
|
|
372
|
-
- [`host-authoritative-state`](../host-authoritative-state/SKILL.md) — deeper dive on JUST the state ownership decision matrix
|
|
373
|
-
- [`setup-multiplayer`](../setup-multiplayer/SKILL.md) — lighter intro that just gets a session running (use that one if the user just wants 2 players to see each other; use this one if they want to ship a real game)
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
id: skill/peer-to-peer-multiplayer
|
|
2
|
-
kind: skill
|
|
3
|
-
version: 1.0.4
|
|
4
|
-
summary: Start a multiplayer game from scratch with peer-to-peer host authority — network architecture built top-down, before writing game logic.
|
|
5
|
-
use_when:
|
|
6
|
-
- starting a multiplayer Summer game from scratch with peer-to-peer host authority, before writing game logic
|
|
7
|
-
- I want to play co-op or online with friends and the game doesn't exist yet
|
|
8
|
-
facets:
|
|
9
|
-
lifecycle:
|
|
10
|
-
- build
|
|
11
|
-
domains:
|
|
12
|
-
- multiplayer
|
|
13
|
-
- project
|
|
14
|
-
modalities:
|
|
15
|
-
- scripts
|
|
16
|
-
- scenes
|
|
17
|
-
compatibility:
|
|
18
|
-
engine: ">=4.6"
|
|
19
|
-
related:
|
|
20
|
-
skills:
|
|
21
|
-
- skill/host-authoritative-state
|
|
22
|
-
- skill/setup-multiplayer
|
|
23
|
-
source: official
|
|
24
|
-
license: MIT
|
|
25
|
-
status: stable
|
|
26
|
-
recommended: true
|
|
27
|
-
aliases:
|
|
28
|
-
- skills/multiplayer-and-networking/peer-to-peer-multiplayer
|