@aqian0/dsh-unity 0.0.0-stage → 0.1.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 (360) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/LICENSE-UNITY-COMPANION.md +122 -0
  3. package/LICENSE.md +31 -0
  4. package/NOTICE.md +90 -0
  5. package/README.md +273 -2
  6. package/cordis.patch.yml +22 -0
  7. package/docs/design.md +297 -0
  8. package/icon.svg +16 -0
  9. package/lib/frontmatter.js +392 -0
  10. package/lib/gating.js +105 -0
  11. package/lib/harness.js +16 -0
  12. package/lib/index.js +171 -0
  13. package/lib/project.js +210 -0
  14. package/lib/registry.js +268 -0
  15. package/lib/requirements.js +214 -0
  16. package/lib/skills.js +191 -0
  17. package/lib/types/frontmatter.d.ts +84 -0
  18. package/lib/types/gating.d.ts +80 -0
  19. package/lib/types/harness.d.ts +152 -0
  20. package/lib/types/index.d.ts +75 -0
  21. package/lib/types/project.d.ts +93 -0
  22. package/lib/types/registry.d.ts +96 -0
  23. package/lib/types/requirements.d.ts +67 -0
  24. package/lib/types/skills.d.ts +54 -0
  25. package/lib/types/watch.d.ts +34 -0
  26. package/lib/watch.js +84 -0
  27. package/locale/en.json +6 -0
  28. package/locale/zh.json +6 -0
  29. package/package.json +70 -4
  30. package/skills/2d-pixel-perfect/SKILL.md +208 -0
  31. package/skills/2d-pixel-perfect/references/api-reference.md +81 -0
  32. package/skills/2d-pixel-perfect/references/camera-setup-builtin.cs +40 -0
  33. package/skills/2d-pixel-perfect/references/camera-setup-urp.cs +49 -0
  34. package/skills/2d-pixel-perfect/references/pipeline-detection.cs +50 -0
  35. package/skills/2d-pixel-perfect/references/sprite-settings.cs +37 -0
  36. package/skills/asset-transformer-toolkit/SECURITY.md +15 -0
  37. package/skills/asset-transformer-toolkit/SKILL.md +39 -0
  38. package/skills/asset-transformer-toolkit/references/create-importer.md +121 -0
  39. package/skills/asset-transformer-toolkit/references/lods.md +61 -0
  40. package/skills/asset-transformer-toolkit/references/rule-api.md +154 -0
  41. package/skills/asset-transformer-toolkit/references/ruleblock-api.md +37 -0
  42. package/skills/asset-transformer-toolkit/references/ruleset-api.md +138 -0
  43. package/skills/asset-transformer-toolkit/references/rulesets-and-actions.md +227 -0
  44. package/skills/audio-setup-mixers/SECURITY.md +16 -0
  45. package/skills/audio-setup-mixers/SKILL.md +127 -0
  46. package/skills/audio-setup-mixers/references/api.md +162 -0
  47. package/skills/build-gtk/SECURITY.md +16 -0
  48. package/skills/build-gtk/SKILL.md +200 -0
  49. package/skills/build-gtk/references/api-reference.md +33 -0
  50. package/skills/build-gtk/references/graph-api-gaps.md +96 -0
  51. package/skills/build-gtk/references/runtime-and-visualization.md +191 -0
  52. package/skills/build-gtk/references/state-machine-api.md +270 -0
  53. package/skills/build-gtk/references/where-the-docs-stop.md +49 -0
  54. package/skills/build-live-game/SKILL.md +159 -0
  55. package/skills/build-live-game/references/achievements.md +757 -0
  56. package/skills/build-live-game/references/apis.md +271 -0
  57. package/skills/build-live-game/references/authentication.md +431 -0
  58. package/skills/build-live-game/references/battlepass.md +904 -0
  59. package/skills/build-live-game/references/blueprints-and-samples.md +75 -0
  60. package/skills/build-live-game/references/cloud-code-cloudbehaviours.md +1111 -0
  61. package/skills/build-live-game/references/cloud-code-modules.md +1276 -0
  62. package/skills/build-live-game/references/cloud-code.md +58 -0
  63. package/skills/build-live-game/references/cloud-save.md +464 -0
  64. package/skills/build-live-game/references/deployment.md +231 -0
  65. package/skills/build-live-game/references/orientation.md +30 -0
  66. package/skills/build-live-game/references/player-account.md +821 -0
  67. package/skills/build-live-game/references/remote-config.md +96 -0
  68. package/skills/build-live-game/references/services-overview.md +76 -0
  69. package/skills/build-live-game/references/tooling.md +375 -0
  70. package/skills/build-live-game/references/ugs-cli.md +86 -0
  71. package/skills/craft-game-floors/SECURITY.md +23 -0
  72. package/skills/craft-game-floors/SKILL.md +134 -0
  73. package/skills/craft-game-floors/references/art-direction.md +85 -0
  74. package/skills/craft-game-floors/references/camera-rigs.md +51 -0
  75. package/skills/craft-game-floors/references/character-motion.md +35 -0
  76. package/skills/craft-game-floors/references/game-audio.md +47 -0
  77. package/skills/craft-game-floors/references/game-feel.md +68 -0
  78. package/skills/craft-game-floors/references/game-shell.md +45 -0
  79. package/skills/craft-game-floors/references/live-floor.md +35 -0
  80. package/skills/craft-game-floors/references/placeholder-assets.md +35 -0
  81. package/skills/craft-game-floors/references/scene-lighting.md +52 -0
  82. package/skills/craft-game-floors/references/toon-shading.md +34 -0
  83. package/skills/craft-game-floors/references/vfx.md +40 -0
  84. package/skills/craft-game-floors/references/visual-scorecard.md +43 -0
  85. package/skills/create-2d-physics/SECURITY.md +16 -0
  86. package/skills/create-2d-physics/SKILL.md +128 -0
  87. package/skills/create-2d-physics/references/2d-physics-core.md +155 -0
  88. package/skills/create-2d-physics/references/legacy-2d-physics.md +45 -0
  89. package/skills/create-tile-palette-or-rule-tiles/SKILL.md +95 -0
  90. package/skills/create-tile-palette-or-rule-tiles/references/hexagonalruletile.md +71 -0
  91. package/skills/create-tile-palette-or-rule-tiles/references/palette-create.md +38 -0
  92. package/skills/create-tile-palette-or-rule-tiles/references/ruletile-createempty.md +37 -0
  93. package/skills/create-tile-palette-or-rule-tiles/references/ruletile-createfromsegment.md +219 -0
  94. package/skills/create-tile-palette-or-rule-tiles/references/ruletile.md +95 -0
  95. package/skills/create-tile-palette-or-rule-tiles/scripts/CreatePaletteTemplate.cs +148 -0
  96. package/skills/create-tile-palette-or-rule-tiles/scripts/ManualWorkflowExample.cs +63 -0
  97. package/skills/create-tile-palette-or-rule-tiles/scripts/RuleTileGenerator.cs +61 -0
  98. package/skills/create-tile-palette-or-rule-tiles/scripts/TilemapRuleTileCreateFromSegment.cs +225 -0
  99. package/skills/fix-3d-physics-collisions/SECURITY.md +17 -0
  100. package/skills/fix-3d-physics-collisions/SKILL.md +139 -0
  101. package/skills/fix-3d-physics-collisions/references/collision-checklist.md +80 -0
  102. package/skills/fix-3d-physics-collisions/references/collision-debugger.md +18 -0
  103. package/skills/fix-3d-physics-collisions/references/console-errors-table.md +9 -0
  104. package/skills/fix-3d-physics-collisions/references/kinematic-trigger-facts.md +18 -0
  105. package/skills/fix-3d-physics-collisions/references/other-symptoms.md +29 -0
  106. package/skills/fix-3d-physics-collisions/references/raycast-checklist.md +8 -0
  107. package/skills/fix-3d-physics-collisions/references/topic-map.md +42 -0
  108. package/skills/fix-3d-physics-collisions/references/trigger-checklist.md +9 -0
  109. package/skills/fix-3d-physics-collisions/scripts/CollisionDebugger.cs +561 -0
  110. package/skills/generate-editor-search-query/SECURITY.md +17 -0
  111. package/skills/generate-editor-search-query/SKILL.md +117 -0
  112. package/skills/generate-editor-search-query/references/open-search-window.md +70 -0
  113. package/skills/generate-editor-search-query/references/query-patterns.md +124 -0
  114. package/skills/implement-in-app-purchases/SKILL.md +82 -0
  115. package/skills/implement-in-app-purchases/references/api-details.md +41 -0
  116. package/skills/implement-in-app-purchases/references/api-notes.md +105 -0
  117. package/skills/implement-in-app-purchases/references/codeless-catalog.md +197 -0
  118. package/skills/implement-in-app-purchases/references/convert-adapty.md +270 -0
  119. package/skills/implement-in-app-purchases/references/convert-essentialkit.md +239 -0
  120. package/skills/implement-in-app-purchases/references/convert-revenuecat.md +277 -0
  121. package/skills/implement-in-app-purchases/references/convert-unipay.md +145 -0
  122. package/skills/implement-in-app-purchases/references/migration-v4-to-v5.md +46 -0
  123. package/skills/implement-in-app-purchases/references/path-add-iap-to-new-project.md +197 -0
  124. package/skills/implement-in-app-purchases/references/path-convert-native-google-billing.md +302 -0
  125. package/skills/implement-in-app-purchases/references/path-convert-native-storekit.md +357 -0
  126. package/skills/implement-in-app-purchases/references/path-implement-iap-d2c.md +253 -0
  127. package/skills/implement-in-app-purchases/references/platform-notes.md +55 -0
  128. package/skills/implement-in-app-purchases/references/pre-check.md +207 -0
  129. package/skills/implement-in-app-purchases/references/topic-map.md +46 -0
  130. package/skills/initialize-ai-navigation/SKILL.md +109 -0
  131. package/skills/initialize-ai-navigation/references/navigation-system.md +793 -0
  132. package/skills/initialize-ai-navigation/references/topic-map.md +66 -0
  133. package/skills/levelplay-unity-integration/SKILL.md +122 -0
  134. package/skills/levelplay-unity-integration/references/adding-more-ad-formats.md +15 -0
  135. package/skills/levelplay-unity-integration/references/banner-api.md +927 -0
  136. package/skills/levelplay-unity-integration/references/best-practices.md +604 -0
  137. package/skills/levelplay-unity-integration/references/dependency-resolution.md +94 -0
  138. package/skills/levelplay-unity-integration/references/examples.md +10 -0
  139. package/skills/levelplay-unity-integration/references/ilrd-api.md +337 -0
  140. package/skills/levelplay-unity-integration/references/initialization-api.md +851 -0
  141. package/skills/levelplay-unity-integration/references/interstitial-api.md +907 -0
  142. package/skills/levelplay-unity-integration/references/ios-setup.md +463 -0
  143. package/skills/levelplay-unity-integration/references/migration-sdk-9.md +666 -0
  144. package/skills/levelplay-unity-integration/references/privacy-settings.md +593 -0
  145. package/skills/levelplay-unity-integration/references/production-release-checklist.md +22 -0
  146. package/skills/levelplay-unity-integration/references/rewarded-api.md +918 -0
  147. package/skills/levelplay-unity-integration/references/step-0-new-or-migration.md +8 -0
  148. package/skills/levelplay-unity-integration/references/step-1-verify-environment.md +4 -0
  149. package/skills/levelplay-unity-integration/references/step-10-testing.md +15 -0
  150. package/skills/levelplay-unity-integration/references/step-2-business-goals.md +12 -0
  151. package/skills/levelplay-unity-integration/references/step-3-install-sdk.md +55 -0
  152. package/skills/levelplay-unity-integration/references/step-4-native-dependencies.md +22 -0
  153. package/skills/levelplay-unity-integration/references/step-5-app-key-and-ad-units.md +18 -0
  154. package/skills/levelplay-unity-integration/references/step-6-5-privacy-and-regulation.md +51 -0
  155. package/skills/levelplay-unity-integration/references/step-6-admob-keys.md +16 -0
  156. package/skills/levelplay-unity-integration/references/step-7-initialize.md +50 -0
  157. package/skills/levelplay-unity-integration/references/step-8-ad-unit-strategy.md +16 -0
  158. package/skills/levelplay-unity-integration/references/step-9-implement-ad-units.md +66 -0
  159. package/skills/levelplay-unity-integration/references/testing-and-validation.md +222 -0
  160. package/skills/levelplay-unity-integration/references/troubleshooting.md +99 -0
  161. package/skills/localize-unity-project/SKILL.md +159 -0
  162. package/skills/localize-unity-project/references/api-notes.md +15 -0
  163. package/skills/localize-unity-project/references/cjk-fonts.md +83 -0
  164. package/skills/localize-unity-project/references/docs-map.md +42 -0
  165. package/skills/localize-unity-project/references/documentation-lookup.md +33 -0
  166. package/skills/localize-unity-project/references/locale-switching.md +17 -0
  167. package/skills/localize-unity-project/references/table-completeness-check.md +47 -0
  168. package/skills/localize-unity-project/references/translating-existing-project.md +47 -0
  169. package/skills/localize-unity-project/references/translation-strategy.md +41 -0
  170. package/skills/localize-unity-project/references/troubleshooting.md +12 -0
  171. package/skills/localize-unity-project/scripts/L10nBatchProcessor.cs +191 -0
  172. package/skills/localize-unity-project/scripts/LocalizedFontAsset.cs +18 -0
  173. package/skills/manage-sprite-atlas/SKILL.md +202 -0
  174. package/skills/manage-sprite-atlas/references/addressables-delivery.md +58 -0
  175. package/skills/manage-sprite-atlas/references/advanced-manual-authoring.md +19 -0
  176. package/skills/manage-sprite-atlas/references/api.md +382 -0
  177. package/skills/manage-sprite-atlas/references/best-practices.md +183 -0
  178. package/skills/manage-sprite-atlas/references/common-errors.md +123 -0
  179. package/skills/manage-sprite-atlas/references/complete-workflow.md +61 -0
  180. package/skills/manage-sprite-atlas/references/custom-packing.md +147 -0
  181. package/skills/manage-sprite-atlas/references/delivery-mechanism.md +30 -0
  182. package/skills/manage-sprite-atlas/references/key-requirements.md +21 -0
  183. package/skills/manage-sprite-atlas/references/manual-authoring.md +39 -0
  184. package/skills/manage-sprite-atlas/references/namespaces.md +15 -0
  185. package/skills/manage-sprite-atlas/references/v2-architecture.md +21 -0
  186. package/skills/manage-sprite-atlas/resources/authoringvsruntime.cs +176 -0
  187. package/skills/manage-sprite-atlas/resources/buildaddressablespostprocess.cs +59 -0
  188. package/skills/manage-sprite-atlas/resources/cacheloadedatlases.cs +145 -0
  189. package/skills/manage-sprite-atlas/resources/cleanupbatchoperations.cs +119 -0
  190. package/skills/manage-sprite-atlas/resources/createandconfiguremasteratlas.cs +57 -0
  191. package/skills/manage-sprite-atlas/resources/custompackersetup.cs +88 -0
  192. package/skills/manage-sprite-atlas/resources/deprecatedmethods.cs +142 -0
  193. package/skills/manage-sprite-atlas/resources/dontmodifywithoutsaving.cs +120 -0
  194. package/skills/manage-sprite-atlas/resources/dontpackinruntimebuilds.cs +190 -0
  195. package/skills/manage-sprite-atlas/resources/dontscriptspriteatlasineditor.cs +114 -0
  196. package/skills/manage-sprite-atlas/resources/editorconfiguration.cs +95 -0
  197. package/skills/manage-sprite-atlas/resources/enablespritepacking.cs +172 -0
  198. package/skills/manage-sprite-atlas/resources/gridspritepacker.cs +50 -0
  199. package/skills/manage-sprite-atlas/resources/handlelatebinding.cs +147 -0
  200. package/skills/manage-sprite-atlas/resources/handlenullcases.cs +141 -0
  201. package/skills/manage-sprite-atlas/resources/migratev1tov2.cs +335 -0
  202. package/skills/manage-sprite-atlas/resources/multipagepacker.cs +80 -0
  203. package/skills/manage-sprite-atlas/resources/optimizetexturesettings.cs +191 -0
  204. package/skills/manage-sprite-atlas/resources/packerdebugging.cs +158 -0
  205. package/skills/manage-sprite-atlas/resources/packtransform.cs +51 -0
  206. package/skills/manage-sprite-atlas/resources/platformnames.cs +127 -0
  207. package/skills/manage-sprite-atlas/resources/queryspritesefficiently.cs +222 -0
  208. package/skills/manage-sprite-atlas/resources/registercallbacks.cs +152 -0
  209. package/skills/manage-sprite-atlas/resources/runtimeatlasloader.cs +15 -0
  210. package/skills/manage-sprite-atlas/resources/runtimespriteaccess.cs +19 -0
  211. package/skills/manage-sprite-atlas/resources/savespriteatlasasset.cs +107 -0
  212. package/skills/manage-sprite-atlas/resources/scriptablepacker.cs +81 -0
  213. package/skills/manage-sprite-atlas/resources/setincludeinbuild.cs +164 -0
  214. package/skills/manage-sprite-atlas/resources/sizeoptimizedpacker.cs +88 -0
  215. package/skills/manage-sprite-atlas/resources/spriteatlaslatebinding.cs +123 -0
  216. package/skills/manage-sprite-atlas/resources/spriteatlasprebuildgenerator.cs +219 -0
  217. package/skills/manage-sprite-atlas/resources/spritedata.cs +35 -0
  218. package/skills/manage-sprite-atlas/resources/spritepack.cs +39 -0
  219. package/skills/manage-sprite-atlas/resources/texturedata.cs +15 -0
  220. package/skills/manage-sprite-atlas/resources/updatebuildscripts.cs +357 -0
  221. package/skills/manage-sprite-atlas/resources/updatesettingsconfiguration.cs +135 -0
  222. package/skills/manage-sprite-atlas/resources/usevariantswisely.cs +179 -0
  223. package/skills/manage-sprite-atlas/resources/variantatlas.cs +39 -0
  224. package/skills/migrate-birp-to-urp/SECURITY.md +16 -0
  225. package/skills/migrate-birp-to-urp/SKILL.md +167 -0
  226. package/skills/migrate-birp-to-urp/references/capturing-the-editor.md +66 -0
  227. package/skills/migrate-birp-to-urp/references/complex-shader-situations.md +272 -0
  228. package/skills/migrate-birp-to-urp/references/conversion-and-rendering-triage.md +113 -0
  229. package/skills/migrate-birp-to-urp/references/custom-shader-triage.md +123 -0
  230. package/skills/migrate-birp-to-urp/references/implementation-patterns.md +529 -0
  231. package/skills/migrate-birp-to-urp/references/migration-workflow.md +265 -0
  232. package/skills/migrate-birp-to-urp/references/parity-validation-and-troubleshooting.md +114 -0
  233. package/skills/migrate-birp-to-urp/references/preflight-and-urp-setup.md +195 -0
  234. package/skills/migrate-birp-to-urp/references/quality-settings-map.md +69 -0
  235. package/skills/new-unity-project/SECURITY.md +26 -0
  236. package/skills/new-unity-project/SKILL.md +171 -0
  237. package/skills/new-unity-project/references/checklist.md +18 -0
  238. package/skills/new-unity-project/references/common-mistakes.md +15 -0
  239. package/skills/new-unity-project/references/skill-setup.md +66 -0
  240. package/skills/new-unity-project/references/step-1-concept.md +13 -0
  241. package/skills/new-unity-project/references/step-2-platforms-and-create.md +31 -0
  242. package/skills/new-unity-project/references/step-3-join-and-reconcile.md +16 -0
  243. package/skills/new-unity-project/references/step-4-source-control.md +9 -0
  244. package/skills/new-unity-project/references/step-5-packages.md +18 -0
  245. package/skills/new-unity-project/references/step-6-identity-and-floors.md +20 -0
  246. package/skills/new-unity-project/references/step-7-visual-baseline.md +75 -0
  247. package/skills/new-unity-project/references/step-8-first-commit.md +21 -0
  248. package/skills/new-unity-project/references/step-9-handoff.md +23 -0
  249. package/skills/optimize-audio/SECURITY.md +16 -0
  250. package/skills/optimize-audio/SKILL.md +120 -0
  251. package/skills/optimize-audio/references/diagnostics.md +42 -0
  252. package/skills/optimize-audio/references/troubleshooting.md +41 -0
  253. package/skills/optimize-audio/resources/audio-import-api.md +149 -0
  254. package/skills/optimize-audio/resources/platform-settings.md +48 -0
  255. package/skills/optimize-text-mesh-pro/SKILL.md +181 -0
  256. package/skills/optimize-text-mesh-pro/references/dynamic-atlas-and-profiling.md +28 -0
  257. package/skills/optimize-text-mesh-pro/references/topic-map.md +36 -0
  258. package/skills/optimize-web/SECURITY.md +16 -0
  259. package/skills/optimize-web/SKILL.md +157 -0
  260. package/skills/optimize-web/references/editor-csharp.md +120 -0
  261. package/skills/optimize-web/references/topic-map.md +49 -0
  262. package/skills/optimize-web/references/troubleshooting.md +45 -0
  263. package/skills/optimize-web/references/understand-request.md +19 -0
  264. package/skills/optimize-web/references/web-assets-quality.md +59 -0
  265. package/skills/optimize-web/references/web-player-settings.md +39 -0
  266. package/skills/optimize-web/references/web-profiling-memory.md +71 -0
  267. package/skills/optimize-web/scripts/WebOptimizer.cs +46 -0
  268. package/skills/optimize-web/scripts/toktx-examples.sh +11 -0
  269. package/skills/setup-multiplayer-services/SKILL.md +36 -0
  270. package/skills/setup-multiplayer-services/references/dgs-entrypoint.md +79 -0
  271. package/skills/setup-multiplayer-services/references/entrypoints.md +213 -0
  272. package/skills/setup-multiplayer-services/references/examples.md +33 -0
  273. package/skills/setup-multiplayer-services/references/implementation-fit.md +30 -0
  274. package/skills/setup-multiplayer-services/references/underlying-services.md +11 -0
  275. package/skills/setup-multiplayer-services/references/workflows-prerequisites.md +17 -0
  276. package/skills/setup-vivox-voice-chat/SECURITY.md +16 -0
  277. package/skills/setup-vivox-voice-chat/SKILL.md +166 -0
  278. package/skills/setup-vivox-voice-chat/references/api-reference-lookup.md +28 -0
  279. package/skills/setup-vivox-voice-chat/references/docs-topic-map.md +13 -0
  280. package/skills/setup-vivox-voice-chat/references/events-and-participants.md +79 -0
  281. package/skills/setup-vivox-voice-chat/references/init-and-login.md +92 -0
  282. package/skills/setup-vivox-voice-chat/references/text-chat.md +93 -0
  283. package/skills/setup-vivox-voice-chat/references/troubleshooting.md +47 -0
  284. package/skills/setup-vivox-voice-chat/references/voice-channels.md +92 -0
  285. package/skills/shader-graph-create-custom-node/SKILL.md +25 -0
  286. package/skills/shader-graph-create-custom-node/resources/all_hints.hlsl +182 -0
  287. package/skills/sprite-editor/SECURITY.md +16 -0
  288. package/skills/sprite-editor/SKILL.md +66 -0
  289. package/skills/sprite-editor/references/api_reference.md +151 -0
  290. package/skills/sprite-editor/references/background.md +112 -0
  291. package/skills/sprite-editor/references/templates.md +72 -0
  292. package/skills/sprite-editor/scripts/AutomaticSliceTexture.cs +40 -0
  293. package/skills/sprite-editor/scripts/GenerateNewSpriteRects.cs +200 -0
  294. package/skills/sprite-editor/scripts/GetTextureSourceImageSize.cs +32 -0
  295. package/skills/sprite-editor/scripts/GetTextureToSlice.cs +55 -0
  296. package/skills/sprite-editor/scripts/GridSliceTexture.cs +40 -0
  297. package/skills/sprite-editor/scripts/IsometricSliceTexture.cs +141 -0
  298. package/skills/sprite-editor/scripts/README.md +134 -0
  299. package/skills/sprite-editor/scripts/SetPivotExample.cs +58 -0
  300. package/skills/sprite-editor/scripts/SpriteToPng.cs +88 -0
  301. package/skills/sprite-segment-3x3grid/SKILL.md +177 -0
  302. package/skills/sprite-segment-3x3grid/references/topic-map.md +37 -0
  303. package/skills/sprite-segment-3x3grid/scripts/SpriteGridAnalysis.cs +119 -0
  304. package/skills/ui/SKILL.md +142 -0
  305. package/skills/ui-imgui/SKILL.md +180 -0
  306. package/skills/ui-imgui/references/gui-elements.md +156 -0
  307. package/skills/ui-imgui/references/templates.md +141 -0
  308. package/skills/ui-ugui/SKILL.md +217 -0
  309. package/skills/ui-ugui/references/layout-and-anchors.md +61 -0
  310. package/skills/ui-ugui/references/scrollview-setup.md +29 -0
  311. package/skills/ui-uitk/SKILL.md +229 -0
  312. package/skills/ui-uitk/references/common-issues.md +74 -0
  313. package/skills/ui-uitk/references/custom-elements.md +241 -0
  314. package/skills/ui-uitk/references/painter2d.md +282 -0
  315. package/skills/ui-uitk/references/pointermanipulator-guide.md +94 -0
  316. package/skills/ui-uitk/references/svg-icons.md +136 -0
  317. package/skills/ui-uitk/references/ui-runtime-binding.md +234 -0
  318. package/skills/ui-uitk/references/uss-guide.md +138 -0
  319. package/skills/unity-cli/CHANGELOG.md +524 -0
  320. package/skills/unity-cli/SECURITY.md +68 -0
  321. package/skills/unity-cli/SKILL.md +160 -0
  322. package/skills/unity-cli/references/auth-license-cloud.md +318 -0
  323. package/skills/unity-cli/references/bootstrap-project.md +177 -0
  324. package/skills/unity-cli/references/build-run-test.md +555 -0
  325. package/skills/unity-cli/references/catalog-plugins.md +86 -0
  326. package/skills/unity-cli/references/ci-recipes.md +76 -0
  327. package/skills/unity-cli/references/cloud-automation.md +819 -0
  328. package/skills/unity-cli/references/cloud-workflows.md +19 -0
  329. package/skills/unity-cli/references/collaboration.md +533 -0
  330. package/skills/unity-cli/references/config-hub.md +239 -0
  331. package/skills/unity-cli/references/diagnostics-maintenance.md +718 -0
  332. package/skills/unity-cli/references/docs-workflow.md +179 -0
  333. package/skills/unity-cli/references/editor-targeting.md +29 -0
  334. package/skills/unity-cli/references/editors-install.md +490 -0
  335. package/skills/unity-cli/references/environment-variables.md +42 -0
  336. package/skills/unity-cli/references/exit-codes.md +47 -0
  337. package/skills/unity-cli/references/global-flags.md +32 -0
  338. package/skills/unity-cli/references/integration-advanced.md +1130 -0
  339. package/skills/unity-cli/references/notes.md +20 -0
  340. package/skills/unity-cli/references/notifications.md +39 -0
  341. package/skills/unity-cli/references/playmode-verification-loop.md +279 -0
  342. package/skills/unity-cli/references/projects-templates.md +997 -0
  343. package/skills/unity-cli/references/scene-editing-preflight.md +25 -0
  344. package/skills/unity-cli/references/setup-and-help.md +54 -0
  345. package/skills/unity-cli/references/version-control.md +612 -0
  346. package/skills/unity-package-management/SKILL.md +169 -0
  347. package/skills/unity-package-management/references/discovering-packages.md +66 -0
  348. package/skills/unity-package-management/references/headless-run.md +31 -0
  349. package/skills/unity-package-management/references/installer-script.md +92 -0
  350. package/skills/unity-package-management/references/select-packages.md +126 -0
  351. package/skills/unity-pipeline/SECURITY.md +17 -0
  352. package/skills/unity-pipeline/SKILL.md +205 -0
  353. package/skills/unity-pipeline/references/csharp-eval.md +23 -0
  354. package/skills/unity-pipeline/references/multiplayer-play-mode.md +103 -0
  355. package/skills/urp-postprocessing/SECURITY.md +16 -0
  356. package/skills/urp-postprocessing/SKILL.md +188 -0
  357. package/skills/urp-postprocessing/references/code-templates.md +150 -0
  358. package/skills/urp-postprocessing/references/effect-reference.md +86 -0
  359. package/skills/validate-urp-render-graph-renderer-feature/SKILL.md +92 -0
  360. package/skills/validate-urp-render-graph-renderer-feature/references/validation-checklist.md +179 -0
@@ -0,0 +1,1111 @@
1
+ # CloudBehaviours Reference
2
+
3
+ Server-authoritative game logic authored as a C# class, with the client bindings — RPCs, synchronized
4
+ state, push events — generated for you. Part of `com.unity.services.cloudcode`, requiring Unity 6000.5+.
5
+
6
+ Read this when the goal needs cloud-owned state that clients read but cannot write: shared match state,
7
+ per-player secret state, server-owned timers. For request/response server logic with nothing to
8
+ synchronize, plain Cloud Code modules are generally available and the better default.
9
+
10
+ ## Table of Contents
11
+
12
+ - [Prerequisites](#prerequisites)
13
+ - [Anti-Hallucination Reference](#anti-hallucination-reference)
14
+ - [The authoring model](#the-authoring-model)
15
+ - [What `public` means on the cloud class](#what-public-means-on-the-cloud-class)
16
+ - [Scopes](#scopes)
17
+ - [Deployed names](#deployed-names)
18
+ - [Generated code is not editable](#generated-code-is-not-editable)
19
+ - [Client bindings](#client-bindings)
20
+ - [Per-viewer state with `[SyncView]`](#per-viewer-state-with-syncview)
21
+ - [Calling another behaviour](#calling-another-behaviour)
22
+ - [Timers](#timers)
23
+ - [State, serialization, and model types](#state-serialization-and-model-types)
24
+ - [Endpoint access control](#endpoint-access-control)
25
+ - [Local development, debugging, and deployment](#local-development-debugging-and-deployment)
26
+ - [Generator diagnostics](#generator-diagnostics)
27
+ - [Silent failure modes](#silent-failure-modes)
28
+
29
+ ## Prerequisites
30
+
31
+ | Requirement | Detail |
32
+ | :-- | :-- |
33
+ | `com.unity.services.cloudcode` **`3.1.0-exp.1`**+ | Confirm in the project with **Create > Services > Cloud Behaviour Script** in the create menu |
34
+ | Editor is 6000.5 or newer | every CloudBehaviours assembly is gated on `UNITY_6000_5_OR_NEWER` |
35
+ | Project is linked to a Unity Cloud project | required to deploy, and to authenticate at runtime |
36
+
37
+ `Scope.MultiplayerSession` additionally needs `com.unity.services.multiplayer` installed. Player-scoped
38
+ behaviours do not. One module can hold CloudBehaviours and native Cloud Code classes side by side.
39
+
40
+ If the create-menu entry is missing, say so and do not write CloudBehaviour code: the installed version
41
+ does not carry CloudBehaviours and no amount of correct code will compile against it. Fall back to a plain
42
+ stateful module unless the user asks for the experimental package.
43
+
44
+ ## Anti-Hallucination Reference
45
+
46
+ | Correct | Incorrect (do NOT use) |
47
+ |---|---|
48
+ | `[StateScope(Scope.Player)]` / `[StateScope(Scope.MultiplayerSession)]` on the cloud class | any other class attribute to mark a behaviour -- `[StateScope]` is the only one |
49
+ | cloud class inherits `CloudBehaviour` | a plain class with only `[StateScope]` -- that is a native module, not a behaviour |
50
+ | `public int Round;`, `public int Round { get; set; }` or `public int Round { get; private set; }` -- all synchronize | a get-only or computed property -- ignored with warning `CCSG113`; expose a computed value with `[SyncView]` |
51
+ | `public` members reach clients; `private` ones never leave the cloud | assuming access control hides a `public` field, or that a `private` one is synchronized |
52
+ | `[CloudCodeService] ITimerService timers` as a method parameter | an unannotated `ITimerService` parameter -- build error `CCSG136` |
53
+ | `await self.EndTurn(arg).ScheduleAsync(delay)` on the injected `I{Behaviour}` | assembling an endpoint-name string by hand to schedule work |
54
+ | `[CloudCodeService] ITimerService` + `GetTimerAsync(id)` to cancel | expecting a cancel on `ICall` -- it has none |
55
+ | `await other.Endpoint(arg).ForScope(scopeId)` to invoke another scope instance now | `ScheduleAsync` for a cross-scope call -- it only ever targets the executing instance |
56
+ | `[CloudCodeAccess(Access.Service)]` on a timer target | leaving it callable by clients because "only the timer calls it" |
57
+ | `Access.Service` endpoints called only from behaviours, via `I{Behaviour}` | calling one from the client -- the generated client has no method for it |
58
+ | `Context.ServiceToken` inside a timer callback | `Context.PlayerId` -- it is `null`; a timer runs for no player |
59
+ | `[SyncView("Hand")] internal List<Card> ProjectHand(string playerId)` | a `private` projection -- build error `CCSG142` |
60
+ | client partial: `OnEnable` -> `EnableClient()`, `OnDisable` -> `DisableClient()` | hand-writing `EnableClient`, `Hydrate`, an RPC, or a synchronized property -- they are generated |
61
+ | client calls the generated method: `await behaviour.PlayTurn(move)` | `CallModuleEndpointAsync("Behaviour", "PlayTurn", args)` from a behaviour's client |
62
+ | `Synchronized`, `ScopeChanged`, `Fatal`, `Error`, `MessageReceived` on the generated bindings | any other event name for connection or state callbacks |
63
+ | read synchronized fields in a `ScopeChanged` handler -- the client hydrated before raising it | calling `Hydrate()` on connect or reconnect -- it already ran, and yours is a second RPC |
64
+ | `List<T>`, `T[]`, `Dictionary<,>` across the boundary | a custom `IEnumerable<T>` type -- compile-time error |
65
+ | model types carrying only fields and properties | methods or constructors on a model -- not regenerated client-side; a computed or get-only property is error `CCSG131` |
66
+ | behaviour class names unique within a module | two behaviours sharing a simple name across namespaces -- generator error |
67
+
68
+ ## Creating the module
69
+
70
+ Always create a new module with **Create > Services > Cloud Behaviour Script**, because the creator writes
71
+ the module's `.ccmu` asset along with both assemblies, and the Deployment window lists only modules that
72
+ have a `.ccmu`. A module built from hand-written asmdefs alone compiles and generates bindings, but never
73
+ appears in the Deployment window, and nothing reports why. An agent without computer-use tools asks the
74
+ user to run this menu step; with them, it runs the step itself.
75
+
76
+ The first behaviour in a folder opens the Cloud Behaviour Module Creator, which writes
77
+ `<ModuleName>.ccmu` at the module root and scaffolds two assemblies:
78
+
79
+ | Folder | Assembly references | Platform |
80
+ | :-- | :-- | :-- |
81
+ | `Cloud/` | `Unity.Services.CloudCode.Core`, `Unity.Services.CloudCode.Apis`, `Unity.Services.CloudBehaviours.Editor`, `Unity.Services.CloudCode.SourceGenerator`, `Newtonsoft.Json.dll` | Editor only, `noEngineReferences` |
82
+ | `Client/` | `Unity.Services.CloudBehaviours.Runtime`, `Unity.Services.Core`, `Unity.Services.CloudCode`, `Unity.Services.CloudCode.SourceGenerator`, **the cloud assembly**, and for `Scope.MultiplayerSession` also `Unity.Services.CloudBehaviours.Multiplayer.Runtime` + `Unity.Services.Multiplayer` | all |
83
+
84
+ Two entries in that `Client/` row are easy to get wrong, and both fail as `CCSG127` plus a wall of
85
+ `CS0103` on the client partial:
86
+
87
+ - **The client assembly must reference the cloud assembly**, even though the cloud assembly is
88
+ Editor-only. This is correct and does not break player builds: the generator emits stubs of the cloud
89
+ types into the client assembly, so nothing Editor-only is carried into a player.
90
+ - **A session-scoped behaviour needs the two multiplayer assemblies on the *client* assembly**, because
91
+ the generated client addresses its state through a `MultiplayerScopeProvider`. Installing
92
+ `com.unity.services.multiplayer` is not sufficient on its own — the references have to be there too.
93
+
94
+ Use the table to check what the creator generated. The creator also wires the cloud reference, which a
95
+ hand-written client asmdef usually misses. The split matters: the cloud assembly has no engine
96
+ references, so `UnityEngine` types are unavailable server-side, and the generator needs both assemblies to
97
+ reference it.
98
+
99
+ If the user already has hand-written assemblies and the module is missing from the Deployment window, the
100
+ module is missing its `.ccmu`. Add one instead of rebuilding the module, because a new `.ccmu` leaves every
101
+ assembly name, GUID and script reference as it is:
102
+
103
+ 1. Stop until the user confirms a rollback point, such as a commit in their version control or a backup
104
+ copy of the project, so a failed repair has a clean way back.
105
+ 2. Check the cloud asmdef against the `Cloud/` row of the table above and add any missing references. The
106
+ create menu refuses to add behaviours to a module whose cloud assembly lacks them.
107
+ 3. Write `<ModuleName>.ccmu` in the folder that holds both assembly folders, in the format and with the
108
+ GUID rules in the `.ccmu` File Format section of `references/cloud-code-modules.md`.
109
+ Take each GUID from the `guid:` line of the matching `.asmdef.meta` in this project, never from
110
+ anywhere else, because a GUID resolves only in the project that minted it.
111
+ 4. Verify: the `.ccmu` imports with zero console errors, and the module is listed in
112
+ **Services > Deployment** under the cloud asmdef's name. If either check fails, restore the rollback
113
+ point and read the console error before trying again.
114
+
115
+ ## The authoring model
116
+
117
+ What you write, and what the generator writes for you:
118
+
119
+ 1. **The creation flow scaffolds two assemblies** — a `Cloud/` assembly (Editor-only, no engine
120
+ references) and a `Client/` assembly, each referencing `Unity.Services.CloudCode.SourceGenerator`.
121
+ 2. **You implement the behaviour on the cloud side**: a class inheriting `CloudBehaviour` with a
122
+ `[StateScope]`, public methods for the operations, public fields for the state clients should see, and
123
+ private members for everything that stays server-side.
124
+ 3. **The source generators run on every compile** and emit two halves from that one class:
125
+ - the **Cloud Code module** itself — a `[CloudCodeFunction("Behaviour__Method")]` per public method,
126
+ the `Behaviour_Hydrate` endpoint, and the synchronization that pushes state to clients in scope.
127
+ This is what gets deployed and what the service runs.
128
+ - the **client bindings** — the other half of your `partial` client class, carrying the RPC methods
129
+ (except `Access.Service` endpoints, which clients cannot call),
130
+ the read-only synchronized properties and the `UnityEvent`s your game code calls and subscribes to.
131
+ 4. **Your game client uses the bindings** like any other component: enable it, call the generated
132
+ methods, react to `Synchronized` and to your own push events.
133
+
134
+ Neither generated half appears under `Assets/`; both are compiler output, regenerated on each compile.
135
+
136
+ ```csharp
137
+ // Cloud/MatchState.cs — cloud assembly, runs in Cloud Code
138
+ using System;
139
+ using System.Collections.Generic;
140
+ using Microsoft.Extensions.Logging;
141
+ using Unity.Services.CloudBehaviours;
142
+ using Unity.Services.CloudCode.Core;
143
+
144
+ [StateScope(Scope.MultiplayerSession)]
145
+ public class MatchState : CloudBehaviour
146
+ {
147
+ public int Round; // synchronized to every client in scope
148
+ private List<string> _deck; // stays in the cloud
149
+
150
+ public event Action<string> RoundEnded; // push channel
151
+
152
+ public string PlayTurn(string move) // RPC
153
+ {
154
+ Round++;
155
+ Logger.LogDebug("Round {Round}", Round); // Logger comes from the base class
156
+ RoundEnded?.Invoke(Context.PlayerId); // Context is IExecutionContext
157
+ return "ok";
158
+ }
159
+ }
160
+ ```
161
+
162
+ ```csharp
163
+ // Client/MatchStateClient.cs — client assembly
164
+ using Unity.Services.CloudBehaviours;
165
+ using UnityEngine;
166
+
167
+ [CloudBehaviour(typeof(MatchState))]
168
+ public partial class MatchStateClient : MonoBehaviour
169
+ {
170
+ protected virtual void OnEnable() => EnableClient();
171
+ protected virtual void OnDisable() => DisableClient();
172
+
173
+ async void OnMoveButton()
174
+ {
175
+ var result = await PlayTurn("rock"); // generated RPC
176
+ }
177
+ }
178
+ ```
179
+
180
+ Write only `OnEnable`/`OnDisable` and your own logic. `EnableClient`, `DisableClient`, `Hydrate`, the
181
+ RPC methods, the synchronized properties, and the `UnityEvent`s are all generated — implementing any of
182
+ them by hand collides with the generated partial.
183
+
184
+ ## What `public` means on the cloud class
185
+
186
+ Accessibility is the opt-in mechanism, which is why secrets simply stay `private`:
187
+
188
+ | Cloud class member | Becomes | On the client |
189
+ | :-- | :-- | :-- |
190
+ | `public` method | RPC | `async Task` / `async Task<T>` method |
191
+ | `public` field (non-static, non-const, non-readonly), or `public` property with a public getter and a setter of any accessibility | synchronized variable | `public T Name { get; private set; }` |
192
+ | `public event Action`, `Action<…>`, `EventHandler`, `EventHandler<T>` | push channel | `UnityEvent` / `UnityEvent<…>` |
193
+ | method with `[SyncView("name")]` | per-viewer projection | read-only property named `name` |
194
+ | parameter with `[CloudCodeService]` | injected service | omitted from every caller-facing signature — the client RPC and `I{Behaviour}` alike |
195
+
196
+ Non-public members are invisible to clients and to the generator.
197
+
198
+ Two traps live in that table:
199
+
200
+ - **A service parameter needs `[CloudCodeService]`.** `Task StartTurn(ITimerService timers)` fails the build with `CCSG136`, because a Cloud Code service or generated `I{Behaviour}` can't arrive in a request payload. Annotate every injected service.
201
+ - **Read-only members don't synchronize.** A get-only or computed property, or a `readonly` field, is ignored with warning `CCSG113`; a `public static` or `const` member is skipped with warning `CCSG158`. Declare synchronized state as a public field or a property with a setter (`{ get; private set; }` is fine), expose a computed value through a `[SyncView]` method, and mutate a collection's contents rather than reassigning a read-only one. The client still sees it as a read-only property.
202
+
203
+ ## Scopes
204
+
205
+ `[StateScope(...)]` binds the instance's state and decides who may call it.
206
+
207
+ | Attribute | State | Default access | Use for |
208
+ | :-- | :-- | :-- | :-- |
209
+ | `[StateScope(Scope.Player)]` | per player, persistent | `Access.Player` | inventory, progression, inboxes |
210
+ | `[StateScope(Scope.MultiplayerSession)]` | per session, shared by all members | `Access.SessionMember` | turn state, shared match state |
211
+
212
+ `[StateScope]` is **mandatory**. A behaviour without one is `CCSG126`, an error that aborts generation
213
+ for the whole module — there is no stateless or non-scoped CloudBehaviour. (Plain Cloud Code functions
214
+ do default to `Access.Global` when unscoped; behaviours do not work that way.) For stateless
215
+ request/response work, use a plain Cloud Code module instead.
216
+
217
+ New scripts are scaffolded as `Scope.Player`. Persistent private state → `Player`; shared volatile
218
+ state → `MultiplayerSession`. Override per endpoint with `[CloudCodeAccess(...)]` — see
219
+ [Endpoint access control](#endpoint-access-control).
220
+
221
+ ## Deployed names
222
+
223
+ The generator emits one Cloud Code function per endpoint, and these strings appear in logs, in the
224
+ Deployment window, and anywhere a function name is passed as a string (timers, for instance):
225
+
226
+ | Thing | Deployed name |
227
+ | :-- | :-- |
228
+ | RPC `Method` on behaviour `B` | `B__Method` |
229
+ | Hydration | `B_Hydrate` (single underscore) |
230
+ | State sync push | `B__Synchronize` |
231
+ | Push event `Ev` | `B__Ev` |
232
+
233
+ Two behaviours in one module may declare the same *method* name — bindings resolve by full type identity.
234
+ Their **class** names must differ, though, even across namespaces: the generated `{module}Extensions`
235
+ aggregator derives `Add{Name}Client` and short-name type references from the simple name, so duplicates
236
+ are reported as an error.
237
+
238
+ Names must also be unique *within* a behaviour, across kinds. An endpoint, a synchronized member, an
239
+ event, and a `[SyncView]` projection all share one wire key namespace, and the comparison is
240
+ case-insensitive, so `Score` and `score` collide. Overloads collide for the same reason — one wire key,
241
+ two methods. A same-kind duplicate keeps the first declaration with warning `CCSG117`, which shows up as a member
242
+ that mysteriously does not exist on the client. Members of different kinds sharing a name have no
243
+ survivor, so all of them are dropped and the build fails with error `CCSG118`. Event payloads are
244
+ limited to four parameters.
245
+
246
+ ## Generated code is not editable
247
+
248
+ The generator runs on every compilation and emits sources the compiler holds in memory — they never
249
+ appear under `Assets/` and are not checked in. To regenerate, recompile. When generated code looks
250
+ wrong, fix the cloud class or the bindings class; there is nothing to edit in the output.
251
+
252
+ ### Reading the generated sources
253
+
254
+ Not editable is not the same as not inspectable, and reading them is often the fastest way to close the
255
+ loop on "the client says that member does not exist". Roslyn can dump every generator's output to disk,
256
+ but the setup has three traps, so follow it exactly.
257
+
258
+ **1. Put the `csc.rsp` next to the `.asmdef`, never at the `Assets/` root.** A root `csc.rsp` is applied
259
+ to every assembly in the project, packages included. Roslyn names output files by generator, not by the
260
+ assembly being compiled, so dozens of parallel compiles write the same paths and fail with `CS0016`. Add
261
+ one file per assembly you want to read, each naming its own output folder:
262
+
263
+ ```
264
+ # Assets/MyGame/Client/csc.rsp (beside MyGame.Client.asmdef)
265
+ /generatedfilesout:Generated/MyGame.Client
266
+ ```
267
+
268
+ **2. Create that folder before you compile.** The path is relative to the project root, not `Assets/`.
269
+ Neither Unity nor Roslyn creates it; Roslyn only creates the per-generator subfolders beneath it. Without
270
+ it, every assembly using the rsp fails with:
271
+
272
+ ```
273
+ error CS0016: Could not write to output file '<project>\Generated\...\AssemblyMonoScriptTypes.generated.cs'
274
+ -- 'Could not find a part of the path ...'
275
+ ```
276
+
277
+ The message names a generated file, so it reads like a generator bug. It is a missing folder. If the rsp
278
+ was global, `Unity.Pipeline.CodeGen` is among the casualties and `unity status` reports no Pipeline
279
+ instance while the Editor is open in front of you; delete the rsp to recover.
280
+
281
+ **3. Keep the folder outside `Assets/`.** Unity would import the dumped `.cs` files and compile them again.
282
+
283
+ Recompile, and the output lands under the folder you named:
284
+
285
+ ```
286
+ Generated/
287
+ MyGame.Client/
288
+ CloudCodeSourceGenerator/
289
+ SourceGenerator.Generators.BehaviourBindingsGenerator/ <-- client partial + its inspector
290
+ MyGame.Client.MatchStateClient.g.cs
291
+ MyGame.Client.MatchStateClient_Inspector.g.cs
292
+ MyGame.Client.MatchState_Sync.g.cs
293
+ MyGame.Cloud/
294
+ CloudCodeSourceGenerator/
295
+ SourceGenerator.Generators.BehaviourManifestGenerator/ <-- the deployed cloud module
296
+ MyGame.Cloud.IMatchState.g.cs
297
+ MyGame.Cloud.MatchState_System.g.cs
298
+ MyGame.Cloud.MatchState_Sync.g.cs
299
+ MyGame.Cloud.__MatchStateCcClient.g.cs
300
+ MyGameExtensions.g.cs
301
+ ```
302
+
303
+ The file name is the hint name — `{Namespace}.{Type}.g.cs` — so a member you expected on the client is
304
+ either in `…MatchStateClient.g.cs` or it was never generated, which is the whole diagnosis. Read the
305
+ bindings file to see what the client actually got, and the manifest file to see the endpoint names and
306
+ the `_Sync` payload the cloud will push.
307
+
308
+ This is build output: it is rewritten on every compile, editing it changes nothing, and it does not
309
+ belong in source control. It is also a trap for everyone else: if the `csc.rsp` is committed and
310
+ `Generated/` is ignored, the folder does not exist on a fresh clone and the project fails to compile for
311
+ anyone but its author. Either keep the `csc.rsp` files uncommitted, or commit a `.gitkeep` in each output
312
+ folder and ignore only the contents:
313
+
314
+ ```
315
+ Generated/**/*.cs
316
+ ```
317
+
318
+ ## Client bindings
319
+
320
+ ### What the generator emits
321
+
322
+ For `[CloudBehaviour(typeof(TCloud))] public partial class MyClient : MonoBehaviour`, the generated half
323
+ of the partial contains:
324
+
325
+ ```csharp
326
+ [field: SerializeField] internal string TargetSessionType { get; private set; } // MultiplayerSession scope — or:
327
+ [field: SerializeField] internal string TargetPlayerId { get; private set; } // Player scope
328
+
329
+ public CloudCodeScope Scope { get; } // the scope provider's current scope
330
+ public bool IsClientEnabled { get; }
331
+
332
+ public UnityEvent ScopeChanged;
333
+ public UnityEvent<CloudBehaviourException> Fatal;
334
+ public UnityEvent<CloudBehaviourError> Error;
335
+ public UnityEvent<IMessageReceivedEvent> MessageReceived;
336
+ public UnityEvent Synchronized;
337
+
338
+ public void EnableClient();
339
+ // MultiplayerSession scope also: EnableClient(string sessionType), EnableClient(ISession),
340
+ // EnableClient(SessionObserver), EnableClient(MultiplayerSession)
341
+ // Player scope also: EnableClient(string playerId)
342
+ public void DisableClient();
343
+ public async Task Hydrate();
344
+
345
+ // one read-only property per synchronized member: public int Round { get; private set; }
346
+ // one UnityEvent per push channel: public UnityEvent<string> RoundEnded;
347
+ // one async method per client-callable RPC: public async Task<string> PlayTurn(string move);
348
+ ```
349
+
350
+ The binding creates a `PlayerScopeProvider` or `MultiplayerScopeProvider` according to the cloud
351
+ class's `[StateScope]`, so the common case needs no wiring. The provider isn't exposed; choose what to
352
+ bind to with the `EnableClient` overload instead.
353
+
354
+ ### Lifecycle
355
+
356
+ ```csharp
357
+ protected virtual void OnEnable() => EnableClient();
358
+ protected virtual void OnDisable() => DisableClient();
359
+ ```
360
+
361
+ `EnableClient()` subscribes to the provider and starts resolving the scope; `DisableClient()` tears that
362
+ down. Calling any `EnableClient` overload twice without an intervening `DisableClient()` logs a warning and
363
+ does nothing. To rebind to a different target, call `DisableClient()` first.
364
+
365
+ RPCs need a resolved scope. Rather than firing calls immediately after enabling, drive them from user
366
+ input or from `ScopeChanged`, which raises once the provider has a scope and the client has tried to
367
+ hydrate against it. Gate state-dependent work on `Synchronized`, or on the absence of
368
+ `HydrationFailedOnConnect` (see [Hydration](#hydration)), because `ScopeChanged` also follows a failed hydrate. `Scope` itself goes non-null earlier — the moment the provider resolves — so polling
369
+ `Scope != null` hands you a scope whose synchronized fields are not populated yet.
370
+
371
+ ### Choosing what to bind to
372
+
373
+ `TargetSessionType` and `TargetPlayerId` are `internal` properties with a serialized backing field, so
374
+ they are editable in the Inspector (**Target Session Type**, **Target Player ID**) but not settable from
375
+ other code. Leaving them empty is the useful default:
376
+
377
+ | Scope | Target | Empty means | Set means |
378
+ | :-- | :-- | :-- | :-- |
379
+ | `MultiplayerSession` | `TargetSessionType` | adopt the single active session; if more than one is active the binding reports `CloudBehaviourExceptionReason.AmbiguousSessions` | watch only for sessions of that type — must match `SessionOptions.Type` used at create/join/matchmaking |
380
+ | `Player` / none | `TargetPlayerId` | follow the signed-in player | bind to that specific player id |
381
+
382
+ To choose the target from code, call the matching overload instead: `EnableClient(sessionType)`,
383
+ `EnableClient(ISession)`, `EnableClient(SessionObserver)` or `EnableClient(MultiplayerSession)` for
384
+ session scope, and `EnableClient(playerId)` for player scope. An overload replaces any target set in the
385
+ Inspector.
386
+
387
+ ### Reacting to state and events
388
+
389
+ Synchronized members arrive as read-only properties; `Synchronized` raises after each applied update
390
+ (an RPC return, an incoming push, or the hydrate that runs when the scope resolves). Refreshing UI
391
+ from `Synchronized` decouples the view from individual RPCs:
392
+
393
+ ```csharp
394
+ protected virtual void OnEnable()
395
+ {
396
+ EnableClient();
397
+ Synchronized.AddListener(RefreshUi);
398
+ RoundEnded.AddListener(OnRoundEnded);
399
+ }
400
+
401
+ protected virtual void OnDisable()
402
+ {
403
+ Synchronized.RemoveListener(RefreshUi);
404
+ RoundEnded.RemoveListener(OnRoundEnded);
405
+ DisableClient();
406
+ }
407
+
408
+ void OnRoundEnded(string playerId) => Debug.Log($"round ended by {playerId}");
409
+ ```
410
+
411
+ Remove runtime listeners in `OnDisable`: `UnityEvent` keeps them across disable and enable, so re-enabling would add duplicates.
412
+
413
+ Because these are `UnityEvent`s, listeners can also be wired in the Inspector.
414
+
415
+ ### Hydration
416
+
417
+ A client hydrates itself when its scope resolves: it pulls current state through the `B_Hydrate`
418
+ endpoint, applies it, and only then raises `ScopeChanged`, with `Synchronized` immediately after. A
419
+ `ScopeChanged` handler can therefore read synchronized fields directly — they are already current,
420
+ provided the hydrate succeeded (see below) — and a client that connects and does nothing else still holds server state.
421
+
422
+ If that first hydrate fails, `Error` is raised with `HydrationFailedOnConnect` *before*
423
+ `ScopeChanged`; `ScopeChanged` still follows, no `Synchronized` does, and the fields keep their
424
+ previous or default values. An `Error` handler therefore runs on a client that has not yet announced
425
+ its scope — `Scope` is already set, so calling `Hydrate()` from that handler is legal.
426
+
427
+ Every RPC return also carries the caller's fresh view of synchronized state, so a client that calls
428
+ an RPC does not need to hydrate either.
429
+
430
+ That leaves `Hydrate()` for two cases: catching up inside a live scope after a period of not
431
+ listening, and recovering from `HydrationFailedOnConnect`. A reconnect is not one of them — it
432
+ re-resolves the scope, which hydrates.
433
+
434
+ Connecting costs one hydrate RPC per scope resolution, per client. A pooled client toggled
435
+ repeatedly, or many behaviours on one player, pays it each time.
436
+
437
+ ### Failures
438
+
439
+ Two events, split by whether the client survives.
440
+
441
+ `Fatal` carries a `CloudBehaviourException` whose `Reason` is one of `Unknown`, `SubscriptionError`,
442
+ `Kicked`, or `AmbiguousSessions`. The client is **already disabled** by the time handlers run —
443
+ `IsClientEnabled` is false and nothing further arrives until `EnableClient()` is called again. Do not
444
+ call it from the handler: a failure that reproduces immediately would recurse. `AmbiguousSessions` in
445
+ particular means the project has multiple active sessions and the binding cannot guess — set
446
+ **Target Session Type** in the Inspector, or call `EnableClient(sessionType)`.
447
+
448
+ `Error` carries a `CloudBehaviourError` and reports a failure the client **carries on from**: it keeps
449
+ its `Scope` and stays enabled. Read `Reason` to decide whether to retry. Today the only value besides
450
+ `Unknown` is `HydrationFailedOnConnect` — the client could not fetch server state when its scope
451
+ resolved, so synchronized fields still hold their previous or default values. Call `Hydrate()` to try
452
+ again.
453
+
454
+ ```csharp
455
+ Fatal.AddListener(e => Debug.LogError($"client stopped: {e.Reason}"));
456
+ Error.AddListener(e =>
457
+ {
458
+ if (e.Reason == CloudBehaviourErrorReason.HydrationFailedOnConnect)
459
+ _ = Hydrate();
460
+ });
461
+ ```
462
+
463
+ Both also log to the console, so an unhandled failure is not silent.
464
+
465
+ ## Per-viewer state with `[SyncView]`
466
+
467
+ A plain synchronized field sends the same value to everyone in scope. When each player must see a
468
+ *different* value — their own hand, their own fog of war, their own private balance — express it as a
469
+ projection instead.
470
+
471
+ `[SyncView("wireName")]` marks a method as the synced member. The method **is** the state on the wire:
472
+ the generator emits one member named `wireName`, typed as the method's return type, and fills it per
473
+ recipient by calling the method with that player's id.
474
+
475
+ ```csharp
476
+ [StateScope(Scope.MultiplayerSession)]
477
+ public class CardGame : CloudBehaviour
478
+ {
479
+ [CloudCodeSerializeProperty]
480
+ private Dictionary<string, List<Card>> _hands = new(); // every hand: persisted, cloud-only
481
+
482
+ [SyncView("Hand")]
483
+ public List<Card> ProjectHand(string playerId) =>
484
+ _hands.TryGetValue(playerId, out var hand) ? hand : new List<Card>();
485
+ }
486
+ ```
487
+
488
+ The client sees `public List<Card> Hand { get; private set; }` holding only that player's cards. The
489
+ unfiltered dictionary never leaves the cloud, which is the point: filtering client-side would ship every
490
+ hand to every player.
491
+
492
+ ### Requirements
493
+
494
+ - Signature is `TWire Method(string playerId)` — one `string` parameter, non-`void` return.
495
+ - The method must be `public` or `internal`. A `private` projection is build error `CCSG142`, so
496
+ `internal` is the tidy choice: visible to the generator, invisible outside the assembly. A `[SyncView]` method never becomes an RPC even when public, because
497
+ projections are partitioned out before endpoints are collected.
498
+ - It must be synchronous and non-generic: `async`, `Task`-returning, generic or `static` methods are not
499
+ captured.
500
+ - The `name` argument is required and non-empty; it is the member name clients see, so it should read
501
+ like a field (`"Hand"`, not `"ProjectHand"`).
502
+ - The `playerId` passed in is always the id of the player the payload is being built for — the viewer
503
+ about to receive this value, not whoever triggered the call. On an RPC return that is the caller, on a
504
+ push it is each recipient in turn, and on `Hydrate()` it is the player asking. Build the value for
505
+ *that* player and nothing else.
506
+ - When the invocation has no calling player — a timer callback, or a cross-behaviour call — the
507
+ **return value** carries an empty envelope rather than calling projections with an empty id. This is
508
+ the RPC return only. Pushes are unaffected: they are built per recipient, so each one still has a real
509
+ player id and projections resolve normally. See
510
+ [What a timer callback still pushes](#what-a-timer-callback-still-pushes).
511
+ - Keep the method side-effect free. It runs once per recipient on every synchronization, so mutating
512
+ state there produces surprising repeated writes.
513
+ - The projection runs for the calling player's own view as well, so a caller sees their projected value
514
+ in the RPC return, not the raw state.
515
+
516
+ ### Sending to specific players
517
+
518
+ Per-viewer control applies to synchronized state. A declared event is pushed to everyone in scope, and a
519
+ check inside the method decides only whether the event fires, not who receives it. When only some players
520
+ should hear about something, push to each of them with
521
+ `Push.SendPlayerMessageAsync(Context, message, messageType, playerId)` instead of invoking an event.
522
+
523
+ ## Calling another behaviour
524
+
525
+ The generator emits an `I{Behaviour}` interface for every behaviour in the module, with one member per
526
+ endpoint. A member does not invoke anything — it returns an unexecuted **`ICall`** (or `ICall<T>` when
527
+ the endpoint returns a value) describing the invocation. Inject the interface with `[CloudCodeService]`,
528
+ name the endpoint with its real arguments, then choose how to run it:
529
+
530
+ | Execution mode | Runs | Against | Returns |
531
+ |---|---|---|---|
532
+ | `ScheduleAsync(delay)` | later, after `delay` | the scope instance that is **executing** | `Task<string>` — the timer's id |
533
+ | `ForScope(scopeId)` | immediately | another instance of the **target's** scope | `Task`, or `Task<T>` carrying the endpoint's return value |
534
+
535
+ The two are alternatives. A described call handed to neither fails the build with `CCSG133`.
536
+
537
+ Use `ScheduleAsync` for timers ([Timers](#timers)). Use `ForScope` for everything else: one behaviour
538
+ invoking another right now, including across different scopes.
539
+
540
+ > **Do not call `ScheduleAsync` on another behaviour's `I{Behaviour}`**: it fails the build with `CCSG132`.
541
+ > `ScheduleAsync` is only for deferring **your own** behaviour's endpoints.
542
+ >
543
+ > To defer work against another behaviour, schedule one of your own endpoints and do the cross-scope
544
+ > call from inside it:
545
+ >
546
+ > ```csharp
547
+ > // in MatchRoom
548
+ > public async Task EndMatchSoon([CloudCodeService] IMatchRoom self) =>
549
+ > await self.FlushScores().ScheduleAsync(TimeSpan.FromSeconds(30)); // own endpoint: fine
550
+ >
551
+ > [CloudCodeAccess(Access.Service)]
552
+ > public async Task FlushScores([CloudCodeService] ILeaderboard leaderboard)
553
+ > {
554
+ > foreach (var entry in _scores)
555
+ > await leaderboard.SubmitScore(entry.Value).ForScope(entry.Key); // cross-scope: ForScope
556
+ > }
557
+ > ```
558
+
559
+ ### Cross-scope invocation with `ForScope`
560
+
561
+ `ForScope(scopeId)` targets a specific instance of the callee's scope. The `scopeId` is a player id or a
562
+ multiplayer session id, matching the target behaviour's own `[StateScope]` — not the caller's.
563
+
564
+ ```csharp
565
+ using System.Collections.Generic;
566
+ using System.Threading.Tasks;
567
+ using Unity.Services.CloudBehaviours;
568
+ using Unity.Services.CloudCode.Core;
569
+
570
+ [StateScope(Scope.Player)]
571
+ public class Leaderboard : CloudBehaviour
572
+ {
573
+ public int BestScore;
574
+
575
+ [CloudCodeAccess(Access.Service)] // no client method is generated; a behaviour can call it
576
+ public int SubmitScore(int score)
577
+ {
578
+ if (score > BestScore) BestScore = score;
579
+ return BestScore;
580
+ }
581
+ }
582
+
583
+ [StateScope(Scope.MultiplayerSession)]
584
+ public class MatchRoom : CloudBehaviour
585
+ {
586
+ [CloudCodeSerializeProperty] private Dictionary<string, int> _scores = new();
587
+
588
+ [CloudCodeAccess(Access.SessionHost)]
589
+ public async Task EndMatch([CloudCodeService] ILeaderboard leaderboard)
590
+ {
591
+ foreach (var entry in _scores)
592
+ {
593
+ // entry.Key is a player id: the scope id of that player's Leaderboard instance
594
+ var best = await leaderboard.SubmitScore(entry.Value).ForScope(entry.Key);
595
+ Logger.LogInformation($"{entry.Key} best is now {best}");
596
+ }
597
+ }
598
+ }
599
+ ```
600
+
601
+ `MatchRoom` is session-scoped and `Leaderboard` is player-scoped, so each call names the player whose
602
+ instance it should land on. Awaiting an `ICall<T>.ForScope(...)` gives that endpoint's return value
603
+ directly — the callee's synchronized members are not part of what the caller receives.
604
+
605
+ ### Requirements
606
+
607
+ - **Both behaviours must live in the same module** (the same cloud assembly). `I{Behaviour}` and the
608
+ `{module}Extensions` registrations are generated per module. If the target is in another module, this
609
+ route does not exist — call it as a plain Cloud Code module instead.
610
+ - **`[CloudCodeService]` on the injected parameter is mandatory.** Without it the build fails with
611
+ `CCSG136`.
612
+ - **Injected parameters are not part of the interface.** The generator strips every
613
+ `[CloudCodeService]` parameter from `I{Behaviour}`, exactly as it does from the client bindings, so
614
+ name only the endpoint's real arguments. Do not pass `null` as a placeholder for an injected slot —
615
+ there is no slot. An endpoint declared
616
+ `Task FlushScores(int round, [CloudCodeService] ILeaderboard leaderboard)` is called as
617
+ `self.FlushScores(round)`.
618
+ - **Lock the target down.** A callable endpoint is callable by clients too. Anything only a behaviour
619
+ should invoke wants `[CloudCodeAccess(Access.Service)]` — a cross-behaviour call arrives as a service
620
+ call, so the restriction blocks clients while letting the caller through, and the generated client has
621
+ no method for it. See
622
+ [Endpoint access control](#endpoint-access-control).
623
+ - **Expect no calling player.** A service invocation has no player behind it: `Context.PlayerId` is
624
+ `null` and there is no player access token. Pass what the endpoint needs as arguments, and use
625
+ `Context.ServiceToken` for outbound service calls.
626
+ - **Class names must be unique within the module**, even across namespaces.
627
+
628
+ ## Timers
629
+
630
+ Timers are server-authoritative: the cloud schedules a call back into one of the behaviour's own
631
+ endpoints, so a player who closes the game cannot dodge a turn clock.
632
+
633
+ Scheduling uses the same generated `I{Behaviour}` interface described in
634
+ [Calling another behaviour](#calling-another-behaviour): inject it, name the endpoint with its real
635
+ arguments, and run the resulting `ICall` with `ScheduleAsync(delay)`. That defers the invocation on the
636
+ scope instance that is executing.
637
+
638
+ ```csharp
639
+ using System;
640
+ using System.Threading.Tasks;
641
+ using Unity.Services.CloudBehaviours;
642
+ using Unity.Services.CloudCode.Core;
643
+
644
+ [StateScope(Scope.MultiplayerSession)]
645
+ public class TurnClock : CloudBehaviour
646
+ {
647
+ public string CurrentTimerId; // persisted, so it survives to the next call
648
+ public int TurnNumber;
649
+
650
+ [CloudCodeAccess(Access.SessionHost)]
651
+ public async Task StartTurn([CloudCodeService] ITurnClock self)
652
+ {
653
+ CurrentTimerId = await self.EndTurn(TurnNumber).ScheduleAsync(TimeSpan.FromSeconds(30));
654
+ }
655
+
656
+ [CloudCodeAccess(Access.Service)]
657
+ public void EndTurn(int turnNumber) // the scheduled endpoint
658
+ {
659
+ if (turnNumber != TurnNumber) return; // a stale timer fires against an old turn
660
+ TurnNumber++;
661
+ }
662
+
663
+ [CloudCodeAccess(Access.SessionHost)]
664
+ public async Task CancelTurn([CloudCodeService] ITimerService timers)
665
+ {
666
+ var timer = await timers.GetTimerAsync(CurrentTimerId);
667
+ timer.Cancel();
668
+ }
669
+ }
670
+ ```
671
+
672
+ Arguments are ordinary typed parameters on the endpoint — no dictionary of loosely-typed values, and no
673
+ function-name string to assemble. Under the hood `ScheduleAsync` registers a timer against the deployed
674
+ endpoint for you.
675
+
676
+ ### Cancelling
677
+
678
+ `ScheduleAsync` returns the timer's id. `ICall` has no cancel of its own, so keep injecting
679
+ `[CloudCodeService] ITimerService` for that: `GetTimerAsync(id)` returns the timer, which exposes
680
+ `RemainingTime()` and `Cancel()`. Persist the id if you will ever need it — a later invocation gets a
681
+ fresh instance.
682
+
683
+ ### Lock the target down, and don't expect a player
684
+
685
+ A scheduled endpoint is still an endpoint, so a client can call it directly. Restrict it with
686
+ `[CloudCodeAccess(Access.Service)]`: a timed invocation arrives as a service call, so the timer still
687
+ fires while clients are blocked. The generated client has no method for it, so a stray client call is a
688
+ compile error.
689
+
690
+ That same identity difference shapes what the endpoint can assume when a timer runs it:
691
+
692
+ - `Context.PlayerId` is `null` — the invocation is not on behalf of anyone, so branching on the "calling
693
+ player" does not work. Pass what you need as arguments, or read it from state.
694
+ - there is no player access token; use `Context.ServiceToken` for outbound service calls.
695
+ - session data is refreshed before it runs, so `Context.Session.PlayerIds` reflects who is in the session
696
+ at that moment — handy for skipping players who left.
697
+
698
+ ### What a timer callback still pushes
699
+
700
+ A timer runs for no player, but clients still see what it changes. A timer-triggered invocation pushes
701
+ to the same recipients as any other call: every session member for `Scope.MultiplayerSession`, and the
702
+ owner for `Scope.Player`.
703
+
704
+ - **Synchronized state is pushed** to each recipient, with each payload built against that recipient's
705
+ id, so `[SyncView]` projections resolve per viewer as they do on a client call. The service fills
706
+ `Session.PlayerIds` for a session-scoped behaviour whatever the endpoint's access level, so a
707
+ service-token invocation still reaches every member.
708
+ - **Declared events are pushed** to the same recipients, carrying a full synchronization envelope.
709
+ - **Only the RPC return value is empty**, and only when the behaviour has projections. Nothing reads the
710
+ return of a timer invocation anyway.
711
+
712
+ A value that fails to arrive after a timer fired is not explained by the timer.
713
+
714
+ ### Limits and patterns
715
+
716
+ - The delay must be **greater than zero**.
717
+ - Each scope instance (one player, or one session) may hold at most **10 active timers**; registering
718
+ an eleventh throws. Cancelling frees the slot, so cancel the ones you no longer need rather than
719
+ letting them pile up.
720
+ - The delays of all active timers on a scope instance may add up to at most **24 hours** in
721
+ production, so no single timer can be longer than that.
722
+ - There is no recurring timer. For a repeating clock, have the scheduled endpoint schedule the next one,
723
+ keeping exactly one alive at a time.
724
+ - **Only schedule your own behaviour's endpoints.** Calling `ScheduleAsync` on another behaviour's
725
+ `I{Behaviour}` fails the build with `CCSG132`. See [Calling another behaviour](#calling-another-behaviour) for the
726
+ pattern that works.
727
+
728
+ ## State, serialization, and model types
729
+
730
+ ### Two separate rules: sync and persistence
731
+
732
+ Whether a member reaches clients and whether it survives to the next invocation are decided
733
+ independently:
734
+
735
+ - **Reaches clients** — determined purely by `public` accessibility.
736
+ - **Persists between invocations** — public members persist by default; two attributes override it, and
737
+ both apply to fields and properties:
738
+
739
+ | Attribute | Effect |
740
+ | :-- | :-- |
741
+ | `[CloudCodeSerializeProperty]` | persists a non-public member — durable state the client never sees |
742
+ | `[CloudCodeIgnorePropertyAttribute]` (`[CloudCodeIgnoreProperty]`) | keeps a public member out of persistence while it still synchronizes |
743
+
744
+ ```csharp
745
+ [CloudCodeSerializeProperty]
746
+ private string _correctAnswer; // survives invocations, never sent to clients
747
+
748
+ [CloudCodeIgnoreProperty]
749
+ public int FramesSinceStart; // synced for display, not worth persisting
750
+ ```
751
+
752
+ Only serialized members survive: the runtime may rehydrate the instance between calls, so anything held
753
+ in a plain private field is gone by the next invocation.
754
+
755
+ Custom serialization with `IStateSerializer` isn't supported on a CloudBehaviour yet, and implementing it
756
+ is error `CCSG157`. Don't suggest it for state migration; use the attributes above and keep saved members
757
+ backward compatible.
758
+
759
+ A persisted data type can't hide an inherited member unless both are properties of the same type. The
760
+ server can't save two members with one name, so that pair is error `CCSG156`. Rename one, or add
761
+ `[CloudCodeIgnoreProperty]` to the one that doesn't need saving.
762
+
763
+ `CCSG156` can't see non-public members of a type compiled in another assembly, such as a shared asmdef.
764
+ There, an `internal` or `private` member that hides a public one isn't reported, and every save of the
765
+ behaviour fails at runtime. Don't hide inherited members in shared data types.
766
+
767
+ Synchronized state is a public field or a public property with a public getter and a setter of any
768
+ accessibility, the same accessor shapes that persist. A `readonly` field or a get-only or computed property is
769
+ ignored with warning `CCSG113`; use a `[SyncView]` method for a computed value. A `public static` or
770
+ `const` member is skipped with warning `CCSG158`. Data **types** crossing the
771
+ boundary are stricter: a member that can be read but not assigned there is error `CCSG131`.
772
+ `[CloudCodeSerializeProperty]` and `[CloudCodeIgnoreProperty]` still apply to both fields and properties
773
+ for persistence purposes.
774
+
775
+ ### Manual synchronization
776
+
777
+ `AutoSynchronize` is `true` by default. After each endpoint returns, Cloud Code pushes the updated
778
+ synchronized state to the other clients in the scope, once per call however many fields changed, so a
779
+ single endpoint never needs to turn it off.
780
+
781
+ Turn it off to batch changes from several calls into one push. `AutoSynchronize` is saved with the
782
+ behaviour's state, so setting it to `false` keeps it off for every later call in that scope, from any
783
+ player, until an endpoint sets it back to `true`. End the batch by setting it to `true` in the last call;
784
+ that call's normal push then carries everything:
785
+
786
+ ```csharp
787
+ public void BeginPlacement()
788
+ {
789
+ AutoSynchronize = false; // later calls change state without pushing
790
+ }
791
+
792
+ public void PlaceShip(int x, int y)
793
+ {
794
+ Ships.Add(new Ship { X = x, Y = y });
795
+ }
796
+
797
+ public void LockPlacement()
798
+ {
799
+ Locked = true;
800
+ AutoSynchronize = true; // this call's push carries the whole batch
801
+ }
802
+ ```
803
+
804
+ Rules:
805
+
806
+ - Write the endpoint that sets `AutoSynchronize` back to `true` in the same change that sets it to
807
+ `false`. Left off, state still saves and each caller still gets it in the RPC return, but no other
808
+ client receives a synchronization push, so the game looks frozen to everyone else.
809
+ - Events still push during a batch, and each one carries the full synchronized state, so raising an
810
+ event mid-batch sends the batch's changes early.
811
+ - Re-enable it on paths that abandon the batch too, such as a player leaving mid-placement or a round
812
+ timing out. The endpoint that starts the next round, or a timer callback, can do it.
813
+ - To push partway through a call, for example before a slow step, call `SynchronizeAsync()` and leave
814
+ `AutoSynchronize` on. The end-of-call push still follows.
815
+ - Don't initialise the field to `false`. That makes the behaviour fully manual, and every endpoint that
816
+ should reach other clients has to call `SynchronizeAsync()` itself.
817
+
818
+ The calling client always receives current state in the RPC return, whatever `AutoSynchronize` is set to.
819
+
820
+ Risks:
821
+
822
+ - The flag belongs to the scope instance, not to the call or the player. One player's endpoint turning it
823
+ off stops the end-of-call push of every endpoint of that behaviour, for every player in the scope, and it
824
+ stays off across restarts of the scope because it is saved with the rest of the state. Only events and
825
+ explicit `SynchronizeAsync()` calls still push.
826
+ - Nothing warns when it is left off. The symptom is other clients no longer receiving updates from
827
+ endpoints unrelated to the batch, while each caller still sees current state in its own RPC return. When
828
+ pushes stop, look for an endpoint that set `AutoSynchronize = false` and a path that never set it back.
829
+ - Read-only endpoints cannot opt out of synchronization. An endpoint that changes nothing still pushes the
830
+ full synchronized state to every push recipient other than the caller: every other member of a
831
+ `Scope.MultiplayerSession`, or the owner of a `Scope.Player` scope when someone else calls it. An owner
832
+ calling its own player scope sends nothing. Don't turn `AutoSynchronize` off to avoid the push: it is the
833
+ same permanent switch. Accept it, and keep read-only session endpoints off hot paths, for example by
834
+ rate-limiting how often the client calls them.
835
+
836
+ Who receives the push depends on scope. For `Scope.MultiplayerSession` the synchronization message goes
837
+ to `Context.Session.PlayerIds`, so session members see each other's changes without calling anything.
838
+ The caller is left out because the RPC return already gave it the state.
839
+
840
+ A player-scoped behaviour's only client is its owner, so every push goes to the owner. When the owner
841
+ calls, the RPC return carries the state, so no synchronization push is sent, and events still reach the
842
+ owner. When a timer, a service call or another player's `Global` call changes the state, the owner gets
843
+ the synchronization push and the events. A caller who isn't the owner gets only the RPC return. Don't
844
+ design a player-scoped behaviour as though other clients will be pushed to.
845
+
846
+ For a raw push outside synchronization, `SendMessageAsync(messageType, payload)` sends to every session
847
+ member for `Scope.MultiplayerSession`, and to the owner for `Scope.Player`. Declared events are usually
848
+ clearer than raw messages.
849
+
850
+ ### Model types
851
+
852
+ Custom classes, structs, and enums referenced from public fields, properties, endpoint parameters/returns,
853
+ or event payloads are regenerated in the client bindings assembly. The regenerated version carries the
854
+ public fields and properties (as `{ get; set; }`) and enum members — nothing else.
855
+
856
+ ```csharp
857
+ // Good — data only
858
+ public class AttackResult
859
+ {
860
+ public string TargetPlayerId;
861
+ public int X;
862
+ public bool WasHit;
863
+ }
864
+
865
+ // Compiles, but the method is missing on the client
866
+ public class AttackResult
867
+ {
868
+ public bool WasHit;
869
+ public bool IsSunk() => /* ... */; // methods aren't regenerated
870
+ }
871
+
872
+ // Fails the build with CCSG131
873
+ public class AttackResult
874
+ {
875
+ public bool WasHit;
876
+ public string Summary => $"{WasHit}"; // readable but not assignable
877
+ }
878
+ ```
879
+
880
+ A method is only dropped from the client stub. A member that can be read but not assigned, such as a
881
+ computed or get-only property, can't round-trip, so the build fails with `CCSG131`.
882
+
883
+ Nested types referenced by a model are generated recursively and stay nested inside their containing
884
+ type. Enum bindings preserve the underlying type (`byte`, `long`, …), so values outside `int` range are
885
+ safe. Types that share a simple name across different namespaces are generated separately.
886
+
887
+ A model type cannot inherit from a `[CloudCodeService]` type — service objects hold server-only state
888
+ that cannot be serialized.
889
+
890
+ ### Unsupported types
891
+
892
+ A custom collection type — anything implementing `IEnumerable<T>` that is not `List<T>`, an array, or
893
+ `Dictionary<,>` — is a **compile-time error** when used as an endpoint parameter, an endpoint return, or a
894
+ data-type field, because it cannot round-trip through JSON. Use `List<T>` or `T[]`.
895
+
896
+ Everything crossing the boundary must be JSON-serializable: primitives, `string`, enums, plain
897
+ classes/structs, and `List<T>`/arrays/dictionaries of those.
898
+
899
+ ## Endpoint access control
900
+
901
+ Every CloudBehaviour endpoint has an access level. Left alone it is derived from the class scope, which
902
+ is the right answer most of the time:
903
+
904
+ | Class scope | Resolved access |
905
+ | :-- | :-- |
906
+ | `[StateScope(Scope.Player)]` | `Access.Player` |
907
+ | `[StateScope(Scope.MultiplayerSession)]` | `Access.SessionMember` |
908
+
909
+ Resolution happens when the module loads, so the attribute is only needed where an endpoint should differ
910
+ from its class default.
911
+
912
+ ```csharp
913
+ using Unity.Services.CloudBehaviours;
914
+ using Unity.Services.CloudCode.Core;
915
+
916
+ [StateScope(Scope.MultiplayerSession)]
917
+ public class Match : CloudBehaviour
918
+ {
919
+ public void SubmitMove(string move) { } // Access.SessionMember (default)
920
+
921
+ [CloudCodeAccess(Access.SessionHost)]
922
+ public void StartMatch() { } // only the session host
923
+
924
+ [CloudCodeAccess(Access.Service)]
925
+ public void GrantReward(string playerId, int amount) { } // only service-token callers
926
+ }
927
+ ```
928
+
929
+ An `Access.Service` endpoint gets no method on the generated client binding, so calling it from game
930
+ code is a compile error rather than a runtime `403`. Behaviours still reach it through `I{Behaviour}`
931
+ (`ScheduleAsync`, `ForScope`).
932
+
933
+ ### The `Access` enum
934
+
935
+ Which values are legal depends on the class's `[StateScope]`, so the enum groups by scope:
936
+
937
+ | Valid on | Value | Who may call |
938
+ | :-- | :-- | :-- |
939
+ | **any scope** | `Unspecified` | resolve from the class scope at load time (the default) |
940
+ | | `Global` | any authenticated caller |
941
+ | | `Service` | service-token callers only — other services, trusted backends, timer invocations; never a game client |
942
+ | **`Scope.Player` only** | `Player` | the player who owns the scope |
943
+ | **`Scope.MultiplayerSession` only** | `SessionMember` | any member of the session |
944
+ | | `SessionHost` | the host of the session |
945
+
946
+ An `Access` value the module cannot honour throws when the module loads rather than failing quietly, so
947
+ pass enum members and avoid casting integers into `Access`.
948
+
949
+ ### Choosing a level
950
+
951
+ Reach for `Access.Service` on anything that mints value — granting currency, awarding items, adjusting
952
+ another player's state. `SessionHost` suits match flow control (start, force-advance, kick) where one
953
+ client is authoritative. Beyond that, the scope defaults are usually correct, and adding attributes that
954
+ merely restate them is noise.
955
+
956
+ Access control is not a substitute for keeping state private: an endpoint restricted to `Service` still
957
+ exposes any `public` field to every client in scope.
958
+
959
+ ## Local development, debugging, and deployment
960
+
961
+ ### The local Cloud Code server
962
+
963
+ Deploying to the local server is the fast loop: it runs the cloud assembly in a local process, supports
964
+ breakpoints, and lets Play mode talk to real cloud logic without a round trip to UGS.
965
+
966
+ - Configuration lives on the `CloudCodeLocalServerSettings` asset — the listening **port** (default
967
+ **14750**) and an optional **secrets** `TextAsset`. The settings UI can create and assign a placeholder
968
+ secrets JSON file and open it in an external editor; invalid JSON surfaces an error dialog rather than
969
+ failing at runtime.
970
+ - A module whose source changed is **redeployed automatically** onto the running server — no stop/start
971
+ cycle. Redeploy happens after a recompile, outside Play mode only. **Nothing deploys to the local server
972
+ during Play mode**, not even an explicit `services_deployment_item_deploy`, which is refused. A
973
+ `404 function-not-found` for an endpoint added while playing means the module isn't deployed yet: exit
974
+ Play mode, wait for `Local Cloud Code server reloaded` in the Console, then re-enter. Same known limitation
975
+ as modules: see
976
+ the Deploying via the Deployment Window section of `references/cloud-code-modules.md`.
977
+ - The server won't start without an active services environment: see
978
+ the Running the Local Cloud Code Server section of `references/cloud-code-modules.md`.
979
+ - On startup the server checks for port conflicts and a usable .NET runtime, and a server that dies during
980
+ startup reports why.
981
+
982
+ ### The binding Inspector
983
+
984
+ The generator also emits a custom Inspector for each bindings component, which is the fastest way to see
985
+ what a binding is actually doing at runtime. In Play mode it shows:
986
+
987
+ - a live connection-status indicator and the resolved player or session identifier — so a binding that
988
+ never resolved a scope is visible without adding a single log line;
989
+ - the current value of every synchronized member and `[SyncView]` projection, read-only;
990
+ - `UnityEvent` slots for `ScopeChanged`, `Fatal`, `Error`, `MessageReceived`, `Synchronized`, and each
991
+ push event, so listeners can be wired without code.
992
+
993
+ Check it before instrumenting anything. A missing member points at generation (see
994
+ [Generator diagnostics](#generator-diagnostics)); a resolved scope with stale values points at the push
995
+ path; no scope at all points at the lifecycle.
996
+
997
+ ### Logging
998
+
999
+ Two independent switches:
1000
+
1001
+ | Where | How |
1002
+ | :-- | :-- |
1003
+ | Server-side, inside a behaviour | `Logger` on the base class (`Microsoft.Extensions.Logging.ILogger`) |
1004
+ | Cloud Code's own Editor/authoring logging, including local server operation | **Verbose Logging** in **Project Settings > Services > Cloud Code**, backed by the `ENABLE_UNITY_CLOUD_CODE_AUTHORING_VERBOSE_LOGGING` define |
1005
+ | Client-side binding traffic — scope resolution, pushes, RPC calls | add the `ENABLE_CLOUDBEHAVIOUR_LOGS` scripting define; the generated bindings' log calls are `[Conditional]` on it, so they compile out without it |
1006
+
1007
+ Local server infrastructure logging defaults to non-verbose, so turn Verbose Logging on before chasing a
1008
+ deployment or connection problem.
1009
+
1010
+ ### Deployment
1011
+
1012
+ CloudBehaviour modules appear in the **Deployment** window as Cloud Code modules and deploy from there to
1013
+ the linked UGS project. A module asset is a `.ccmu` file; its inspector offers deployment actions and a
1014
+ summary of the last successful deployment. The window finds modules only through their `.ccmu`, so a module
1015
+ that is missing from it has no `.ccmu`. Repair it by adding one (see
1016
+ [Creating the module](#creating-the-module)).
1017
+
1018
+ Names come from the **cloud assembly (asmdef) name**, not the `.ccmu` asset name. While the local server
1019
+ runs, an explicit deploy goes to it instead of the environment, so stop it before deploying remotely.
1020
+
1021
+ A deployed behaviour runs in the same hosting model as any other Cloud Code module, so **cold start
1022
+ applies**: after roughly 15 minutes without traffic the worker shuts down and the next call pays a
1023
+ latency spike while it starts again. Budget for it in the UX — do not put a tight timeout on the first
1024
+ call after an idle period, and do not treat that first slow response as a failure.
1025
+
1026
+ ### When the generated bindings do not compile
1027
+
1028
+ Work through it in this order, since the later steps are rarer:
1029
+
1030
+ 1. Recompile. Generated sources are produced per compilation; a stale error can survive an interrupted
1031
+ one.
1032
+ 2. Check both assemblies reference `Unity.Services.CloudCode.SourceGenerator`, and that the cloud
1033
+ assembly references `Unity.Services.CloudBehaviours.Editor`. A missing reference is the most common
1034
+ cause, and the generator's diagnostic names it.
1035
+ 3. Check the console for generator diagnostics — collisions between behaviour members, read-only members
1036
+ skipped for synchronization, unsupported collection types, and duplicate names are all reported there
1037
+ rather than as mystery type errors.
1038
+ 4. Confirm no generated member has been implemented by hand in the partial (`EnableClient`,
1039
+ `DisableClient`, `Hydrate`, an RPC, a synchronized property).
1040
+ 5. Confirm the editor is 6000.5+ — both scaffolded assemblies are gated behind `UNITY_6000_5_OR_NEWER`,
1041
+ so on an older editor the types simply do not exist and every reference to them fails.
1042
+
1043
+ ## Generator diagnostics
1044
+
1045
+ The generator reports problems as compiler diagnostics with ids of the form `CCSG1xx` (CloudBehaviours;
1046
+ `CCSG2xx` is native modules, `CCSG3xx` bindings). They surface in the Console and in the IDE like any
1047
+ other compiler message, and severities can be adjusted through `.editorconfig`.
1048
+
1049
+ **Read them before assuming the generator is broken.** Most CloudBehaviour problems are reported, name
1050
+ the offending member, and say what was kept and what was dropped.
1051
+
1052
+ **An error that aborts generation takes every generated member with it.** The client partial then has no
1053
+ generated half, so a single `CCSG1xx` error surfaces as a pile of `CS0103 - does not exist in the current
1054
+ context` on `EnableClient`, `DisableClient`, `Synchronized`, the RPCs and the synchronized properties.
1055
+ Those are symptoms. Fix the `CCSG1xx` and they all go away together — editing the client partial to chase
1056
+ them is wasted work.
1057
+
1058
+ Common ones, by what they tell you:
1059
+
1060
+ | Id | Situation | Severity | Fix |
1061
+ | :-- | :-- | :-- | :-- |
1062
+ | `CCSG126` | Behaviour class has no `[StateScope]` | error | Add `[StateScope]` to the class |
1063
+ | `CCSG101` | Two behaviours in a module share a simple class name | error | Rename one behaviour so simple names are unique in the module |
1064
+ | `CCSG120` / `CCSG122` | Name reserved by a generated type or framework type | error | Rename the member or class away from the reserved name |
1065
+ | `CCSG115` | `Access` value incompatible with the class scope | error | Use an `Access` value valid for the class scope |
1066
+ | `CCSG116` | `[CloudCodeAccess]` on something that is not an endpoint | error | Remove `[CloudCodeAccess]` or put it on an endpoint |
1067
+ | `CCSG109` | `ref` / `out` parameter | error | Remove the `ref` / `out` modifier |
1068
+ | `CCSG112` / `CCSG125` | Generic behaviour class, or a nested one | error | Make the behaviour class non-generic and top-level |
1069
+ | `CCSG110` / `CCSG111` | Unsupported awaitable return, or `async void` | error | Return `Task`, `Task<T>` or a plain value, and never `async void` |
1070
+ | `CCSG128` / `CCSG129` | Custom collection type in a signature or data-type field | error | Use `List<T>`, arrays or dictionaries instead of a custom collection |
1071
+ | `CCSG131` | Data-type member that is readable but not settable, including a `readonly` field | error | Give the member a setter, or remove it from the data type |
1072
+ | `CCSG132` | `ScheduleAsync` on another behaviour's `I{Behaviour}` — a timer only runs on the behaviour that scheduled it | error | Call `ScheduleAsync` only on the behaviour that owns the timer |
1073
+ | `CCSG133` | A described `ICall` is never given `ScheduleAsync` or `ForScope` | error | Call `ScheduleAsync` or `ForScope` on the described `ICall` |
1074
+ | `CCSG136` | Cloud Code service or generated `I{Behaviour}` parameter without `[CloudCodeService]` | error | Mark the parameter `[CloudCodeService]` |
1075
+ | `CCSG142` | `[SyncView]` method that isn't `public` or `internal` | error | Make the `[SyncView]` method `public` or `internal` |
1076
+ | `CCSG127` | `Scope.MultiplayerSession` client assembly missing `Unity.Services.CloudBehaviours.Multiplayer.Runtime` / `Unity.Services.Multiplayer` references — the message names both. Fires even with the package installed | error | Add the two assembly references the message names to the client assembly |
1077
+ | `CCSG118` | Members of different kinds sharing a name — all of them dropped | error | Rename the members so no two kinds share a name |
1078
+ | `CCSG117` | Duplicate member name within a behaviour — first declaration kept | **warning** | Rename the duplicate member |
1079
+ | `CCSG119` | Member name reserved by the generated bindings class | **warning** | Rename the member away from the bindings class name |
1080
+ | `CCSG113` | Read-only synchronized member ignored: a `readonly` field, or a get-only or computed property | **warning** | Give the member a setter if it must synchronize |
1081
+ | `CCSG158` | `public static` or `const` member on a behaviour — not synchronized | **warning** | Move the value to a synchronized instance member if clients need it |
1082
+ | `CCSG114` | Unsupported event handler type, including more than four payload parameters | **warning** | Use a supported handler type with at most four payload parameters |
1083
+
1084
+ The warnings matter as much as the errors: each one means a member you wrote is **not** on the wire, and
1085
+ the build still succeeds. If a client member is missing, search the Console for `CCSG1` before changing
1086
+ any code.
1087
+
1088
+ ## Silent failure modes
1089
+
1090
+ These compile and then do nothing, which is what makes them expensive:
1091
+
1092
+ - **Read-only and static members don't synchronize.** Public fields and public properties with a
1093
+ setter of any accessibility sync. A `readonly` field or a get-only or computed property raises warning
1094
+ `CCSG113`; a `public static` or `const` member raises warning `CCSG158`. Either way the client property never appears.
1095
+ - **Names collide across kinds, case-insensitively.** Endpoints, synchronized fields, events and
1096
+ projections share one wire-key namespace, and overloads collide for the same reason. A same-kind
1097
+ duplicate keeps the first declaration with **warning** `CCSG117`, so the build stays green while the
1098
+ dropped member is missing on the client. A cross-kind clash drops all of them and fails with error `CCSG118`.
1099
+ - **Push events are never replayed**, so a late joiner sees nothing; lasting state belongs in a
1100
+ synchronized field.
1101
+ - **Player-scoped behaviours push to no other clients.** State reaches that one client through RPC
1102
+ returns, the hydrate on connect, and `Hydrate()`.
1103
+ - **A module without a `.ccmu` never reaches the Deployment window.** Hand-written asmdefs compile and
1104
+ generate bindings, but nothing lists the module for deployment and nothing warns. Create new modules
1105
+ through the menu, and repair an existing one by adding its `.ccmu` (see
1106
+ [Creating the module](#creating-the-module)).
1107
+ - **Model types are data only.** Methods and constructors are not regenerated on the client — the stub
1108
+ carries the public fields and properties, nothing else. A computed or get-only property is error
1109
+ `CCSG131`.
1110
+
1111
+ When a member is missing on the client, read the console for generator diagnostics before changing code.