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.
Files changed (366) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +27 -5
  3. package/.codex-plugin/plugin.json +28 -6
  4. package/.cursor-plugin/plugin.json +27 -5
  5. package/.factory-plugin/plugin.json +27 -5
  6. package/.opencode/plugins/summer.js +1 -1
  7. package/CHANGELOG.md +102 -0
  8. package/README.md +18 -3
  9. package/assets/audit/scene_audit.gd +4136 -0
  10. package/assets/seeing/seeing_probe.gd +1873 -0
  11. package/dist/cli/commands/skills.js +20 -2
  12. package/dist/core/api-client.d.ts +9 -1
  13. package/dist/core/api-client.js +21 -4
  14. package/dist/core/capabilities/agent-playbook.js +46 -0
  15. package/dist/core/capabilities/audit/accept.d.ts +55 -0
  16. package/dist/core/capabilities/audit/accept.js +147 -0
  17. package/dist/core/capabilities/audit/args.d.ts +95 -0
  18. package/dist/core/capabilities/audit/args.js +89 -0
  19. package/dist/core/capabilities/audit/audit.d.ts +56 -0
  20. package/dist/core/capabilities/audit/audit.js +287 -0
  21. package/dist/core/capabilities/audit/frame.d.ts +14 -0
  22. package/dist/core/capabilities/audit/frame.js +35 -0
  23. package/dist/core/capabilities/audit/gaps.d.ts +96 -0
  24. package/dist/core/capabilities/audit/gaps.js +257 -0
  25. package/dist/core/capabilities/audit/judge.d.ts +162 -0
  26. package/dist/core/capabilities/audit/judge.js +1134 -0
  27. package/dist/core/capabilities/audit/math.d.ts +110 -0
  28. package/dist/core/capabilities/audit/math.js +263 -0
  29. package/dist/core/capabilities/audit/summary.d.ts +74 -0
  30. package/dist/core/capabilities/audit/summary.js +212 -0
  31. package/dist/core/capabilities/board.d.ts +31 -0
  32. package/dist/core/capabilities/board.js +43 -0
  33. package/dist/core/capabilities/camera-view.d.ts +4 -2
  34. package/dist/core/capabilities/camera-view.js +6 -3
  35. package/dist/core/capabilities/connect-signal.d.ts +85 -0
  36. package/dist/core/capabilities/connect-signal.js +285 -0
  37. package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
  38. package/dist/core/capabilities/engine-fallbacks.js +5 -0
  39. package/dist/core/capabilities/engine-ops.d.ts +5 -1
  40. package/dist/core/capabilities/engine-ops.js +0 -3
  41. package/dist/core/capabilities/engine-receipt.d.ts +12 -0
  42. package/dist/core/capabilities/engine-receipt.js +26 -0
  43. package/dist/core/capabilities/file-read.d.ts +64 -0
  44. package/dist/core/capabilities/file-read.js +325 -0
  45. package/dist/core/capabilities/game-task-plan.js +4 -2
  46. package/dist/core/capabilities/grep.d.ts +60 -0
  47. package/dist/core/capabilities/grep.js +190 -0
  48. package/dist/core/capabilities/inspect-node.d.ts +56 -0
  49. package/dist/core/capabilities/inspect-node.js +225 -0
  50. package/dist/core/capabilities/inspect-resource.d.ts +61 -0
  51. package/dist/core/capabilities/inspect-resource.js +376 -0
  52. package/dist/core/capabilities/math3d.d.ts +28 -0
  53. package/dist/core/capabilities/math3d.js +88 -0
  54. package/dist/core/capabilities/motion-generation.d.ts +62 -0
  55. package/dist/core/capabilities/motion-generation.js +138 -0
  56. package/dist/core/capabilities/placement-batch.d.ts +102 -0
  57. package/dist/core/capabilities/placement-batch.js +483 -0
  58. package/dist/core/capabilities/placement-math.d.ts +70 -0
  59. package/dist/core/capabilities/placement-math.js +209 -0
  60. package/dist/core/capabilities/placement-script.d.ts +42 -0
  61. package/dist/core/capabilities/placement-script.js +1096 -0
  62. package/dist/core/capabilities/placement.d.ts +485 -0
  63. package/dist/core/capabilities/placement.js +1731 -0
  64. package/dist/core/capabilities/project-context.d.ts +33 -2
  65. package/dist/core/capabilities/project-context.js +87 -4
  66. package/dist/core/capabilities/replace-node.d.ts +91 -0
  67. package/dist/core/capabilities/replace-node.js +423 -0
  68. package/dist/core/capabilities/runtime-control.d.ts +18 -2
  69. package/dist/core/capabilities/runtime-control.js +67 -2
  70. package/dist/core/capabilities/scene-batch.d.ts +63 -0
  71. package/dist/core/capabilities/scene-batch.js +394 -0
  72. package/dist/core/capabilities/scene-readback.d.ts +36 -0
  73. package/dist/core/capabilities/scene-readback.js +54 -0
  74. package/dist/core/capabilities/seeing/args.d.ts +348 -0
  75. package/dist/core/capabilities/seeing/args.js +178 -0
  76. package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
  77. package/dist/core/capabilities/seeing/candidates.js +282 -0
  78. package/dist/core/capabilities/seeing/math.d.ts +134 -0
  79. package/dist/core/capabilities/seeing/math.js +269 -0
  80. package/dist/core/capabilities/seeing/probe.d.ts +62 -0
  81. package/dist/core/capabilities/seeing/probe.js +188 -0
  82. package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
  83. package/dist/core/capabilities/seeing/scoring.js +639 -0
  84. package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
  85. package/dist/core/capabilities/seeing/seeing.js +1341 -0
  86. package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
  87. package/dist/core/capabilities/seeing/shot-store.js +204 -0
  88. package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
  89. package/dist/core/capabilities/surface-snap-mesh.js +425 -0
  90. package/dist/core/capabilities/surface-snap.d.ts +77 -0
  91. package/dist/core/capabilities/surface-snap.js +461 -0
  92. package/dist/core/capabilities/tool-dispatch.js +231 -72
  93. package/dist/core/capabilities/tscn.d.ts +85 -0
  94. package/dist/core/capabilities/tscn.js +391 -0
  95. package/dist/core/capabilities/world-snapshot.d.ts +58 -0
  96. package/dist/core/capabilities/world-snapshot.js +150 -0
  97. package/dist/core/capability-skew.d.ts +5 -5
  98. package/dist/core/capability-skew.js +14 -6
  99. package/dist/core/headless/worker-engine-client.d.ts +1 -1
  100. package/dist/core/headless/worker-engine-client.js +2 -2
  101. package/dist/core/library-read.d.ts +37 -5
  102. package/dist/core/library-read.js +264 -33
  103. package/dist/core/library-search.d.ts +2 -2
  104. package/dist/installer/agent-config.js +5 -3
  105. package/dist/installer/agent-table.d.ts +16 -0
  106. package/dist/installer/agent-table.js +52 -17
  107. package/dist/mcp/server.js +9 -3
  108. package/dist/mcp/tools/asset-tools.js +21 -3
  109. package/dist/mcp/tools/audit-tools.d.ts +23 -0
  110. package/dist/mcp/tools/audit-tools.js +60 -0
  111. package/dist/mcp/tools/debug-tools.js +3 -1
  112. package/dist/mcp/tools/file-tools.js +17 -4
  113. package/dist/mcp/tools/generate-tools.js +74 -66
  114. package/dist/mcp/tools/library-tools.js +2 -0
  115. package/dist/mcp/tools/perception-tools.js +6 -19
  116. package/dist/mcp/tools/placement-tools.d.ts +24 -0
  117. package/dist/mcp/tools/placement-tools.js +91 -0
  118. package/dist/mcp/tools/project-tools.js +18 -15
  119. package/dist/mcp/tools/scene-tools.js +71 -83
  120. package/dist/mcp/tools/seeing-tools.d.ts +24 -0
  121. package/dist/mcp/tools/seeing-tools.js +64 -0
  122. package/dist/mcp/tools/spatial-tools.js +17 -9
  123. package/dist/mcp/tools/visual-tools.js +59 -7
  124. package/gemini-extension.json +26 -4
  125. package/integrations/amp/README.md +1 -1
  126. package/integrations/crush/README.md +1 -1
  127. package/integrations/grok-build/README.md +1 -1
  128. package/integrations/hermes/README.md +1 -1
  129. package/integrations/kimi-code/README.md +1 -1
  130. package/integrations/mistral-vibe/README.md +1 -1
  131. package/integrations/opencode/README.md +1 -1
  132. package/integrations/rovo-dev/README.md +1 -1
  133. package/integrations/vscode-copilot/README.md +1 -1
  134. package/integrations/warp/README.md +1 -1
  135. package/integrations/windsurf/README.md +1 -1
  136. package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
  137. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  138. package/library/references/summer-folder/resource.yaml +1 -1
  139. package/library/references/summer-folder/summer-folder.md +1 -1
  140. package/library/skills/3d-lighting/SKILL.md +1 -1
  141. package/library/skills/README.md +36 -14
  142. package/library/skills/adaptive-music/SKILL.md +1 -1
  143. package/library/skills/agent-playtesting/SKILL.md +1 -1
  144. package/library/skills/ai-characters-in-games/SKILL.md +220 -0
  145. package/library/skills/ai-characters-in-games/resource.yaml +30 -0
  146. package/library/skills/ambient-bed/SKILL.md +1 -1
  147. package/library/skills/animated-loop/SKILL.md +1 -1
  148. package/library/skills/animation-tree/SKILL.md +1 -1
  149. package/library/skills/art-direction/SKILL.md +1 -1
  150. package/library/skills/asset-strategy/SKILL.md +2 -1
  151. package/library/skills/asset-strategy/resource.yaml +2 -1
  152. package/library/skills/audio-direction/SKILL.md +1 -1
  153. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  154. package/library/skills/brainstorm-game/SKILL.md +1 -1
  155. package/library/skills/brainstorming/SKILL.md +1 -1
  156. package/library/skills/browse-templates/SKILL.md +3 -3
  157. package/library/skills/browse-templates/resource.yaml +1 -1
  158. package/library/skills/camera-rigs/SKILL.md +128 -0
  159. package/library/skills/camera-rigs/resource.yaml +29 -0
  160. package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
  161. package/library/skills/character-animation-wiring/SKILL.md +1 -1
  162. package/library/skills/character-model/SKILL.md +1 -1
  163. package/library/skills/character-movement/SKILL.md +326 -0
  164. package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
  165. package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
  166. package/library/skills/character-movement/references/movement-3d.md +136 -0
  167. package/library/skills/character-movement/references/platformer-feel.md +83 -0
  168. package/library/skills/character-movement/resource.yaml +40 -0
  169. package/library/skills/character-portrait/SKILL.md +1 -1
  170. package/library/skills/cinematic-cutscene/SKILL.md +1 -1
  171. package/library/skills/combat-basics/SKILL.md +159 -0
  172. package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
  173. package/library/skills/combat-basics/resource.yaml +34 -0
  174. package/library/skills/concept-art/SKILL.md +1 -1
  175. package/library/skills/create-asset-sheet/SKILL.md +1 -1
  176. package/library/skills/debug/SKILL.md +1 -1
  177. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  178. package/library/skills/design-level/SKILL.md +1 -1
  179. package/library/skills/design-mechanic/SKILL.md +1 -1
  180. package/library/skills/design-npc/SKILL.md +1 -1
  181. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  182. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
  183. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  184. package/library/skills/environment-kit/SKILL.md +1 -1
  185. package/library/skills/export-and-ship/SKILL.md +1 -1
  186. package/library/skills/fabricating-assets/SKILL.md +1 -1
  187. package/library/skills/facial-and-lipsync/SKILL.md +1 -1
  188. package/library/skills/fps-controller/SKILL.md +9 -7
  189. package/library/skills/fps-controller/resource.yaml +4 -1
  190. package/library/skills/game-feel/SKILL.md +1 -1
  191. package/library/skills/gameskill/SKILL.md +1 -1
  192. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  193. package/library/skills/generate-motion/SKILL.md +9 -9
  194. package/library/skills/generate-motion/resource.yaml +2 -1
  195. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  196. package/library/skills/headless-scripting/SKILL.md +1 -1
  197. package/library/skills/in-game-purchases/SKILL.md +254 -0
  198. package/library/skills/in-game-purchases/resource.yaml +29 -0
  199. package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
  200. package/library/skills/investigating-bugs/SKILL.md +1 -1
  201. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
  202. package/library/skills/make-game/SKILL.md +25 -3
  203. package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
  204. package/library/skills/make-game/references/2d-platformer.md +117 -0
  205. package/library/skills/make-game/references/2d-rpg-content.md +234 -0
  206. package/library/skills/make-game/references/2d-rpg.md +241 -0
  207. package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
  208. package/library/skills/make-game/references/2d-survivors.md +237 -0
  209. package/library/skills/make-game/references/3d-fps-systems.md +216 -0
  210. package/library/skills/make-game/references/3d-fps.md +177 -0
  211. package/library/skills/make-game/references/3d-horror-systems.md +186 -0
  212. package/library/skills/make-game/references/3d-horror.md +201 -0
  213. package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
  214. package/library/skills/make-game/references/3d-third-person.md +215 -0
  215. package/library/skills/make-game/resource.yaml +6 -1
  216. package/library/skills/multiplayer/SKILL.md +150 -0
  217. package/library/skills/multiplayer/resource.yaml +33 -0
  218. package/library/skills/multiplayer-movement/SKILL.md +510 -0
  219. package/library/skills/multiplayer-movement/files.md +501 -0
  220. package/library/skills/multiplayer-movement/resource.yaml +30 -0
  221. package/library/skills/multiplayer-project/SKILL.md +521 -0
  222. package/library/skills/multiplayer-project/files.md +448 -0
  223. package/library/skills/multiplayer-project/resource.yaml +36 -0
  224. package/library/skills/multiplayer-publish/SKILL.md +229 -0
  225. package/library/skills/multiplayer-publish/export-errors.md +89 -0
  226. package/library/skills/multiplayer-publish/resource.yaml +28 -0
  227. package/library/skills/multiplayer-state/SKILL.md +438 -0
  228. package/library/skills/multiplayer-state/files.md +592 -0
  229. package/library/skills/multiplayer-state/resource.yaml +35 -0
  230. package/library/skills/multiplayer-testing/SKILL.md +236 -0
  231. package/library/skills/multiplayer-testing/resource.yaml +39 -0
  232. package/library/skills/music-track/SKILL.md +1 -1
  233. package/library/skills/navigate-summer/SKILL.md +1 -1
  234. package/library/skills/new-project/SKILL.md +1 -1
  235. package/library/skills/organic-model/SKILL.md +1 -1
  236. package/library/skills/pixel-art/SKILL.md +1 -1
  237. package/library/skills/play/SKILL.md +1 -1
  238. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  239. package/library/skills/procedural-animation/SKILL.md +2 -2
  240. package/library/skills/procedural-animation/resource.yaml +1 -1
  241. package/library/skills/prop-model/SKILL.md +1 -1
  242. package/library/skills/psx-retro-rendering/SKILL.md +1 -1
  243. package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
  244. package/library/skills/remote-deploy/SKILL.md +1 -1
  245. package/library/skills/retarget/SKILL.md +1 -1
  246. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  247. package/library/skills/save-load/SKILL.md +156 -0
  248. package/library/skills/save-load/resource.yaml +27 -0
  249. package/library/skills/scene-composition/SKILL.md +1 -1
  250. package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
  251. package/library/skills/scene-scripting/SKILL.md +1 -1
  252. package/library/skills/scene-to-level/SKILL.md +1 -1
  253. package/library/skills/skill-create/SKILL.md +1 -1
  254. package/library/skills/skill-improve/SKILL.md +1 -1
  255. package/library/skills/skill-test/SKILL.md +1 -1
  256. package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
  257. package/library/skills/skybox-panorama/SKILL.md +1 -1
  258. package/library/skills/sound-effect/SKILL.md +1 -1
  259. package/library/skills/spatial-placement/SKILL.md +134 -3
  260. package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
  261. package/library/skills/spatial-placement/resource.yaml +16 -2
  262. package/library/skills/sprite-sheet/SKILL.md +1 -1
  263. package/library/skills/summer-analytics/SKILL.md +147 -0
  264. package/library/skills/summer-analytics/resource.yaml +28 -0
  265. package/library/skills/summer-friends/SKILL.md +226 -0
  266. package/library/skills/summer-friends/resource.yaml +28 -0
  267. package/library/skills/summer-leaderboards/SKILL.md +231 -0
  268. package/library/skills/summer-leaderboards/resource.yaml +30 -0
  269. package/library/skills/summer-match-results/SKILL.md +286 -0
  270. package/library/skills/summer-match-results/resource.yaml +32 -0
  271. package/library/skills/summer-matchmaking/SKILL.md +301 -0
  272. package/library/skills/summer-matchmaking/resource.yaml +32 -0
  273. package/library/skills/summer-parties/SKILL.md +253 -0
  274. package/library/skills/summer-parties/resource.yaml +30 -0
  275. package/library/skills/summer-player-data/SKILL.md +322 -0
  276. package/library/skills/summer-player-data/resource.yaml +30 -0
  277. package/library/skills/summer-store/SKILL.md +271 -0
  278. package/library/skills/summer-store/resource.yaml +29 -0
  279. package/library/skills/summer-world-chat/SKILL.md +195 -0
  280. package/library/skills/summer-world-chat/resource.yaml +30 -0
  281. package/library/skills/summer-world-saves/SKILL.md +284 -0
  282. package/library/skills/summer-world-saves/resource.yaml +29 -0
  283. package/library/skills/text-to-motion/SKILL.md +99 -0
  284. package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
  285. package/library/skills/text-to-motion/resource.yaml +41 -0
  286. package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
  287. package/library/skills/tileable-texture/SKILL.md +1 -1
  288. package/library/skills/trailer-shot/SKILL.md +1 -1
  289. package/library/skills/tune-performance/SKILL.md +1 -1
  290. package/library/skills/ui-basics/SKILL.md +10 -1
  291. package/library/skills/ui-graphics/SKILL.md +1 -1
  292. package/library/skills/use-widget-asset/SKILL.md +1 -1
  293. package/library/skills/using-summer/SKILL.md +2 -2
  294. package/library/skills/using-summer/resource.yaml +2 -2
  295. package/library/skills/vehicle-model/SKILL.md +1 -1
  296. package/library/skills/verification-before-completion/SKILL.md +1 -1
  297. package/library/skills/verifying-scenes/SKILL.md +76 -2
  298. package/library/skills/verifying-scenes/resource.yaml +13 -2
  299. package/library/skills/vfx-dissolve/SKILL.md +1 -1
  300. package/library/skills/vfx-fire/SKILL.md +1 -1
  301. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  302. package/library/skills/vfx-lightning/SKILL.md +1 -1
  303. package/library/skills/vfx-magic-glow/SKILL.md +1 -1
  304. package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
  305. package/library/skills/vfx-smoke/SKILL.md +1 -1
  306. package/library/skills/vfx-water-ripple/SKILL.md +1 -1
  307. package/library/skills/voice-line/SKILL.md +1 -1
  308. package/library/skills/web-and-touch-input/SKILL.md +38 -0
  309. package/library/skills/web-and-touch-input/resource.yaml +28 -0
  310. package/library/skills/web-compatibility-look/SKILL.md +62 -0
  311. package/library/skills/web-compatibility-look/resource.yaml +30 -0
  312. package/library/skills/world-building-3d/SKILL.md +8 -1
  313. package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
  314. package/library/skills/world-building-3d/resource.yaml +2 -1
  315. package/library/skills/writing-plans/SKILL.md +1 -1
  316. package/library/skills/writing-skills/SKILL.md +1 -1
  317. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
  318. package/library/tools/attach-to-surface/resource.yaml +142 -0
  319. package/library/tools/batch/resource.yaml +9 -3
  320. package/library/tools/connect-ports/resource.yaml +120 -0
  321. package/library/tools/connect-signal/resource.yaml +3 -2
  322. package/library/tools/debug-views/resource.yaml +94 -0
  323. package/library/tools/frame-nodes/resource.yaml +104 -0
  324. package/library/tools/frame-shot/resource.yaml +134 -0
  325. package/library/tools/generate-motion/resource.yaml +44 -9
  326. package/library/tools/get-board/resource.yaml +50 -0
  327. package/library/tools/get-project-context/resource.yaml +13 -2
  328. package/library/tools/grep/resource.yaml +77 -0
  329. package/library/tools/inspect-asset/resource.yaml +71 -0
  330. package/library/tools/inspect-node/resource.yaml +9 -2
  331. package/library/tools/inspect-resource/resource.yaml +16 -5
  332. package/library/tools/instantiate-scene/resource.yaml +27 -2
  333. package/library/tools/measure/resource.yaml +96 -0
  334. package/library/tools/place-adjacent/resource.yaml +113 -0
  335. package/library/tools/play/resource.yaml +15 -2
  336. package/library/tools/raycast/resource.yaml +101 -0
  337. package/library/tools/read-file/resource.yaml +36 -2
  338. package/library/tools/read-library/resource.yaml +2 -2
  339. package/library/tools/repeat-along/resource.yaml +129 -0
  340. package/library/tools/replace-node/resource.yaml +11 -5
  341. package/library/tools/scene-audit/resource.yaml +144 -0
  342. package/library/tools/screenshot/resource.yaml +13 -2
  343. package/library/tools/search-assets/resource.yaml +18 -0
  344. package/library/tools/shot-sheet/resource.yaml +110 -0
  345. package/library/tools/snap-to-surface/resource.yaml +4 -1
  346. package/library/tools/world-snapshot/resource.yaml +24 -3
  347. package/library/tools/zoom/resource.yaml +105 -0
  348. package/package.json +1 -1
  349. package/registry/generated/aliases.json +17 -3
  350. package/registry/generated/counts.json +3 -3
  351. package/registry/generated/gemini-extension.json +26 -4
  352. package/registry/generated/index.json +2154 -469
  353. package/registry/generated/marketplace.claude.json +2 -2
  354. package/registry/generated/plugin.claude.json +27 -5
  355. package/registry/generated/plugin.codex.json +28 -6
  356. package/registry/generated/plugin.cursor.json +27 -5
  357. package/registry/generated/plugin.factory.json +27 -5
  358. package/registry/generated/skills-index.md +36 -14
  359. package/registry/generated/skills-registry.json +421 -124
  360. package/registry/generated/templates-registry.json +2 -2
  361. package/library/skills/host-authoritative-state/SKILL.md +0 -332
  362. package/library/skills/host-authoritative-state/resource.yaml +0 -28
  363. package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
  364. package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
  365. package/library/skills/setup-multiplayer/SKILL.md +0 -336
  366. package/library/skills/setup-multiplayer/resource.yaml +0 -28
@@ -0,0 +1,510 @@
1
+ ---
2
+ name: multiplayer-movement
3
+ description: "Client-side movement in Summer multiplayer: owners move instantly, others glide, the authority checks each pose."
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_inspect_node summer_get_script_errors summer_project_setting summer_play summer_stop summer_get_diagnostics
9
+ paths: ["network/**", "client/**", "authority/**"]
10
+ ---
11
+
12
+ # /multiplayer-movement — each player moves their own character
13
+
14
+ Step 2 of the multiplayer sequence. It continues the Courtyard game from
15
+ `multiplayer-project`; read that first if `network/game.gd` does not exist.
16
+ [files.md](files.md) has every file this step changes, complete, as it ran
17
+ under Local Play.
18
+
19
+ ## How it works
20
+
21
+ Summer games use **client-side movement**:
22
+
23
+ - **The owner** runs the whole character controller locally: input, walking,
24
+ gravity, jumping, collision. Its camera follows instantly, at any latency.
25
+ About 20 times a second it **publishes its pose** (position, facing) to an
26
+ owner-written State group.
27
+ - **The authority** runs no movement. It **checks every pose** with an
28
+ acceptance policy: inside the level, no faster than the game allows, still
29
+ the same player. A refused pose snaps the owner back to the last accepted
30
+ one. The authority can also move a player on purpose (respawn, portal).
31
+ - **Everyone else** receives each accepted pose and draws it 100 ms in the
32
+ past, gliding between poses, so remote players move smoothly through
33
+ jitter and lost packets.
34
+
35
+ ```text
36
+ owner client authority other clients
37
+ input → controller ──pose──► acceptance policy ──pose──► glide between poses
38
+ camera follows instantly refuses: owner snaps back (100 ms behind)
39
+ ```
40
+
41
+ Game rules on the authority (pickups, melee, triggers) read the latest
42
+ accepted pose (`multiplayer-state`).
43
+
44
+ The pose is bound to the player's **entity**, which the Spawner creates for
45
+ every joined player. The authority creates the pose group when that entity
46
+ spawns, which is after the player's network is ready and again after a
47
+ reconnect. The group ends with the entity.
48
+
49
+ ## 1. Declare the pose stream
50
+
51
+ Add a `Motion` State stream to `network/composition.tres`, before the
52
+ Command stream's sub-resources:
53
+
54
+ ```ini
55
+ [sub_resource type="SummerNetworkField" id="MotionPosition"]
56
+ field_name = &"position"
57
+ type = 9
58
+
59
+ [sub_resource type="SummerNetworkField" id="MotionYaw"]
60
+ field_name = &"yaw"
61
+ type = 2
62
+
63
+ [sub_resource type="SummerNetworkField" id="MotionEntity"]
64
+ field_name = &"entity"
65
+ type = 1
66
+
67
+ [sub_resource type="SummerNetworkSchema" id="MotionSchema"]
68
+ schema_id = &"courtyard.motion.v1"
69
+ state_fields = Array[SummerNetworkField]([SubResource("MotionEntity"), SubResource("MotionPosition"), SubResource("MotionYaw")])
70
+
71
+ [sub_resource type="SummerWorldAudience" id="Everyone"]
72
+ max_expansion = 8
73
+
74
+ [sub_resource type="SummerNetworkStateStream" id="Motion"]
75
+ stream_id = &"courtyard.motion"
76
+ writer_policy = 1
77
+ skip_writer_echo = true
78
+ state_schema = SubResource("MotionSchema")
79
+ audience_policy = SubResource("Everyone")
80
+ max_audience_count = 8
81
+ max_keys = 8
82
+ max_state_bytes = 1024
83
+ max_total_bytes = 8192
84
+ ```
85
+
86
+ and list it in the `[resource]`:
87
+
88
+ ```ini
89
+ [resource]
90
+ network_version = "1.0.0"
91
+ state_streams = Array[SummerNetworkStateStream]([SubResource("Motion")])
92
+ occurrence_streams = Array[SummerNetworkOccurrenceStream]([SubResource("Commands")])
93
+ ```
94
+
95
+ Update `load_steps` in the first line to the number of sub-resources plus
96
+ one. [files.md](files.md) has the complete file.
97
+
98
+ | Setting | Why |
99
+ |---|---|
100
+ | `writer_policy = 1` | `WRITER_OWNER`: a Session writes it, not the authority |
101
+ | `skip_writer_echo = true` | The owner is not sent its own accepted poses, only refusals and the authority's moves |
102
+ | `audience_policy` World, `max_expansion = 8`, `max_audience_count = 8` | Everyone sees every pose. Set both to at least the player count: 0 reaches nobody |
103
+ | `max_keys = 8` | One key per seat |
104
+ | `max_state_bytes = 1024` | Room for the encoded pose. Too tight and every submission fails as `invalid_state` |
105
+ | fields `entity` (`1`, INT), `position` (`9`, VECTOR3), `yaw` (`2`, FLOAT) | Exactly what others need to draw the player |
106
+
107
+ Field `type` values are `SummerNetworkField.TYPE_*`: BOOL 0, INT 1, FLOAT 2,
108
+ STRING 3, VECTOR2 5, VECTOR3 9, QUATERNION 15, COLOR 19, PACKED_BYTE_ARRAY
109
+ 20. A pose Dictionary must have **exactly** the declared fields with exactly
110
+ those types: no extras, no missing keys, no ints for floats. To send more
111
+ (an animation state, a carried item), add a field to the schema and to every
112
+ pose.
113
+
114
+ ## 2. Shared rules
115
+
116
+ `network/game.gd` gains the stream id, the speed limit and the level
117
+ bounds, after `const QUEUE`:
118
+
119
+ ```gdscript
120
+ const MOTION := &"courtyard.motion"
121
+ ```
122
+
123
+ ```gdscript
124
+ ## The authority's sanity bound on movement, in metres per second. Keep it
125
+ ## above the fastest legitimate speed (walking, jumping, falling).
126
+ const MAX_SPEED := 15.0
127
+ const ARENA_RADIUS := 40.0
128
+ ```
129
+
130
+ and the seat key, the spawn pose and the bounds check, after
131
+ `build_network`:
132
+
133
+ ```gdscript
134
+ ## Pose groups are keyed by seat, so a stream needs at most MAX_PLAYERS keys.
135
+ static func motion_key(seat: int) -> StringName:
136
+ return StringName("p%d" % seat)
137
+
138
+
139
+ ## Seats spawn on a circle. `entity` ties the pose to the player's entity.
140
+ static func spawn_pose(seat: int, entity_id: int) -> Dictionary:
141
+ var angle := TAU * float(seat) / MAX_PLAYERS
142
+ return {"entity": entity_id, "position": Vector3(cos(angle) * 4.0, 1.0, sin(angle) * 4.0), "yaw": 0.0}
143
+
144
+
145
+ static func in_bounds(position: Vector3) -> bool:
146
+ return position.is_finite() and Vector2(position.x, position.z).length() <= ARENA_RADIUS \
147
+ and position.y > -10.0 and position.y < 30.0
148
+ ```
149
+
150
+ `MAX_SPEED` must stay above the fastest legitimate speed, falling included.
151
+ The authority enforces it; the client never reads it.
152
+
153
+ ## 3. The authority checks every pose
154
+
155
+ `authority/main.gd`:
156
+
157
+ ```gdscript
158
+ extends Node
159
+ ## The match server. It runs headless and owns every rule.
160
+
161
+ const Game = preload("res://network/game.gd")
162
+ const PlayerArchetype = preload("res://authority/player_archetype.tres")
163
+ ## How far a player may move in one burst after a network stall, in metres.
164
+ const BURST := Game.MAX_SPEED * 0.5
165
+
166
+ var spawner: SummerNetworkSpawner
167
+ var world: SummerNetworkWorld
168
+ var seat_of := {} # session_id -> seat, held until the Session leaves
169
+ var allowance := {} # seat -> metres the player may still move
170
+ var refilled_at := {} # seat -> msec of the last allowance refill
171
+
172
+
173
+ func _ready() -> void:
174
+ spawner = Game.build_network(self, PlayerArchetype)
175
+ world = $World
176
+ spawner.entity_spawned.connect(_on_player_spawned)
177
+ spawner.command_received.connect(_on_command)
178
+ world.session_left.connect(_on_session_left)
179
+ var initialized: SummerResult = await Summer.initialize(Game.GAME_ID).get_result_or_completed_signal()
180
+ if not initialized.ok:
181
+ push_error(initialized.message)
182
+ return
183
+ if not world.is_ready():
184
+ await world.binding_ready
185
+ if not spawner.is_network_ready():
186
+ await spawner.network_ready
187
+ print("COURTYARD_AUTHORITY_READY")
188
+
189
+
190
+ ## A seat is a player's place in this World for their whole stay, across
191
+ ## reconnects. Returns -1 when the World is full.
192
+ func _seat(session: SummerSession) -> int:
193
+ if seat_of.has(session.session_id):
194
+ return seat_of[session.session_id]
195
+ var taken := seat_of.values()
196
+ for seat in Game.MAX_PLAYERS:
197
+ if seat not in taken:
198
+ seat_of[session.session_id] = seat
199
+ return seat
200
+ return -1
201
+
202
+
203
+ ## The Spawner spawns one player entity per joined player, once that player's
204
+ ## network is ready (and again after a reconnect). Its owner becomes the only
205
+ ## writer of the pose; the group ends with the entity.
206
+ func _on_player_spawned(entity: SummerNetworkEntity) -> void:
207
+ var seat := _seat(entity.session)
208
+ if seat < 0:
209
+ push_warning("no free seat")
210
+ return
211
+ allowance[seat] = BURST
212
+ refilled_at[seat] = Time.get_ticks_msec()
213
+ var group := spawner.create_state_group(Game.MOTION, Game.motion_key(seat), Game.spawn_pose(seat, entity.entity_id), entity.session, entity)
214
+ group.set_acceptance_policy(_accept_motion.bind(seat))
215
+ print("COURTYARD_PLAYER_SPAWNED seat=%d entity=%d" % [seat, entity.entity_id])
216
+
217
+
218
+ ## Runs on every pose an owner submits. True accepts it unchanged; false
219
+ ## refuses it, and the owner snaps back to the last accepted pose.
220
+ func _accept_motion(candidate: Dictionary, current: Dictionary, seat: int) -> bool:
221
+ if candidate.entity != current.entity:
222
+ return false # a player can move, never become someone else
223
+ var position: Vector3 = candidate.position
224
+ if not Game.in_bounds(position) or not is_finite(candidate.yaw):
225
+ return false
226
+ # A distance allowance refilled at MAX_SPEED: bursts after a stall pass,
227
+ # speed hacks and teleports do not.
228
+ var now := Time.get_ticks_msec()
229
+ var budget := minf(float(allowance[seat]) + Game.MAX_SPEED * (now - int(refilled_at[seat])) / 1000.0, BURST)
230
+ refilled_at[seat] = now
231
+ var distance := position.distance_to(current.position)
232
+ if distance > budget:
233
+ allowance[seat] = budget
234
+ return false
235
+ allowance[seat] = budget - distance
236
+ return true
237
+
238
+
239
+ ## Moves a player on purpose (respawn, portal). The owner snaps to it.
240
+ func teleport(seat: int, position: Vector3) -> void:
241
+ var group := spawner.get_state_group(Game.MOTION, Game.motion_key(seat))
242
+ if group != null and group.active:
243
+ var pose := group.get_state()
244
+ pose.position = position
245
+ allowance[seat] = BURST
246
+ group.reset(pose)
247
+
248
+
249
+ func _on_command(request: SummerNetworkCommandRequest) -> void:
250
+ var command := Game.unpack(request.payload)
251
+ match str(command.get("c", "")):
252
+ "join":
253
+ _join(request)
254
+ _:
255
+ request.refuse(&"unknown_command")
256
+
257
+
258
+ ## A client sends "join" once its network is ready.
259
+ func _join(request: SummerNetworkCommandRequest) -> void:
260
+ var seat := _seat(request.get_session())
261
+ if seat < 0:
262
+ request.refuse(&"match_full")
263
+ return
264
+ request.accept({}, Game.pack({"seat": seat}))
265
+ print("COURTYARD_PLAYER_JOINED seat=%d" % seat)
266
+
267
+
268
+ ## The Session ended for good: free the seat. (Its pose group already ended
269
+ ## with the player's entity.)
270
+ func _on_session_left(session: SummerSession, reason: String) -> void:
271
+ var seat: int = seat_of.get(session.session_id, -1)
272
+ seat_of.erase(session.session_id)
273
+ print("COURTYARD_PLAYER_LEFT seat=%d reason=%s" % [seat, reason])
274
+ ```
275
+
276
+ - `create_state_group(stream, key, initial, writer_session, entity)` makes
277
+ that Session the only writer, and ties the group's life to the entity.
278
+ - `set_acceptance_policy` runs synchronously on each pose. Return `true` to
279
+ accept it unchanged and `false` to refuse it. It cannot edit a pose; to
280
+ move a player, call `reset()` as `teleport()` does.
281
+ - The distance allowance refills at `MAX_SPEED` and holds at most half a
282
+ second of movement. Poses bunched together after a lag spike pass;
283
+ teleports and speed hacks don't. A flat per-pose distance check would
284
+ refuse honest players after every hiccup and still let a cheat move fast in
285
+ small steps.
286
+ - A **seat** is the player's place in the World until `session_left`. Pose
287
+ groups are keyed by seat, so a stream needs at most `MAX_PLAYERS` keys.
288
+
289
+ ## 4. The client: own player and everyone else
290
+
291
+ `client/main.gd` pairs each pose group with its entity. Either can arrive
292
+ first. Add a `waiting` dictionary:
293
+
294
+ ```gdscript
295
+ var waiting := {} # entity id -> pose group that arrived before its entity
296
+ ```
297
+
298
+ connect both signals in `_ready()` before joining:
299
+
300
+ ```gdscript
301
+ # Connect before joining, or the first values arrive unseen.
302
+ spawner.state_group_created.connect(_on_group_created)
303
+ spawner.entity_spawned.connect(_on_entity_spawned)
304
+ ```
305
+
306
+ and add the handlers at the end:
307
+
308
+ ```gdscript
309
+ func _on_group_created(group: SummerNetworkStateGroup) -> void:
310
+ if group.get_stream_id() == Game.MOTION:
311
+ _attach_pose(group, int(group.get_state().entity))
312
+
313
+
314
+ ## The pose group and the player's entity can arrive in either order.
315
+ func _attach_pose(group: SummerNetworkStateGroup, entity_id: int) -> void:
316
+ var entity := spawner.get_entity(entity_id)
317
+ if entity == null or entity.root == null:
318
+ waiting[entity_id] = group
319
+ return
320
+ entity.root.bind(group)
321
+
322
+
323
+ func _on_entity_spawned(entity: SummerNetworkEntity) -> void:
324
+ if waiting.has(entity.entity_id):
325
+ _attach_pose(waiting[entity.entity_id], entity.entity_id)
326
+ waiting.erase(entity.entity_id)
327
+ ```
328
+
329
+ The client archetype from `multiplayer-project` already gives each player an
330
+ owner scene and an observer scene. Give them scripts.
331
+
332
+ `client/player_owner.gd`, your own player:
333
+
334
+ ```gdscript
335
+ extends CharacterBody3D
336
+ ## Your own player. You move it locally and publish where it is; the
337
+ ## authority only snaps you somewhere on spawn, teleport or a refused pose.
338
+
339
+ const WALK_SPEED := 6.0
340
+ const JUMP_VELOCITY := 5.0
341
+ const SEND_INTERVAL := 1.0 / 20.0
342
+
343
+ var group: SummerNetworkStateGroup
344
+ var _pose := {} # the full pose; only position and yaw change
345
+ var _sent := {}
346
+ var _send_elapsed := 0.0
347
+
348
+
349
+ func bind(motion: SummerNetworkStateGroup) -> void:
350
+ group = motion
351
+ group.state_installed.connect(_on_installed)
352
+ _on_installed(group.get_state(), group.get_revision(), true)
353
+
354
+
355
+ func _on_installed(state: Dictionary, _revision: int, reset: bool) -> void:
356
+ # Accepted poses are not echoed back to you (skip_writer_echo). A reset is
357
+ # the authority moving you: spawn, teleport, or a pose it refused.
358
+ if not reset or state.is_empty():
359
+ return
360
+ _pose = state.duplicate()
361
+ _sent = state.duplicate()
362
+ global_position = state.position
363
+ rotation.y = state.yaw
364
+ velocity = Vector3.ZERO
365
+
366
+
367
+ func _physics_process(delta: float) -> void:
368
+ if group == null or not group.active:
369
+ return
370
+ if not is_on_floor():
371
+ velocity += get_gravity() * delta
372
+ if Input.is_action_just_pressed(&"ui_accept") and is_on_floor():
373
+ velocity.y = JUMP_VELOCITY
374
+ var input := Input.get_vector(&"ui_left", &"ui_right", &"ui_up", &"ui_down")
375
+ velocity.x = input.x * WALK_SPEED
376
+ velocity.z = input.y * WALK_SPEED
377
+ if input != Vector2.ZERO:
378
+ rotation.y = atan2(-input.x, -input.y)
379
+ move_and_slide()
380
+ _publish(delta)
381
+
382
+
383
+ ## Publishes the pose 20 times a second, and only when it changed.
384
+ func _publish(delta: float) -> void:
385
+ _send_elapsed += delta
386
+ if _send_elapsed < SEND_INTERVAL:
387
+ return
388
+ _send_elapsed = 0.0
389
+ _pose.position = global_position
390
+ _pose.yaw = rotation.y
391
+ if _pose == _sent:
392
+ return
393
+ group.submit(_pose)
394
+ _sent = _pose.duplicate()
395
+ ```
396
+
397
+ Attach it to `client/player_owner.tscn` (`script = ExtResource(...)` on the
398
+ root, as in [files.md](files.md)).
399
+
400
+ `client/player_observer.gd`, everyone else:
401
+
402
+ ```gdscript
403
+ extends Node3D
404
+ ## Someone else's player. It glides between the poses its owner published,
405
+ ## drawn VIEW_DELAY in the past so there is always a newer pose to glide to.
406
+
407
+ const VIEW_DELAY := 0.1
408
+
409
+ var group: SummerNetworkStateGroup
410
+ var _samples: Array = [] # [time, position, yaw] per received pose
411
+ var _offsets: Array = [] # recent arrival minus authority acceptance, seconds
412
+
413
+
414
+ func bind(motion: SummerNetworkStateGroup) -> void:
415
+ group = motion
416
+ group.state_installed.connect(_on_installed)
417
+ _on_installed(group.get_state(), group.get_revision(), true)
418
+
419
+
420
+ func _on_installed(state: Dictionary, _revision: int, reset: bool) -> void:
421
+ if state.is_empty():
422
+ return
423
+ var now := Time.get_ticks_usec() / 1000000.0
424
+ if reset:
425
+ _samples.clear() # a spawn or teleport is a cut, not a slide
426
+ global_position = state.position
427
+ rotation.y = state.yaw
428
+ _samples.append([_pose_time(now), state.position, state.yaw])
429
+ if _samples.size() > 20:
430
+ _samples.pop_front()
431
+
432
+
433
+ func _process(_delta: float) -> void:
434
+ if _samples.is_empty():
435
+ return
436
+ var at := Time.get_ticks_usec() / 1000000.0 - VIEW_DELAY
437
+ var newest: Array = _samples[-1]
438
+ if at >= newest[0] or _samples.size() == 1:
439
+ global_position = newest[1]
440
+ rotation.y = newest[2]
441
+ return
442
+ for i in range(_samples.size() - 1, 0, -1):
443
+ var a: Array = _samples[i - 1]
444
+ var b: Array = _samples[i]
445
+ if at >= a[0]:
446
+ var t := clampf((at - a[0]) / maxf(b[0] - a[0], 0.0001), 0.0, 1.0)
447
+ global_position = (a[1] as Vector3).lerp(b[1], t)
448
+ rotation.y = lerp_angle(a[2], b[2], t)
449
+ return
450
+ global_position = _samples[0][1]
451
+
452
+
453
+ ## Places a pose on the local timeline by when the authority accepted it, so
454
+ ## poses that arrive bunched together still play back evenly.
455
+ func _pose_time(now: float) -> float:
456
+ var timing := group.get_timing()
457
+ if timing == null or timing.state_authority_usec < 0:
458
+ return now
459
+ var accepted := timing.state_authority_usec / 1000000.0
460
+ _offsets.append(now - accepted)
461
+ if _offsets.size() > 40:
462
+ _offsets.pop_front()
463
+ return minf(accepted + float(_offsets.min()), now)
464
+ ```
465
+
466
+ Attach it to `client/player_observer.tscn` the same way.
467
+
468
+ - The owner ignores ordinary installs: with `skip_writer_echo` it is not sent
469
+ its own accepted poses. A `reset` install is the authority moving it:
470
+ spawn, `teleport()`, or a refused pose.
471
+ - The owner publishes only changed poses, 20 times a second. The engine
472
+ keeps only the newest pending pose per player, so a faster rate adds
473
+ traffic, not smoothness.
474
+ - The observer orders poses by when the authority accepted them
475
+ (`get_timing().state_authority_usec`), so a burst of late packets still
476
+ plays back evenly. A `reset` is a cut, not a slide.
477
+ - Observer scenes have no collision. Other players are drawings on your
478
+ screen; their owners decide where they are.
479
+
480
+ ## 5. Test it under latency
481
+
482
+ Run Local Play with 2 players at 100 ms round trip, 10 ms jitter and 1 %
483
+ loss (`multiplayer-testing` shows how):
484
+
485
+ - [ ] Your own player responds instantly and never snaps back while moving normally.
486
+ - [ ] The other window's copy of your player moves smoothly and stops exactly where you stopped.
487
+ - [ ] Jumping and falling look the same on both screens.
488
+ - [ ] No `SCRIPT ERROR`, no `cannot attach`, and every process has `"script_errors":0`.
489
+
490
+ To check the anti-cheat, move the owner by 30 m in one frame from a test
491
+ script. The authority refuses the pose, the owner snaps back, and the other
492
+ client never sees the jump.
493
+
494
+ ## Common mistakes
495
+
496
+ | Don't | Do | Why |
497
+ |---|---|---|
498
+ | Move other players' characters locally | Draw others from their published poses | Each player's client is the only source of their position |
499
+ | Create the pose group on `session_joined` | Create it in `entity_spawned` | At `session_joined` the player's network is not ready yet, and creation fails |
500
+ | Trust poses unchecked | An acceptance policy with a refilling distance allowance | Otherwise any client can teleport |
501
+ | A flat "max distance per pose" check | An allowance refilled at `MAX_SPEED` | Bunched poses after lag get refused, and cheats still move fast |
502
+ | Snap observers to each pose as it arrives | Glide 100 ms behind, ordered by acceptance time | Poses arrive unevenly; snapping looks like stutter |
503
+ | Add a key to the pose Dictionary without the schema | Add the field to the schema too | Undeclared or missing fields fail the submission |
504
+ | `max_state_bytes = 256` | 1024 or more | The bound covers the encoded pose; too tight fails as `invalid_state` |
505
+ | Collide with observer bodies | Leave observer scenes without collision | Another player's body is only a drawing on your screen |
506
+
507
+ ## Next
508
+
509
+ `multiplayer-state`: scores, doors, private inventories, and actions the
510
+ authority checks against these poses.