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.
Files changed (413) 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 +120 -0
  8. package/README.md +22 -7
  9. package/assets/audit/scene_audit.gd +4136 -0
  10. package/assets/seeing/seeing_probe.gd +1873 -0
  11. package/dist/cli/commands/setup.js +6 -19
  12. package/dist/cli/commands/skills.js +57 -32
  13. package/dist/core/api-client.d.ts +9 -1
  14. package/dist/core/api-client.js +21 -4
  15. package/dist/core/capabilities/agent-playbook.js +46 -0
  16. package/dist/core/capabilities/audit/accept.d.ts +55 -0
  17. package/dist/core/capabilities/audit/accept.js +147 -0
  18. package/dist/core/capabilities/audit/args.d.ts +95 -0
  19. package/dist/core/capabilities/audit/args.js +89 -0
  20. package/dist/core/capabilities/audit/audit.d.ts +56 -0
  21. package/dist/core/capabilities/audit/audit.js +287 -0
  22. package/dist/core/capabilities/audit/frame.d.ts +14 -0
  23. package/dist/core/capabilities/audit/frame.js +35 -0
  24. package/dist/core/capabilities/audit/gaps.d.ts +96 -0
  25. package/dist/core/capabilities/audit/gaps.js +257 -0
  26. package/dist/core/capabilities/audit/judge.d.ts +162 -0
  27. package/dist/core/capabilities/audit/judge.js +1134 -0
  28. package/dist/core/capabilities/audit/math.d.ts +110 -0
  29. package/dist/core/capabilities/audit/math.js +263 -0
  30. package/dist/core/capabilities/audit/summary.d.ts +74 -0
  31. package/dist/core/capabilities/audit/summary.js +212 -0
  32. package/dist/core/capabilities/board.d.ts +31 -0
  33. package/dist/core/capabilities/board.js +43 -0
  34. package/dist/core/capabilities/camera-view.d.ts +4 -2
  35. package/dist/core/capabilities/camera-view.js +6 -3
  36. package/dist/core/capabilities/connect-signal.d.ts +85 -0
  37. package/dist/core/capabilities/connect-signal.js +285 -0
  38. package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
  39. package/dist/core/capabilities/engine-fallbacks.js +5 -0
  40. package/dist/core/capabilities/engine-ops.d.ts +5 -1
  41. package/dist/core/capabilities/engine-ops.js +0 -3
  42. package/dist/core/capabilities/engine-receipt.d.ts +12 -0
  43. package/dist/core/capabilities/engine-receipt.js +26 -0
  44. package/dist/core/capabilities/file-read.d.ts +64 -0
  45. package/dist/core/capabilities/file-read.js +325 -0
  46. package/dist/core/capabilities/game-task-plan.js +4 -2
  47. package/dist/core/capabilities/grep.d.ts +60 -0
  48. package/dist/core/capabilities/grep.js +190 -0
  49. package/dist/core/capabilities/inspect-node.d.ts +56 -0
  50. package/dist/core/capabilities/inspect-node.js +225 -0
  51. package/dist/core/capabilities/inspect-resource.d.ts +61 -0
  52. package/dist/core/capabilities/inspect-resource.js +376 -0
  53. package/dist/core/capabilities/math3d.d.ts +28 -0
  54. package/dist/core/capabilities/math3d.js +88 -0
  55. package/dist/core/capabilities/motion-generation.d.ts +62 -0
  56. package/dist/core/capabilities/motion-generation.js +138 -0
  57. package/dist/core/capabilities/placement-batch.d.ts +102 -0
  58. package/dist/core/capabilities/placement-batch.js +483 -0
  59. package/dist/core/capabilities/placement-math.d.ts +70 -0
  60. package/dist/core/capabilities/placement-math.js +209 -0
  61. package/dist/core/capabilities/placement-script.d.ts +42 -0
  62. package/dist/core/capabilities/placement-script.js +1096 -0
  63. package/dist/core/capabilities/placement.d.ts +485 -0
  64. package/dist/core/capabilities/placement.js +1731 -0
  65. package/dist/core/capabilities/project-context.d.ts +33 -2
  66. package/dist/core/capabilities/project-context.js +87 -4
  67. package/dist/core/capabilities/replace-node.d.ts +91 -0
  68. package/dist/core/capabilities/replace-node.js +423 -0
  69. package/dist/core/capabilities/runtime-control.d.ts +20 -4
  70. package/dist/core/capabilities/runtime-control.js +67 -2
  71. package/dist/core/capabilities/scene-batch.d.ts +63 -0
  72. package/dist/core/capabilities/scene-batch.js +394 -0
  73. package/dist/core/capabilities/scene-readback.d.ts +36 -0
  74. package/dist/core/capabilities/scene-readback.js +54 -0
  75. package/dist/core/capabilities/seeing/args.d.ts +348 -0
  76. package/dist/core/capabilities/seeing/args.js +178 -0
  77. package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
  78. package/dist/core/capabilities/seeing/candidates.js +282 -0
  79. package/dist/core/capabilities/seeing/math.d.ts +134 -0
  80. package/dist/core/capabilities/seeing/math.js +269 -0
  81. package/dist/core/capabilities/seeing/probe.d.ts +62 -0
  82. package/dist/core/capabilities/seeing/probe.js +188 -0
  83. package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
  84. package/dist/core/capabilities/seeing/scoring.js +639 -0
  85. package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
  86. package/dist/core/capabilities/seeing/seeing.js +1341 -0
  87. package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
  88. package/dist/core/capabilities/seeing/shot-store.js +204 -0
  89. package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
  90. package/dist/core/capabilities/surface-snap-mesh.js +425 -0
  91. package/dist/core/capabilities/surface-snap.d.ts +77 -0
  92. package/dist/core/capabilities/surface-snap.js +461 -0
  93. package/dist/core/capabilities/tool-dispatch.js +231 -72
  94. package/dist/core/capabilities/tscn.d.ts +85 -0
  95. package/dist/core/capabilities/tscn.js +391 -0
  96. package/dist/core/capabilities/world-snapshot.d.ts +58 -0
  97. package/dist/core/capabilities/world-snapshot.js +150 -0
  98. package/dist/core/capability-skew.d.ts +5 -5
  99. package/dist/core/capability-skew.js +14 -6
  100. package/dist/core/headless/worker-engine-client.d.ts +1 -1
  101. package/dist/core/headless/worker-engine-client.js +2 -2
  102. package/dist/core/library-read.d.ts +37 -5
  103. package/dist/core/library-read.js +264 -33
  104. package/dist/core/library-search.d.ts +2 -2
  105. package/dist/core/skills-registry.d.ts +9 -1
  106. package/dist/core/skills-registry.js +25 -2
  107. package/dist/installer/agent-config.d.ts +9 -4
  108. package/dist/installer/agent-config.js +194 -392
  109. package/dist/installer/agent-table.d.ts +119 -0
  110. package/dist/installer/agent-table.js +619 -0
  111. package/dist/installer/setup.js +4 -2
  112. package/dist/installer/skill-locations.d.ts +8 -17
  113. package/dist/installer/skill-locations.js +22 -101
  114. package/dist/installer/version-check.d.ts +5 -1
  115. package/dist/installer/version-check.js +38 -28
  116. package/dist/mcp/server.js +9 -3
  117. package/dist/mcp/tools/asset-tools.js +21 -3
  118. package/dist/mcp/tools/audit-tools.d.ts +23 -0
  119. package/dist/mcp/tools/audit-tools.js +60 -0
  120. package/dist/mcp/tools/debug-tools.js +3 -1
  121. package/dist/mcp/tools/file-tools.js +17 -4
  122. package/dist/mcp/tools/generate-tools.js +74 -66
  123. package/dist/mcp/tools/library-tools.js +2 -0
  124. package/dist/mcp/tools/perception-tools.js +6 -19
  125. package/dist/mcp/tools/placement-tools.d.ts +24 -0
  126. package/dist/mcp/tools/placement-tools.js +91 -0
  127. package/dist/mcp/tools/project-tools.js +18 -15
  128. package/dist/mcp/tools/scene-tools.js +71 -83
  129. package/dist/mcp/tools/seeing-tools.d.ts +24 -0
  130. package/dist/mcp/tools/seeing-tools.js +64 -0
  131. package/dist/mcp/tools/spatial-tools.js +17 -9
  132. package/dist/mcp/tools/visual-tools.js +59 -7
  133. package/gemini-extension.json +26 -4
  134. package/integrations/README.md +48 -19
  135. package/integrations/amp/README.md +12 -0
  136. package/integrations/amp/manifest-target.json +3 -0
  137. package/integrations/antigravity/README.md +12 -0
  138. package/integrations/antigravity/manifest-target.json +3 -0
  139. package/integrations/claude-desktop/README.md +12 -0
  140. package/integrations/claude-desktop/manifest-target.json +3 -0
  141. package/integrations/cline/README.md +8 -9
  142. package/integrations/cline-cli/README.md +12 -0
  143. package/integrations/cline-cli/manifest-target.json +3 -0
  144. package/integrations/copilot-jetbrains/README.md +12 -0
  145. package/integrations/copilot-jetbrains/manifest-target.json +3 -0
  146. package/integrations/crush/README.md +12 -0
  147. package/integrations/crush/manifest-target.json +3 -0
  148. package/integrations/github-copilot/README.md +8 -8
  149. package/integrations/goose/README.md +12 -0
  150. package/integrations/goose/manifest-target.json +3 -0
  151. package/integrations/grok-build/README.md +12 -0
  152. package/integrations/grok-build/manifest-target.json +3 -0
  153. package/integrations/hermes/README.md +12 -0
  154. package/integrations/hermes/manifest-target.json +3 -0
  155. package/integrations/junie/README.md +12 -0
  156. package/integrations/junie/manifest-target.json +3 -0
  157. package/integrations/kilo-code/README.md +6 -10
  158. package/integrations/kimi-code/README.md +12 -0
  159. package/integrations/kimi-code/manifest-target.json +3 -0
  160. package/integrations/kiro/README.md +12 -0
  161. package/integrations/kiro/manifest-target.json +3 -0
  162. package/integrations/lm-studio/README.md +8 -8
  163. package/integrations/mistral-vibe/README.md +12 -0
  164. package/integrations/mistral-vibe/manifest-target.json +3 -0
  165. package/integrations/opencode/README.md +9 -12
  166. package/integrations/qoder/README.md +12 -0
  167. package/integrations/qoder/manifest-target.json +3 -0
  168. package/integrations/qwen-code/README.md +12 -0
  169. package/integrations/qwen-code/manifest-target.json +3 -0
  170. package/integrations/roo-code/README.md +8 -7
  171. package/integrations/rovo-dev/README.md +12 -0
  172. package/integrations/rovo-dev/manifest-target.json +3 -0
  173. package/integrations/trae/README.md +12 -0
  174. package/integrations/trae/manifest-target.json +3 -0
  175. package/integrations/visual-studio/README.md +12 -0
  176. package/integrations/visual-studio/manifest-target.json +3 -0
  177. package/integrations/vscode-copilot/README.md +9 -9
  178. package/integrations/warp/README.md +12 -0
  179. package/integrations/warp/manifest-target.json +3 -0
  180. package/integrations/windsurf/README.md +8 -9
  181. package/integrations/zed/README.md +12 -0
  182. package/integrations/zed/manifest-target.json +3 -0
  183. package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
  184. package/library/references/mcp-tools-reference/resource.yaml +1 -1
  185. package/library/references/summer-folder/resource.yaml +1 -1
  186. package/library/references/summer-folder/summer-folder.md +1 -1
  187. package/library/skills/3d-lighting/SKILL.md +1 -1
  188. package/library/skills/README.md +264 -0
  189. package/library/skills/adaptive-music/SKILL.md +1 -1
  190. package/library/skills/agent-playtesting/SKILL.md +1 -1
  191. package/library/skills/ai-characters-in-games/SKILL.md +220 -0
  192. package/library/skills/ai-characters-in-games/resource.yaml +30 -0
  193. package/library/skills/ambient-bed/SKILL.md +1 -1
  194. package/library/skills/animated-loop/SKILL.md +1 -1
  195. package/library/skills/animation-tree/SKILL.md +1 -1
  196. package/library/skills/art-direction/SKILL.md +1 -1
  197. package/library/skills/asset-strategy/SKILL.md +2 -1
  198. package/library/skills/asset-strategy/resource.yaml +2 -1
  199. package/library/skills/audio-direction/SKILL.md +1 -1
  200. package/library/skills/auto-fire-targeting/SKILL.md +1 -1
  201. package/library/skills/brainstorm-game/SKILL.md +1 -1
  202. package/library/skills/brainstorming/SKILL.md +1 -1
  203. package/library/skills/browse-templates/SKILL.md +3 -3
  204. package/library/skills/browse-templates/resource.yaml +1 -1
  205. package/library/skills/camera-rigs/SKILL.md +128 -0
  206. package/library/skills/camera-rigs/resource.yaml +29 -0
  207. package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
  208. package/library/skills/character-animation-wiring/SKILL.md +1 -1
  209. package/library/skills/character-model/SKILL.md +1 -1
  210. package/library/skills/character-movement/SKILL.md +326 -0
  211. package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
  212. package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
  213. package/library/skills/character-movement/references/movement-3d.md +136 -0
  214. package/library/skills/character-movement/references/platformer-feel.md +83 -0
  215. package/library/skills/character-movement/resource.yaml +40 -0
  216. package/library/skills/character-portrait/SKILL.md +1 -1
  217. package/library/skills/cinematic-cutscene/SKILL.md +1 -1
  218. package/library/skills/combat-basics/SKILL.md +159 -0
  219. package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
  220. package/library/skills/combat-basics/resource.yaml +34 -0
  221. package/library/skills/concept-art/SKILL.md +1 -1
  222. package/library/skills/create-asset-sheet/SKILL.md +1 -1
  223. package/library/skills/debug/SKILL.md +1 -1
  224. package/library/skills/debugging-game-feel/SKILL.md +1 -1
  225. package/library/skills/design-level/SKILL.md +1 -1
  226. package/library/skills/design-mechanic/SKILL.md +1 -1
  227. package/library/skills/design-npc/SKILL.md +1 -1
  228. package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
  229. package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
  230. package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
  231. package/library/skills/environment-kit/SKILL.md +1 -1
  232. package/library/skills/export-and-ship/SKILL.md +1 -1
  233. package/library/skills/fabricating-assets/SKILL.md +1 -1
  234. package/library/skills/facial-and-lipsync/SKILL.md +1 -1
  235. package/library/skills/fps-controller/SKILL.md +9 -7
  236. package/library/skills/fps-controller/resource.yaml +4 -1
  237. package/library/skills/game-feel/SKILL.md +1 -1
  238. package/library/skills/gameskill/SKILL.md +1 -1
  239. package/library/skills/gdscript-patterns/SKILL.md +1 -1
  240. package/library/skills/generate-motion/SKILL.md +9 -9
  241. package/library/skills/generate-motion/resource.yaml +2 -1
  242. package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
  243. package/library/skills/headless-scripting/SKILL.md +1 -1
  244. package/library/skills/in-game-purchases/SKILL.md +254 -0
  245. package/library/skills/in-game-purchases/resource.yaml +29 -0
  246. package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
  247. package/library/skills/investigating-bugs/SKILL.md +1 -1
  248. package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
  249. package/library/skills/make-game/SKILL.md +25 -3
  250. package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
  251. package/library/skills/make-game/references/2d-platformer.md +117 -0
  252. package/library/skills/make-game/references/2d-rpg-content.md +234 -0
  253. package/library/skills/make-game/references/2d-rpg.md +241 -0
  254. package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
  255. package/library/skills/make-game/references/2d-survivors.md +237 -0
  256. package/library/skills/make-game/references/3d-fps-systems.md +216 -0
  257. package/library/skills/make-game/references/3d-fps.md +177 -0
  258. package/library/skills/make-game/references/3d-horror-systems.md +186 -0
  259. package/library/skills/make-game/references/3d-horror.md +201 -0
  260. package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
  261. package/library/skills/make-game/references/3d-third-person.md +215 -0
  262. package/library/skills/make-game/resource.yaml +6 -1
  263. package/library/skills/multiplayer/SKILL.md +150 -0
  264. package/library/skills/multiplayer/resource.yaml +33 -0
  265. package/library/skills/multiplayer-movement/SKILL.md +510 -0
  266. package/library/skills/multiplayer-movement/files.md +501 -0
  267. package/library/skills/multiplayer-movement/resource.yaml +30 -0
  268. package/library/skills/multiplayer-project/SKILL.md +521 -0
  269. package/library/skills/multiplayer-project/files.md +448 -0
  270. package/library/skills/multiplayer-project/resource.yaml +36 -0
  271. package/library/skills/multiplayer-publish/SKILL.md +229 -0
  272. package/library/skills/multiplayer-publish/export-errors.md +89 -0
  273. package/library/skills/multiplayer-publish/resource.yaml +28 -0
  274. package/library/skills/multiplayer-state/SKILL.md +438 -0
  275. package/library/skills/multiplayer-state/files.md +592 -0
  276. package/library/skills/multiplayer-state/resource.yaml +35 -0
  277. package/library/skills/multiplayer-testing/SKILL.md +236 -0
  278. package/library/skills/multiplayer-testing/resource.yaml +39 -0
  279. package/library/skills/music-track/SKILL.md +1 -1
  280. package/library/skills/navigate-summer/SKILL.md +1 -1
  281. package/library/skills/new-project/SKILL.md +1 -1
  282. package/library/skills/organic-model/SKILL.md +1 -1
  283. package/library/skills/pixel-art/SKILL.md +1 -1
  284. package/library/skills/play/SKILL.md +1 -1
  285. package/library/skills/playtesting-a-feature/SKILL.md +1 -1
  286. package/library/skills/procedural-animation/SKILL.md +2 -2
  287. package/library/skills/procedural-animation/resource.yaml +1 -1
  288. package/library/skills/prop-model/SKILL.md +1 -1
  289. package/library/skills/psx-retro-rendering/SKILL.md +1 -1
  290. package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
  291. package/library/skills/remote-deploy/SKILL.md +1 -1
  292. package/library/skills/retarget/SKILL.md +1 -1
  293. package/library/skills/running-in-the-cloud/SKILL.md +1 -1
  294. package/library/skills/save-load/SKILL.md +156 -0
  295. package/library/skills/save-load/resource.yaml +27 -0
  296. package/library/skills/scene-composition/SKILL.md +1 -1
  297. package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
  298. package/library/skills/scene-scripting/SKILL.md +1 -1
  299. package/library/skills/scene-to-level/SKILL.md +1 -1
  300. package/library/skills/skill-create/SKILL.md +1 -1
  301. package/library/skills/skill-improve/SKILL.md +1 -1
  302. package/library/skills/skill-test/SKILL.md +1 -1
  303. package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
  304. package/library/skills/skybox-panorama/SKILL.md +1 -1
  305. package/library/skills/sound-effect/SKILL.md +1 -1
  306. package/library/skills/spatial-placement/SKILL.md +134 -3
  307. package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
  308. package/library/skills/spatial-placement/resource.yaml +16 -2
  309. package/library/skills/sprite-sheet/SKILL.md +1 -1
  310. package/library/skills/summer-analytics/SKILL.md +147 -0
  311. package/library/skills/summer-analytics/resource.yaml +28 -0
  312. package/library/skills/summer-friends/SKILL.md +226 -0
  313. package/library/skills/summer-friends/resource.yaml +28 -0
  314. package/library/skills/summer-leaderboards/SKILL.md +231 -0
  315. package/library/skills/summer-leaderboards/resource.yaml +30 -0
  316. package/library/skills/summer-match-results/SKILL.md +286 -0
  317. package/library/skills/summer-match-results/resource.yaml +32 -0
  318. package/library/skills/summer-matchmaking/SKILL.md +301 -0
  319. package/library/skills/summer-matchmaking/resource.yaml +32 -0
  320. package/library/skills/summer-parties/SKILL.md +253 -0
  321. package/library/skills/summer-parties/resource.yaml +30 -0
  322. package/library/skills/summer-player-data/SKILL.md +322 -0
  323. package/library/skills/summer-player-data/resource.yaml +30 -0
  324. package/library/skills/summer-store/SKILL.md +271 -0
  325. package/library/skills/summer-store/resource.yaml +29 -0
  326. package/library/skills/summer-world-chat/SKILL.md +195 -0
  327. package/library/skills/summer-world-chat/resource.yaml +30 -0
  328. package/library/skills/summer-world-saves/SKILL.md +284 -0
  329. package/library/skills/summer-world-saves/resource.yaml +29 -0
  330. package/library/skills/text-to-motion/SKILL.md +99 -0
  331. package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
  332. package/library/skills/text-to-motion/resource.yaml +41 -0
  333. package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
  334. package/library/skills/tileable-texture/SKILL.md +1 -1
  335. package/library/skills/trailer-shot/SKILL.md +1 -1
  336. package/library/skills/tune-performance/SKILL.md +1 -1
  337. package/library/skills/ui-basics/SKILL.md +10 -1
  338. package/library/skills/ui-graphics/SKILL.md +1 -1
  339. package/library/skills/use-widget-asset/SKILL.md +1 -1
  340. package/library/skills/using-summer/SKILL.md +2 -2
  341. package/library/skills/using-summer/resource.yaml +2 -2
  342. package/library/skills/vehicle-model/SKILL.md +1 -1
  343. package/library/skills/verification-before-completion/SKILL.md +1 -1
  344. package/library/skills/verifying-scenes/SKILL.md +76 -2
  345. package/library/skills/verifying-scenes/resource.yaml +13 -2
  346. package/library/skills/vfx-dissolve/SKILL.md +1 -1
  347. package/library/skills/vfx-fire/SKILL.md +1 -1
  348. package/library/skills/vfx-hit-spark/SKILL.md +1 -1
  349. package/library/skills/vfx-lightning/SKILL.md +1 -1
  350. package/library/skills/vfx-magic-glow/SKILL.md +1 -1
  351. package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
  352. package/library/skills/vfx-smoke/SKILL.md +1 -1
  353. package/library/skills/vfx-water-ripple/SKILL.md +1 -1
  354. package/library/skills/voice-line/SKILL.md +1 -1
  355. package/library/skills/web-and-touch-input/SKILL.md +38 -0
  356. package/library/skills/web-and-touch-input/resource.yaml +28 -0
  357. package/library/skills/web-compatibility-look/SKILL.md +62 -0
  358. package/library/skills/web-compatibility-look/resource.yaml +30 -0
  359. package/library/skills/world-building-3d/SKILL.md +8 -1
  360. package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
  361. package/library/skills/world-building-3d/resource.yaml +2 -1
  362. package/library/skills/writing-plans/SKILL.md +1 -1
  363. package/library/skills/writing-skills/SKILL.md +1 -1
  364. package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
  365. package/library/tools/attach-to-surface/resource.yaml +142 -0
  366. package/library/tools/batch/resource.yaml +9 -3
  367. package/library/tools/connect-ports/resource.yaml +120 -0
  368. package/library/tools/connect-signal/resource.yaml +3 -2
  369. package/library/tools/debug-views/resource.yaml +94 -0
  370. package/library/tools/frame-nodes/resource.yaml +104 -0
  371. package/library/tools/frame-shot/resource.yaml +134 -0
  372. package/library/tools/generate-motion/resource.yaml +44 -9
  373. package/library/tools/get-board/resource.yaml +50 -0
  374. package/library/tools/get-project-context/resource.yaml +13 -2
  375. package/library/tools/grep/resource.yaml +77 -0
  376. package/library/tools/inspect-asset/resource.yaml +71 -0
  377. package/library/tools/inspect-node/resource.yaml +9 -2
  378. package/library/tools/inspect-resource/resource.yaml +16 -5
  379. package/library/tools/instantiate-scene/resource.yaml +27 -2
  380. package/library/tools/measure/resource.yaml +96 -0
  381. package/library/tools/place-adjacent/resource.yaml +113 -0
  382. package/library/tools/play/resource.yaml +15 -2
  383. package/library/tools/raycast/resource.yaml +101 -0
  384. package/library/tools/read-file/resource.yaml +36 -2
  385. package/library/tools/read-library/resource.yaml +2 -2
  386. package/library/tools/repeat-along/resource.yaml +129 -0
  387. package/library/tools/replace-node/resource.yaml +11 -5
  388. package/library/tools/scene-audit/resource.yaml +144 -0
  389. package/library/tools/screenshot/resource.yaml +13 -2
  390. package/library/tools/search-assets/resource.yaml +18 -0
  391. package/library/tools/shot-sheet/resource.yaml +110 -0
  392. package/library/tools/snap-to-surface/resource.yaml +4 -1
  393. package/library/tools/world-snapshot/resource.yaml +24 -3
  394. package/library/tools/zoom/resource.yaml +105 -0
  395. package/package.json +3 -3
  396. package/registry/generated/aliases.json +17 -3
  397. package/registry/generated/counts.json +3 -3
  398. package/registry/generated/gemini-extension.json +26 -4
  399. package/registry/generated/index.json +2154 -469
  400. package/registry/generated/marketplace.claude.json +2 -2
  401. package/registry/generated/plugin.claude.json +27 -5
  402. package/registry/generated/plugin.codex.json +28 -6
  403. package/registry/generated/plugin.cursor.json +27 -5
  404. package/registry/generated/plugin.factory.json +27 -5
  405. package/registry/generated/skills-index.md +264 -0
  406. package/registry/generated/skills-registry.json +901 -206
  407. package/registry/generated/templates-registry.json +2 -2
  408. package/library/skills/host-authoritative-state/SKILL.md +0 -332
  409. package/library/skills/host-authoritative-state/resource.yaml +0 -28
  410. package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
  411. package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
  412. package/library/skills/setup-multiplayer/SKILL.md +0 -336
  413. package/library/skills/setup-multiplayer/resource.yaml +0 -28
@@ -0,0 +1,592 @@
1
+ # multiplayer-state: complete files after this step
2
+
3
+ Every file below ran as shown under Local Play. Compare against your project, or copy a file whole.
4
+
5
+ ## `authority/main.gd`
6
+
7
+ ```gdscript
8
+ extends Node
9
+ ## The match server. It runs headless and owns every rule.
10
+
11
+ const Game = preload("res://network/game.gd")
12
+ const PlayerArchetype = preload("res://authority/player_archetype.tres")
13
+ ## How far a player may move in one burst after a network stall, in metres.
14
+ const BURST := Game.MAX_SPEED * 0.5
15
+ const REACH := 2.0 # how close a player must stand to collect a coin
16
+
17
+ var spawner: SummerNetworkSpawner
18
+ var world: SummerNetworkWorld
19
+ var seat_of := {} # session_id -> seat, held until the Session leaves
20
+ var allowance := {} # seat -> metres the player may still move
21
+ var refilled_at := {} # seat -> msec of the last allowance refill
22
+
23
+ var match_group: SummerNetworkStateGroup
24
+ var match_doc := {"coins": {"0": [10.0, 0.5, 0.0], "1": [-10.0, 0.5, 0.0], "2": [0.0, 0.5, 10.0]}, "scores": {}}
25
+ var mine_groups := {} # session_id -> private group
26
+ var wallets := {} # session_id -> coins this player holds
27
+ var receipts := {} # session_id -> {request id: result}
28
+
29
+
30
+ func _ready() -> void:
31
+ spawner = Game.build_network(self, PlayerArchetype)
32
+ world = $World
33
+ spawner.entity_spawned.connect(_on_player_spawned)
34
+ spawner.command_received.connect(_on_command)
35
+ world.session_left.connect(_on_session_left)
36
+ var initialized: SummerResult = await Summer.initialize(Game.GAME_ID).get_result_or_completed_signal()
37
+ if not initialized.ok:
38
+ push_error(initialized.message)
39
+ return
40
+ if not world.is_ready():
41
+ await world.binding_ready
42
+ if not spawner.is_network_ready():
43
+ await spawner.network_ready
44
+ match_group = spawner.create_state_group(Game.MATCH, &"match", Game.pack(match_doc))
45
+ print("COURTYARD_AUTHORITY_READY")
46
+
47
+
48
+ ## A seat is a player's place in this World for their whole stay, across
49
+ ## reconnects. Returns -1 when the World is full.
50
+ func _seat(session: SummerSession) -> int:
51
+ if seat_of.has(session.session_id):
52
+ return seat_of[session.session_id]
53
+ var taken := seat_of.values()
54
+ for seat in Game.MAX_PLAYERS:
55
+ if seat not in taken:
56
+ seat_of[session.session_id] = seat
57
+ return seat
58
+ return -1
59
+
60
+
61
+ ## The Spawner spawns one player entity per joined player, once that player's
62
+ ## network is ready (and again after a reconnect). Its owner becomes the only
63
+ ## writer of the pose; the group ends with the entity.
64
+ func _on_player_spawned(entity: SummerNetworkEntity) -> void:
65
+ var seat := _seat(entity.session)
66
+ if seat < 0:
67
+ push_warning("no free seat")
68
+ return
69
+ allowance[seat] = BURST
70
+ refilled_at[seat] = Time.get_ticks_msec()
71
+ var group := spawner.create_state_group(Game.MOTION, Game.motion_key(seat), Game.spawn_pose(seat, entity.entity_id), entity.session, entity)
72
+ group.set_acceptance_policy(_accept_motion.bind(seat))
73
+ print("COURTYARD_PLAYER_SPAWNED seat=%d entity=%d" % [seat, entity.entity_id])
74
+
75
+
76
+ ## Runs on every pose an owner submits. True accepts it unchanged; false
77
+ ## refuses it, and the owner snaps back to the last accepted pose.
78
+ func _accept_motion(candidate: Dictionary, current: Dictionary, seat: int) -> bool:
79
+ if candidate.entity != current.entity:
80
+ return false # a player can move, never become someone else
81
+ var position: Vector3 = candidate.position
82
+ if not Game.in_bounds(position) or not is_finite(candidate.yaw):
83
+ return false
84
+ # A distance allowance refilled at MAX_SPEED: bursts after a stall pass,
85
+ # speed hacks and teleports do not.
86
+ var now := Time.get_ticks_msec()
87
+ var budget := minf(float(allowance[seat]) + Game.MAX_SPEED * (now - int(refilled_at[seat])) / 1000.0, BURST)
88
+ refilled_at[seat] = now
89
+ var distance := position.distance_to(current.position)
90
+ if distance > budget:
91
+ allowance[seat] = budget
92
+ return false
93
+ allowance[seat] = budget - distance
94
+ return true
95
+
96
+
97
+ ## Moves a player on purpose (respawn, portal). The owner snaps to it.
98
+ func teleport(seat: int, position: Vector3) -> void:
99
+ var group := spawner.get_state_group(Game.MOTION, Game.motion_key(seat))
100
+ if group != null and group.active:
101
+ var pose := group.get_state()
102
+ pose.position = position
103
+ allowance[seat] = BURST
104
+ group.reset(pose)
105
+
106
+
107
+ func _on_command(request: SummerNetworkCommandRequest) -> void:
108
+ var command := Game.unpack(request.payload)
109
+ var session := request.get_session()
110
+ var rid := str(command.get("rid", ""))
111
+ # A retry of a request already answered gets the same answer, applied once.
112
+ var answered: Dictionary = receipts.get(session.session_id, {})
113
+ if rid != "" and answered.has(rid):
114
+ request.accept({}, Game.pack(answered[rid]))
115
+ return
116
+ match str(command.get("c", "")):
117
+ "join":
118
+ _join(request)
119
+ "collect":
120
+ _collect(request, rid, str(command.get("coin", "")))
121
+ _:
122
+ request.refuse(&"unknown_command")
123
+
124
+
125
+ ## A client sends "join" once its network is ready: set up what the player
126
+ ## owns besides their body.
127
+ func _join(request: SummerNetworkCommandRequest) -> void:
128
+ var session := request.get_session()
129
+ var seat := _seat(session)
130
+ if seat < 0:
131
+ request.refuse(&"match_full")
132
+ return
133
+ if not mine_groups.has(session.session_id):
134
+ wallets[session.session_id] = 0
135
+ # Private state: only this Session receives it, only the authority writes it.
136
+ mine_groups[session.session_id] = spawner.create_state_group(Game.MINE, StringName("mine%d" % seat), Game.pack({"coins": 0}), null, null, session)
137
+ request.accept({}, Game.pack({"seat": seat}))
138
+ print("COURTYARD_PLAYER_JOINED seat=%d" % seat)
139
+
140
+
141
+ ## Checks everything before changing anything, then changes state only
142
+ ## through the groups so every client, late joiners included, agrees.
143
+ func _collect(request: SummerNetworkCommandRequest, rid: String, coin: String) -> void:
144
+ var session := request.get_session()
145
+ if rid == "":
146
+ request.refuse(&"missing_request_id")
147
+ return
148
+ if not mine_groups.has(session.session_id):
149
+ request.refuse(&"not_joined")
150
+ return
151
+ if not match_doc.coins.has(coin):
152
+ request.refuse(&"coin_gone")
153
+ return
154
+ # The player's latest accepted pose, never a position from the payload.
155
+ var body := spawner.get_state_group(Game.MOTION, Game.motion_key(seat_of[session.session_id]))
156
+ if body == null or not body.active:
157
+ request.refuse(&"not_spawned")
158
+ return
159
+ var at: Array = match_doc.coins[coin]
160
+ if (body.get_state().position as Vector3).distance_to(Vector3(at[0], at[1], at[2])) > REACH:
161
+ request.refuse(&"too_far")
162
+ return
163
+ match_doc.coins.erase(coin)
164
+ var name := session.player.display_name
165
+ match_doc.scores[name] = int(match_doc.scores.get(name, 0)) + 1
166
+ match_group.reset(Game.pack(match_doc))
167
+ wallets[session.session_id] += 1
168
+ (mine_groups[session.session_id] as SummerNetworkStateGroup).reset(Game.pack({"coins": wallets[session.session_id]}))
169
+ spawner.publish_event_by_policy(Game.FX, {"position": Vector3(at[0], at[1], at[2])})
170
+ var result := {"coin": coin, "coins": wallets[session.session_id]}
171
+ var answered: Dictionary = receipts.get(session.session_id, {})
172
+ answered[rid] = result
173
+ receipts[session.session_id] = answered
174
+ request.accept({}, Game.pack(result))
175
+
176
+
177
+ ## The Session ended for good: free the seat and everything the player had.
178
+ func _on_session_left(session: SummerSession, reason: String) -> void:
179
+ var seat: int = seat_of.get(session.session_id, -1)
180
+ seat_of.erase(session.session_id)
181
+ var mine: SummerNetworkStateGroup = mine_groups.get(session.session_id)
182
+ if mine != null and mine.active:
183
+ mine.retire()
184
+ mine_groups.erase(session.session_id)
185
+ wallets.erase(session.session_id)
186
+ receipts.erase(session.session_id)
187
+ print("COURTYARD_PLAYER_LEFT seat=%d reason=%s" % [seat, reason])
188
+ ```
189
+
190
+ ## `client/bot.gd`
191
+
192
+ ```gdscript
193
+ extends Node
194
+ ## Test bot for Local Play, enabled with `-- --bot={client}`. It presses the
195
+ ## real input actions. Bot 1 walks to the first coin and collects it; every
196
+ ## bot prints what it sees, for the test run to check.
197
+
198
+ const COIN := "0"
199
+
200
+ var number := 0
201
+ var main: Node
202
+ var elapsed := 0.0
203
+ var step := 0
204
+
205
+
206
+ func _process(delta: float) -> void:
207
+ elapsed += delta
208
+ var me := _own_player()
209
+ if me == null:
210
+ return
211
+ if number == 1 and step == 0:
212
+ var at: Array = main.match_doc.get("coins", {}).get(COIN, [])
213
+ if at.is_empty():
214
+ return
215
+ var to_coin := Vector2(at[0] - me.global_position.x, at[2] - me.global_position.z)
216
+ if to_coin.length() > 0.5:
217
+ _steer(to_coin.normalized())
218
+ return
219
+ _steer(Vector2.ZERO)
220
+ step = 1
221
+ print("BOT_MOVED %s" % me.global_position)
222
+ var collected: Dictionary = await main.collect(COIN)
223
+ print("BOT_COLLECT %s" % JSON.stringify(collected))
224
+ if elapsed > 6.0 and step < 2:
225
+ step = 2
226
+ for node in main.get_node("Entities").get_children():
227
+ if node != me:
228
+ print("BOT_SAW %s" % node.global_position)
229
+ print("BOT_SCORES %s" % JSON.stringify(main.match_doc.get("scores", {})))
230
+
231
+
232
+ func _own_player() -> Node3D:
233
+ for node in main.get_node("Entities").get_children():
234
+ if node.get_script() == preload("res://client/player_owner.gd"):
235
+ return node
236
+ return null
237
+
238
+
239
+ func _steer(direction: Vector2) -> void:
240
+ for action in [&"ui_left", &"ui_right", &"ui_up", &"ui_down"]:
241
+ Input.action_release(action)
242
+ if direction.x > 0.0: Input.action_press(&"ui_right", direction.x)
243
+ if direction.x < 0.0: Input.action_press(&"ui_left", -direction.x)
244
+ if direction.y > 0.0: Input.action_press(&"ui_down", direction.y)
245
+ if direction.y < 0.0: Input.action_press(&"ui_up", -direction.y)
246
+ ```
247
+
248
+ ## `client/main.gd`
249
+
250
+ ```gdscript
251
+ extends Node
252
+ ## What each player runs: joins the match and shows every player.
253
+
254
+ const Game = preload("res://network/game.gd")
255
+ const PlayerArchetype = preload("res://client/player_archetype.tres")
256
+
257
+ var spawner: SummerNetworkSpawner
258
+ var waiting := {} # entity id -> pose group that arrived before its entity
259
+ var match_doc := {} # what the authority published for everyone
260
+ var mine := {} # what the authority published for this player only
261
+
262
+
263
+ func _ready() -> void:
264
+ spawner = Game.build_network(self, PlayerArchetype)
265
+ # Connect before joining, or the first values arrive unseen.
266
+ spawner.state_group_created.connect(_on_group_created)
267
+ spawner.entity_spawned.connect(_on_entity_spawned)
268
+ spawner.event_received.connect(_on_event)
269
+ var initialized: SummerResult = await Summer.initialize(Game.GAME_ID).get_result_or_completed_signal()
270
+ if not initialized.ok:
271
+ push_error(initialized.message)
272
+ return
273
+ var join := Summer.client.join(SummerJoinTarget.queue(Game.QUEUE))
274
+ # Hosted matchmaking may ask the player to accept a match.
275
+ join.acceptance_required.connect(func(proposal: SummerMatchmakingProposal) -> void: proposal.accept())
276
+ var joined: SummerResult = await join.get_result_or_completed_signal()
277
+ if not joined.ok:
278
+ push_error("join failed: %s %s" % [joined.code, joined.message])
279
+ return
280
+ if not spawner.is_network_ready():
281
+ await spawner.network_ready
282
+ var entered := await command({"c": "join"})
283
+ if not entered.ok:
284
+ push_error("could not enter the match: %s" % entered.error)
285
+ return
286
+ print("COURTYARD_CLIENT_JOINED")
287
+ # Local Play test bot: `-- --bot={client}` (see multiplayer-testing).
288
+ for arg in OS.get_cmdline_user_args():
289
+ if arg.begins_with("--bot="):
290
+ var bot := preload("res://client/bot.gd").new()
291
+ bot.number = int(arg.trim_prefix("--bot="))
292
+ bot.main = self
293
+ add_child(bot)
294
+
295
+
296
+ ## Sends one Command and waits for the authority's answer.
297
+ func command(payload: Dictionary) -> Dictionary:
298
+ var admission := spawner.enqueue_command(Game.CMD, Game.pack(payload))
299
+ if not admission.is_enqueued():
300
+ return {"ok": false, "error": String(admission.get_code())}
301
+ var handle := admission.get_handle()
302
+ if not handle.is_terminal():
303
+ await handle.completed
304
+ if handle.get_outcome() == SummerNetworkCommandHandle.OUTCOME_ACCEPTED:
305
+ return {"ok": true, "error": "", "result": Game.unpack(handle.get_result())}
306
+ return {"ok": false, "error": String(handle.get_reason())}
307
+
308
+
309
+ func _on_group_created(group: SummerNetworkStateGroup) -> void:
310
+ match group.get_stream_id():
311
+ Game.MOTION:
312
+ _attach_pose(group, int(group.get_state().entity))
313
+ Game.MATCH, Game.MINE:
314
+ # The baseline is already installed when this fires; later
315
+ # changes arrive through state_installed.
316
+ _on_document(group, group.get_state())
317
+ group.state_installed.connect(func(state: Dictionary, _revision: int, _reset: bool) -> void: _on_document(group, state))
318
+
319
+
320
+ ## The pose group and the player's entity can arrive in either order.
321
+ func _attach_pose(group: SummerNetworkStateGroup, entity_id: int) -> void:
322
+ var entity := spawner.get_entity(entity_id)
323
+ if entity == null or entity.root == null:
324
+ waiting[entity_id] = group
325
+ return
326
+ entity.root.bind(group)
327
+
328
+
329
+ func _on_entity_spawned(entity: SummerNetworkEntity) -> void:
330
+ if waiting.has(entity.entity_id):
331
+ _attach_pose(waiting[entity.entity_id], entity.entity_id)
332
+ waiting.erase(entity.entity_id)
333
+
334
+
335
+ func _on_document(group: SummerNetworkStateGroup, state: Dictionary) -> void:
336
+ if group.get_stream_id() == Game.MATCH:
337
+ match_doc = Game.unpack(state)
338
+ else:
339
+ mine = Game.unpack(state)
340
+
341
+
342
+ func _on_event(event: SummerNetworkEvent) -> void:
343
+ if event.stream_id == Game.FX:
344
+ print("COURTYARD_SPARKLE at %s" % event.payload.position)
345
+
346
+
347
+ ## Asks the authority for a coin. The request id makes a retry safe.
348
+ func collect(coin: String) -> Dictionary:
349
+ return await command({"c": "collect", "coin": coin, "rid": "collect-%s-%d" % [coin, Time.get_ticks_usec()]})
350
+ ```
351
+
352
+ ## `export_presets.cfg`
353
+
354
+ ```ini
355
+ [preset.0]
356
+
357
+ name="summer.games"
358
+ platform="summer.games"
359
+ runnable=false
360
+ dedicated_server=false
361
+ custom_features="summer_client"
362
+ export_filter="all_resources"
363
+ include_filter=""
364
+ exclude_filter=""
365
+ export_path=""
366
+ encrypt_pck=false
367
+ encrypt_directory=false
368
+ script_export_mode=2
369
+
370
+ [preset.0.options]
371
+
372
+ platforms/ios=true
373
+ platforms/macos=true
374
+ platforms/windows=false
375
+
376
+ [preset.1]
377
+
378
+ name="Courtyard Authority"
379
+ platform="Linux"
380
+ runnable=false
381
+ dedicated_server=true
382
+ custom_features="summer_authority"
383
+ export_filter="all_resources"
384
+ include_filter=""
385
+ exclude_filter="client/*"
386
+ export_path=""
387
+ encrypt_pck=false
388
+ encrypt_directory=false
389
+ script_export_mode=2
390
+
391
+ [preset.1.options]
392
+
393
+ binary_format/architecture="x86_64"
394
+ ```
395
+
396
+ ## `network/composition.tres`
397
+
398
+ ```ini
399
+ [gd_resource type="SummerNetworkComposition" load_steps=22 format=3]
400
+
401
+ [sub_resource type="SummerNetworkField" id="MotionPosition"]
402
+ field_name = &"position"
403
+ type = 9
404
+
405
+ [sub_resource type="SummerNetworkField" id="MotionYaw"]
406
+ field_name = &"yaw"
407
+ type = 2
408
+
409
+ [sub_resource type="SummerNetworkField" id="MotionEntity"]
410
+ field_name = &"entity"
411
+ type = 1
412
+
413
+ [sub_resource type="SummerNetworkSchema" id="MotionSchema"]
414
+ schema_id = &"courtyard.motion.v1"
415
+ state_fields = Array[SummerNetworkField]([SubResource("MotionEntity"), SubResource("MotionPosition"), SubResource("MotionYaw")])
416
+
417
+ [sub_resource type="SummerWorldAudience" id="Everyone"]
418
+ max_expansion = 8
419
+
420
+ [sub_resource type="SummerNetworkStateStream" id="Motion"]
421
+ stream_id = &"courtyard.motion"
422
+ writer_policy = 1
423
+ skip_writer_echo = true
424
+ state_schema = SubResource("MotionSchema")
425
+ audience_policy = SubResource("Everyone")
426
+ max_audience_count = 8
427
+ max_keys = 8
428
+ max_state_bytes = 1024
429
+ max_total_bytes = 8192
430
+
431
+ [sub_resource type="SummerNetworkField" id="CommandBytes"]
432
+ field_name = &"b"
433
+ type = 20
434
+
435
+ [sub_resource type="SummerNetworkSchema" id="CommandSchema"]
436
+ schema_id = &"courtyard.cmd.v1"
437
+ state_fields = Array[SummerNetworkField]([SubResource("CommandBytes")])
438
+
439
+ [sub_resource type="SummerNetworkField" id="ResultBytes"]
440
+ field_name = &"b"
441
+ type = 20
442
+
443
+ [sub_resource type="SummerNetworkSchema" id="ResultSchema"]
444
+ schema_id = &"courtyard.cmd.result.v1"
445
+ state_fields = Array[SummerNetworkField]([SubResource("ResultBytes")])
446
+
447
+ [sub_resource type="SummerNetworkOccurrenceStream" id="Commands"]
448
+ stream_id = &"courtyard.cmd"
449
+ command_scope = 2
450
+ command_tick_dispatch = true
451
+ command_timeout_msec = 8000
452
+ payload_schema = SubResource("CommandSchema")
453
+ result_schema = SubResource("ResultSchema")
454
+
455
+ [sub_resource type="SummerNetworkField" id="MatchBytes"]
456
+ field_name = &"b"
457
+ type = 20
458
+
459
+ [sub_resource type="SummerNetworkSchema" id="MatchSchema"]
460
+ schema_id = &"courtyard.match.v1"
461
+ state_fields = Array[SummerNetworkField]([SubResource("MatchBytes")])
462
+
463
+ [sub_resource type="SummerWorldAudience" id="MatchAudience"]
464
+ max_expansion = 8
465
+
466
+ [sub_resource type="SummerNetworkStateStream" id="Match"]
467
+ stream_id = &"courtyard.match"
468
+ state_schema = SubResource("MatchSchema")
469
+ audience_policy = SubResource("MatchAudience")
470
+ max_audience_count = 8
471
+ max_keys = 1
472
+ max_state_bytes = 4096
473
+ max_total_bytes = 4096
474
+
475
+ [sub_resource type="SummerNetworkField" id="MineBytes"]
476
+ field_name = &"b"
477
+ type = 20
478
+
479
+ [sub_resource type="SummerNetworkSchema" id="MineSchema"]
480
+ schema_id = &"courtyard.mine.v1"
481
+ state_fields = Array[SummerNetworkField]([SubResource("MineBytes")])
482
+
483
+ [sub_resource type="SummerTargetSessionAudience" id="OnlyThatPlayer"]
484
+ max_expansion = 1
485
+
486
+ [sub_resource type="SummerNetworkStateStream" id="Mine"]
487
+ stream_id = &"courtyard.mine"
488
+ state_schema = SubResource("MineSchema")
489
+ audience_policy = SubResource("OnlyThatPlayer")
490
+ max_audience_count = 1
491
+ max_keys = 8
492
+ max_state_bytes = 1024
493
+ max_total_bytes = 8192
494
+
495
+ [sub_resource type="SummerNetworkField" id="FxPosition"]
496
+ field_name = &"position"
497
+ type = 9
498
+
499
+ [sub_resource type="SummerNetworkSchema" id="FxSchema"]
500
+ schema_id = &"courtyard.fx.v1"
501
+ state_fields = Array[SummerNetworkField]([SubResource("FxPosition")])
502
+
503
+ [sub_resource type="SummerWorldAudience" id="FxAudience"]
504
+ max_expansion = 8
505
+
506
+ [sub_resource type="SummerNetworkOccurrenceStream" id="Fx"]
507
+ stream_id = &"courtyard.fx"
508
+ kind = 2
509
+ command_scope = 0
510
+ event_scope = 2
511
+ event_delivery = 2
512
+ transient_ttl_ticks = 30
513
+ max_outcome_history = 0
514
+ command_timeout_msec = 0
515
+ audience_policy = SubResource("FxAudience")
516
+ max_audience_count = 8
517
+ payload_schema = SubResource("FxSchema")
518
+
519
+ [resource]
520
+ network_version = "1.0.0"
521
+ state_streams = Array[SummerNetworkStateStream]([SubResource("Motion"), SubResource("Match"), SubResource("Mine")])
522
+ occurrence_streams = Array[SummerNetworkOccurrenceStream]([SubResource("Commands"), SubResource("Fx")])
523
+ ```
524
+
525
+ ## `network/game.gd`
526
+
527
+ ```gdscript
528
+ extends RefCounted
529
+ ## Shared by the client and the authority: ids, streams and shared rules.
530
+
531
+ const GAME_ID := "game_courtyard"
532
+ const QUEUE := &"courtyard"
533
+ const MOTION := &"courtyard.motion"
534
+ const CMD := &"courtyard.cmd"
535
+ const MATCH := &"courtyard.match" # one document every player sees
536
+ const MINE := &"courtyard.mine" # one private document per player
537
+ const FX := &"courtyard.fx" # one-off effects, never stored
538
+ const MAX_PLAYERS := 8
539
+ const COMPOSITION := preload("res://network/composition.tres")
540
+
541
+ ## The authority's sanity bound on movement, in metres per second. Keep it
542
+ ## above the fastest legitimate speed (walking, jumping, falling).
543
+ const MAX_SPEED := 15.0
544
+ const ARENA_RADIUS := 40.0
545
+
546
+
547
+ ## Adds the World, an Entities root and the Spawner under `parent`. Both sides
548
+ ## pass their own player archetype; the composition is the same file.
549
+ static func build_network(parent: Node, player: SummerEntityArchetype) -> SummerNetworkSpawner:
550
+ var world := SummerNetworkWorld.new()
551
+ world.name = "World"
552
+ parent.add_child(world)
553
+ var entities := Node3D.new()
554
+ entities.name = "Entities"
555
+ parent.add_child(entities)
556
+ var spawner := SummerNetworkSpawner.new()
557
+ spawner.name = "Spawner"
558
+ spawner.network_world_path = NodePath("../World")
559
+ spawner.entities_root_path = NodePath("../Entities")
560
+ spawner.archetypes = [player]
561
+ spawner.player_archetype = &"player" # one entity per joined player
562
+ spawner.network_composition = COMPOSITION
563
+ parent.add_child(spawner)
564
+ return spawner
565
+
566
+
567
+ ## Pose groups are keyed by seat, so a stream needs at most MAX_PLAYERS keys.
568
+ static func motion_key(seat: int) -> StringName:
569
+ return StringName("p%d" % seat)
570
+
571
+
572
+ ## Seats spawn on a circle. `entity` ties the pose to the player's entity.
573
+ static func spawn_pose(seat: int, entity_id: int) -> Dictionary:
574
+ var angle := TAU * float(seat) / MAX_PLAYERS
575
+ return {"entity": entity_id, "position": Vector3(cos(angle) * 4.0, 1.0, sin(angle) * 4.0), "yaw": 0.0}
576
+
577
+
578
+ static func in_bounds(position: Vector3) -> bool:
579
+ return position.is_finite() and Vector2(position.x, position.z).length() <= ARENA_RADIUS \
580
+ and position.y > -10.0 and position.y < 30.0
581
+
582
+
583
+ ## Commands and results travel as JSON bytes: a network String holds at most
584
+ ## 256 bytes.
585
+ static func pack(document: Dictionary) -> Dictionary:
586
+ return {"b": JSON.stringify(document).to_utf8_buffer()}
587
+
588
+
589
+ static func unpack(fields: Dictionary) -> Dictionary:
590
+ var parsed: Variant = JSON.parse_string((fields.get("b", PackedByteArray()) as PackedByteArray).get_string_from_utf8())
591
+ return parsed if parsed is Dictionary else {}
592
+ ```
@@ -0,0 +1,35 @@
1
+ id: skill/multiplayer-state
2
+ kind: skill
3
+ version: 1.0.0
4
+ summary: "Authority-owned game state in Summer multiplayer: shared and private State groups, validated Commands, and Events."
5
+ use_when:
6
+ - "score, health, doors, rounds, inventories or wallets in a Summer multiplayer game, and who owns them"
7
+ - "players need to collect, buy, use, open or trade things and the authority must validate it"
8
+ - "clients desync, cheat or double-spend; sending one-off effects to every player"
9
+ facets:
10
+ lifecycle:
11
+ - build
12
+ domains:
13
+ - multiplayer
14
+ - game-design
15
+ - scripting
16
+ modalities:
17
+ - scripts
18
+ compatibility:
19
+ engine: ">=4.6"
20
+ related:
21
+ skills:
22
+ - skill/multiplayer
23
+ - skill/multiplayer-movement
24
+ - skill/multiplayer-testing
25
+ - skill/summer-player-data
26
+ - skill/summer-match-results
27
+ - skill/multiplayer-project
28
+ - skill/summer-analytics
29
+ - skill/summer-store
30
+ - skill/summer-world-chat
31
+ - skill/summer-world-saves
32
+ source: official
33
+ license: MIT
34
+ status: stable
35
+ recommended: false