nccgs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/nccgs-accessibility-specialist.md +26 -0
- package/.claude/agents/nccgs-adversarial-reviewer.md +26 -0
- package/.claude/agents/nccgs-ai-programmer.md +26 -0
- package/.claude/agents/nccgs-analytics-engineer.md +26 -0
- package/.claude/agents/nccgs-art-direction-lead.md +26 -0
- package/.claude/agents/nccgs-audio-direction-lead.md +26 -0
- package/.claude/agents/nccgs-creative-director.md +26 -0
- package/.claude/agents/nccgs-documentation-manager.md +26 -0
- package/.claude/agents/nccgs-economy-designer.md +26 -0
- package/.claude/agents/nccgs-engine-programmer.md +26 -0
- package/.claude/agents/nccgs-game-design-lead.md +26 -0
- package/.claude/agents/nccgs-game-designer.md +26 -0
- package/.claude/agents/nccgs-gameplay-programmer.md +26 -0
- package/.claude/agents/nccgs-level-designer.md +26 -0
- package/.claude/agents/nccgs-live-ops-designer.md +26 -0
- package/.claude/agents/nccgs-localization-lead.md +26 -0
- package/.claude/agents/nccgs-narrative-lead.md +26 -0
- package/.claude/agents/nccgs-network-programmer.md +26 -0
- package/.claude/agents/nccgs-performance-analyst.md +26 -0
- package/.claude/agents/nccgs-producer.md +26 -0
- package/.claude/agents/nccgs-production-coordinator.md +23 -0
- package/.claude/agents/nccgs-programming-lead.md +26 -0
- package/.claude/agents/nccgs-prototyper.md +26 -0
- package/.claude/agents/nccgs-qa-engineer.md +26 -0
- package/.claude/agents/nccgs-qa-lead.md +26 -0
- package/.claude/agents/nccgs-release-engineer.md +26 -0
- package/.claude/agents/nccgs-release-lead.md +26 -0
- package/.claude/agents/nccgs-security-engineer.md +26 -0
- package/.claude/agents/nccgs-sound-designer.md +26 -0
- package/.claude/agents/nccgs-systems-designer.md +26 -0
- package/.claude/agents/nccgs-technical-architect.md +26 -0
- package/.claude/agents/nccgs-technical-artist.md +26 -0
- package/.claude/agents/nccgs-technical-director.md +26 -0
- package/.claude/agents/nccgs-tools-programmer.md +26 -0
- package/.claude/agents/nccgs-ui-programmer.md +26 -0
- package/.claude/agents/nccgs-unity-build-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-content-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-implementer.md +26 -0
- package/.claude/agents/nccgs-unity-rendering-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-systems-specialist.md +26 -0
- package/.claude/agents/nccgs-unity-ui-specialist.md +26 -0
- package/.claude/agents/nccgs-ux-designer.md +26 -0
- package/.claude/agents/nccgs-verification-engineer.md +26 -0
- package/.claude/agents/nccgs-world-builder.md +26 -0
- package/.claude/agents/nccgs-writer.md +26 -0
- package/.claude/nccgs/THIRD_PARTY_NOTICES.md +13 -0
- package/.claude/nccgs/VERSION +1 -0
- package/.claude/nccgs/constitution.md +131 -0
- package/.claude/nccgs/hooks/agent-audit.mjs +10 -0
- package/.claude/nccgs/hooks/common.mjs +41 -0
- package/.claude/nccgs/hooks/post-compact.mjs +1 -0
- package/.claude/nccgs/hooks/pre-compact.mjs +8 -0
- package/.claude/nccgs/hooks/protect-git.mjs +22 -0
- package/.claude/nccgs/hooks/protect-write.mjs +17 -0
- package/.claude/nccgs/hooks/session-start.mjs +15 -0
- package/.claude/nccgs/hooks/session-stop.mjs +7 -0
- package/.claude/nccgs/protocols/agent-contract.md +34 -0
- package/.claude/nccgs/protocols/context-packets.md +13 -0
- package/.claude/nccgs/protocols/evidence.md +15 -0
- package/.claude/nccgs/protocols/model-routing.md +22 -0
- package/.claude/nccgs/protocols/orchestration.md +26 -0
- package/.claude/nccgs/protocols/unity-boundary.md +11 -0
- package/.claude/nccgs/settings.fragment.json +77 -0
- package/.claude/nccgs/studio.json +347 -0
- package/.claude/nccgs/tools/configure-models.mjs +39 -0
- package/.claude/nccgs/unity-skills-manifest.json +727 -0
- package/.claude/nccgs/workflow-catalog.json +317 -0
- package/.claude/rules/nccgs-canonical-docs.md +19 -0
- package/.claude/rules/nccgs-editor-tools.md +12 -0
- package/.claude/rules/nccgs-localization.md +12 -0
- package/.claude/rules/nccgs-networking.md +13 -0
- package/.claude/rules/nccgs-performance.md +14 -0
- package/.claude/rules/nccgs-rendering.md +16 -0
- package/.claude/rules/nccgs-security.md +13 -0
- package/.claude/rules/nccgs-tests.md +18 -0
- package/.claude/rules/nccgs-ui.md +14 -0
- package/.claude/rules/nccgs-unity-assets.md +21 -0
- package/.claude/rules/nccgs-unity-code.md +22 -0
- package/.claude/skills/accessibility-review/SKILL.md +14 -0
- package/.claude/skills/architecture-decision/SKILL.md +14 -0
- package/.claude/skills/asset-audit/SKILL.md +14 -0
- package/.claude/skills/audit/SKILL.md +16 -0
- package/.claude/skills/audit/references/dimensions.md +46 -0
- package/.claude/skills/balance-review/SKILL.md +14 -0
- package/.claude/skills/bug-triage/SKILL.md +14 -0
- package/.claude/skills/build-live-game/SKILL.md +317 -0
- package/.claude/skills/build-live-game/references/achievements.md +779 -0
- package/.claude/skills/build-live-game/references/apis.md +280 -0
- package/.claude/skills/build-live-game/references/authentication.md +437 -0
- package/.claude/skills/build-live-game/references/battlepass.md +860 -0
- package/.claude/skills/build-live-game/references/cloud-code.md +563 -0
- package/.claude/skills/build-live-game/references/cloud-save.md +474 -0
- package/.claude/skills/build-live-game/references/deployment.md +216 -0
- package/.claude/skills/build-live-game/references/player-account.md +813 -0
- package/.claude/skills/build-live-game/references/remote-config.md +96 -0
- package/.claude/skills/build-live-game/references/tooling.md +431 -0
- package/.claude/skills/closure/SKILL.md +14 -0
- package/.claude/skills/code-review/SKILL.md +14 -0
- package/.claude/skills/compatibility-review/SKILL.md +14 -0
- package/.claude/skills/context-pack/SKILL.md +14 -0
- package/.claude/skills/dependency-review/SKILL.md +14 -0
- package/.claude/skills/design/SKILL.md +20 -0
- package/.claude/skills/design-review/SKILL.md +14 -0
- package/.claude/skills/evidence-review/SKILL.md +14 -0
- package/.claude/skills/hotfix/SKILL.md +14 -0
- package/.claude/skills/implement-in-app-purchases/README.md +233 -0
- package/.claude/skills/implement-in-app-purchases/SKILL.md +158 -0
- package/.claude/skills/implement-in-app-purchases/references/api-notes.md +562 -0
- package/.claude/skills/implement-in-app-purchases/references/codeless-catalog.md +331 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-adapty.md +268 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-essentialkit.md +239 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-revenuecat.md +275 -0
- package/.claude/skills/implement-in-app-purchases/references/convert-unipay.md +145 -0
- package/.claude/skills/implement-in-app-purchases/references/migration-v4-to-v5.md +204 -0
- package/.claude/skills/implement-in-app-purchases/references/path-add-iap-to-new-project.md +198 -0
- package/.claude/skills/implement-in-app-purchases/references/path-convert-native-google-billing.md +356 -0
- package/.claude/skills/implement-in-app-purchases/references/path-convert-native-storekit.md +446 -0
- package/.claude/skills/implement-in-app-purchases/references/path-implement-iap-d2c.md +680 -0
- package/.claude/skills/implement-in-app-purchases/references/platform-notes.md +204 -0
- package/.claude/skills/implement-in-app-purchases/references/pre-check.md +205 -0
- package/.claude/skills/incident-recovery/SKILL.md +14 -0
- package/.claude/skills/initialize-ai-navigation/SKILL.md +95 -0
- package/.claude/skills/initialize-ai-navigation/references/navigation-system.md +794 -0
- package/.claude/skills/levelplay-unity-integration/CHANGELOG.md +57 -0
- package/.claude/skills/levelplay-unity-integration/README.md +74 -0
- package/.claude/skills/levelplay-unity-integration/SKILL.md +1126 -0
- package/.claude/skills/levelplay-unity-integration/references/banner-api.md +920 -0
- package/.claude/skills/levelplay-unity-integration/references/best-practices.md +536 -0
- package/.claude/skills/levelplay-unity-integration/references/ilrd-api.md +337 -0
- package/.claude/skills/levelplay-unity-integration/references/initialization-api.md +630 -0
- package/.claude/skills/levelplay-unity-integration/references/interstitial-api.md +899 -0
- package/.claude/skills/levelplay-unity-integration/references/ios-setup.md +491 -0
- package/.claude/skills/levelplay-unity-integration/references/migration-sdk-9.md +666 -0
- package/.claude/skills/levelplay-unity-integration/references/privacy-settings.md +608 -0
- package/.claude/skills/levelplay-unity-integration/references/rewarded-api.md +902 -0
- package/.claude/skills/localization/SKILL.md +135 -0
- package/.claude/skills/localization/references/api-notes.md +76 -0
- package/.claude/skills/localization/resources/L10nBatchProcessor.cs +69 -0
- package/.claude/skills/localization/resources/LocalizedFontAsset.cs +18 -0
- package/.claude/skills/localize-game/SKILL.md +14 -0
- package/.claude/skills/migrate-project/SKILL.md +21 -0
- package/.claude/skills/migrate-project/references/procedure.md +63 -0
- package/.claude/skills/milestone-review/SKILL.md +14 -0
- package/.claude/skills/new-unity-project/SKILL.md +179 -0
- package/.claude/skills/optimize-audio/SKILL.md +199 -0
- package/.claude/skills/optimize-audio/resources/audio-import-api.md +146 -0
- package/.claude/skills/optimize-audio/resources/platform-settings.md +48 -0
- package/.claude/skills/optimize-text-mesh-pro/SKILL.md +182 -0
- package/.claude/skills/optimize-web/SKILL.md +393 -0
- package/.claude/skills/optimize-web/resources/WebOptimizer.cs +21 -0
- package/.claude/skills/optimize-web/resources/toktx-examples.sh +11 -0
- package/.claude/skills/performance-audit/SKILL.md +14 -0
- package/.claude/skills/physics-3d-collision/SKILL.md +442 -0
- package/.claude/skills/physics-3d-collision/references/troubleshooting.md +41 -0
- package/.claude/skills/physics-3d-collision/resources/CollisionDebugger.cs +33 -0
- package/.claude/skills/plan-feature/SKILL.md +14 -0
- package/.claude/skills/playtest/SKILL.md +14 -0
- package/.claude/skills/project-stage/SKILL.md +14 -0
- package/.claude/skills/prototype-feature/SKILL.md +14 -0
- package/.claude/skills/qa-plan/SKILL.md +14 -0
- package/.claude/skills/release/SKILL.md +18 -0
- package/.claude/skills/release-readiness/SKILL.md +14 -0
- package/.claude/skills/retrospective/SKILL.md +14 -0
- package/.claude/skills/review/SKILL.md +16 -0
- package/.claude/skills/security-audit/SKILL.md +14 -0
- package/.claude/skills/setup-multiplayer-services/SKILL.md +39 -0
- package/.claude/skills/setup-multiplayer-services/references/dgs-entrypoint.md +79 -0
- package/.claude/skills/setup-multiplayer-services/references/entrypoints.md +213 -0
- package/.claude/skills/setup-multiplayer-services/references/examples.md +33 -0
- package/.claude/skills/setup-multiplayer-services/references/implementation-fit.md +30 -0
- package/.claude/skills/setup-multiplayer-services/references/underlying-services.md +11 -0
- package/.claude/skills/setup-multiplayer-services/references/workflows-prerequisites.md +17 -0
- package/.claude/skills/setup-vivox-voice-chat/SKILL.md +118 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/.env.example +6 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/README.md +101 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/promptfooconfig.yaml +32 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/init-and-login.yaml +76 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/text-chat.yaml +65 -0
- package/.claude/skills/setup-vivox-voice-chat/evals/tests/voice-channels.yaml +74 -0
- package/.claude/skills/setup-vivox-voice-chat/references/events-and-participants.md +79 -0
- package/.claude/skills/setup-vivox-voice-chat/references/init-and-login.md +92 -0
- package/.claude/skills/setup-vivox-voice-chat/references/text-chat.md +93 -0
- package/.claude/skills/setup-vivox-voice-chat/references/troubleshooting.md +47 -0
- package/.claude/skills/setup-vivox-voice-chat/references/voice-channels.md +90 -0
- package/.claude/skills/shader-graph-create-custom-node/SKILL.md +25 -0
- package/.claude/skills/shader-graph-create-custom-node/resources/all_hints.hlsl +182 -0
- package/.claude/skills/sprint-plan/SKILL.md +14 -0
- package/.claude/skills/sprite-editor/SKILL.md +66 -0
- package/.claude/skills/sprite-editor/references/api_reference.md +151 -0
- package/.claude/skills/sprite-editor/references/background.md +112 -0
- package/.claude/skills/sprite-editor/references/templates.md +72 -0
- package/.claude/skills/sprite-editor/scripts/AutomaticSliceTexture.cs +40 -0
- package/.claude/skills/sprite-editor/scripts/GenerateNewSpriteRects.cs +200 -0
- package/.claude/skills/sprite-editor/scripts/GetTextureSourceImageSize.cs +32 -0
- package/.claude/skills/sprite-editor/scripts/GetTextureToSlice.cs +55 -0
- package/.claude/skills/sprite-editor/scripts/GridSliceTexture.cs +40 -0
- package/.claude/skills/sprite-editor/scripts/IsometricSliceTexture.cs +141 -0
- package/.claude/skills/sprite-editor/scripts/README.md +134 -0
- package/.claude/skills/sprite-editor/scripts/SetPivotExample.cs +58 -0
- package/.claude/skills/sprite-editor/scripts/SpriteToPng.cs +88 -0
- package/.claude/skills/status/SKILL.md +16 -0
- package/.claude/skills/story-readiness/SKILL.md +14 -0
- package/.claude/skills/test/SKILL.md +16 -0
- package/.claude/skills/ui/SKILL.md +142 -0
- package/.claude/skills/ui-imgui/SKILL.md +186 -0
- package/.claude/skills/ui-imgui/references/gui-elements.md +156 -0
- package/.claude/skills/ui-imgui/references/templates.md +141 -0
- package/.claude/skills/ui-review/SKILL.md +14 -0
- package/.claude/skills/ui-ugui/SKILL.md +282 -0
- package/.claude/skills/ui-ugui/references/scrollview-setup.md +29 -0
- package/.claude/skills/ui-uitk/SKILL.md +235 -0
- package/.claude/skills/ui-uitk/references/common-issues.md +74 -0
- package/.claude/skills/ui-uitk/references/custom-elements.md +241 -0
- package/.claude/skills/ui-uitk/references/painter2d.md +282 -0
- package/.claude/skills/ui-uitk/references/pointermanipulator-guide.md +94 -0
- package/.claude/skills/ui-uitk/references/svg-icons.md +136 -0
- package/.claude/skills/ui-uitk/references/ui-runtime-binding.md +234 -0
- package/.claude/skills/ui-uitk/references/uss-guide.md +138 -0
- package/.claude/skills/unity-cli/CHANGELOG.md +233 -0
- package/.claude/skills/unity-cli/SECURITY.md +22 -0
- package/.claude/skills/unity-cli/SKILL.md +414 -0
- package/.claude/skills/unity-cli/references/auth-license-cloud.md +146 -0
- package/.claude/skills/unity-cli/references/build-run-test.md +349 -0
- package/.claude/skills/unity-cli/references/collaboration.md +472 -0
- package/.claude/skills/unity-cli/references/config-hub.md +103 -0
- package/.claude/skills/unity-cli/references/diagnostics-maintenance.md +326 -0
- package/.claude/skills/unity-cli/references/editors-install.md +327 -0
- package/.claude/skills/unity-cli/references/integration-advanced.md +472 -0
- package/.claude/skills/unity-cli/references/projects-templates.md +574 -0
- package/.claude/skills/unity-package-management/SKILL.md +304 -0
- package/.claude/skills/unity-package-management/references/select-packages.md +108 -0
- package/.claude/skills/urp-postprocessing/SKILL.md +188 -0
- package/.claude/skills/urp-postprocessing/references/code-templates.md +119 -0
- package/.claude/skills/urp-postprocessing/references/effect-reference.md +86 -0
- package/.claude/skills/validate-urp-render-graph-renderer-feature/SKILL.md +269 -0
- package/.claude/skills/work/SKILL.md +36 -0
- package/.claude/skills/work/references/classification.md +41 -0
- package/.claude/skills/work/references/closure.md +50 -0
- package/.claude/skills/work/references/feature-contracts.md +32 -0
- package/.claude/skills/work/references/verification.md +26 -0
- package/CLAUDE.md +6 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/UPGRADING.md +32 -0
- package/VERSION +1 -0
- package/docs/ARCHITECTURE.md +73 -0
- package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +324 -0
- package/docs/MIGRATION-MATRIX.md +23 -0
- package/docs/PROJECT-POLICY.md +67 -0
- package/docs/WORKFLOWS.md +61 -0
- package/package-assets/setup-vivox-voice-chat-evals.gitignore +7 -0
- package/package.json +41 -0
- package/scaffold/.nccgs/bugs/.gitkeep +1 -0
- package/scaffold/.nccgs/closures/.gitkeep +1 -0
- package/scaffold/.nccgs/context/.gitkeep +1 -0
- package/scaffold/.nccgs/decisions/.gitkeep +1 -0
- package/scaffold/.nccgs/evidence/.gitkeep +1 -0
- package/scaffold/.nccgs/features/.gitkeep +1 -0
- package/scaffold/.nccgs/migrations/.gitkeep +1 -0
- package/scaffold/.nccgs/playtests/.gitkeep +1 -0
- package/scaffold/.nccgs/project.yaml +103 -0
- package/scaffold/.nccgs/requirements.yaml +10 -0
- package/scaffold/.nccgs/reviews/.gitkeep +1 -0
- package/scaffold/.nccgs/state.md +40 -0
- package/scaffold/.nccgs/templates/agent-handoff.md +25 -0
- package/scaffold/.nccgs/templates/architecture-decision.md +27 -0
- package/scaffold/.nccgs/templates/closure-record.md +51 -0
- package/scaffold/.nccgs/templates/context-packet.yaml +17 -0
- package/scaffold/.nccgs/templates/evidence-record.md +23 -0
- package/scaffold/.nccgs/templates/feature-contract.md +35 -0
- package/scaffold/.nccgs/templates/migration-plan.md +40 -0
- package/scaffold/.nccgs/templates/waiver.md +11 -0
- package/scripts/cli.mjs +56 -0
- package/scripts/install.mjs +267 -0
- package/scripts/sync-unity-skills.mjs +126 -0
- package/scripts/validate.mjs +205 -0
- package/tests/framework.test.mjs +121 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# Cloud Save Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Anti-Hallucination Reference](#anti-hallucination-reference)
|
|
6
|
+
- [Subsystems](#subsystems)
|
|
7
|
+
- [IPlayerDataService Methods](#iplayerdataservice-methods)
|
|
8
|
+
- [ICustomDataService Methods](#icustomdataservice-methods)
|
|
9
|
+
- [IPlayerFilesService Methods](#iplayerfilesservice-methods)
|
|
10
|
+
- [Key Model Types](#key-model-types)
|
|
11
|
+
- [Access Classes](#access-classes)
|
|
12
|
+
- [Code Templates](#code-templates)
|
|
13
|
+
- [Error Handling](#error-handling)
|
|
14
|
+
|
|
15
|
+
Accessed via `CloudSaveService.Instance` (`ICloudSaveService`, namespace `Unity.Services.CloudSave`). Assembly: `Unity.Services.CloudSave`.
|
|
16
|
+
|
|
17
|
+
Call `UnityServices.InitializeAsync()` from `com.unity.services.core` and sign in via `com.unity.services.authentication` before use.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Anti-Hallucination Reference
|
|
22
|
+
|
|
23
|
+
| Correct | Incorrect (do NOT use) |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `SaveAsync` returns `Task<Dictionary<string, string>>` (write lock tokens) | `SaveAsync` returns `Task` (void) |
|
|
26
|
+
| `Item.Value` is `IDeserializable` -- call `.GetAs<T>()` | `Item.Value` is `object` |
|
|
27
|
+
| `SaveAsync(IDictionary<string, object>)` -- uses `IDictionary` | `SaveAsync(Dictionary<string, object>)` -- wrong interface type |
|
|
28
|
+
| `SaveAsync(IDictionary<string, SaveItem>)` -- bundles value + write lock | `SaveAsync(data, Dictionary<string, WriteLockOptions>)` -- no such overload |
|
|
29
|
+
| Custom data is **read-only** from client (no save/delete methods) | Custom data can be written from client |
|
|
30
|
+
| `CloudSaveConflictException` has `Details` list | Only `CloudSaveException` exists for conflicts |
|
|
31
|
+
| `CloudSaveExceptionReason.Conflict` (value 11) | `WriteLockConflict` reason |
|
|
32
|
+
| `Item.Modified` / `Item.Created` are `DateTime?` | `Item.Modified` is `ModifiedMetadata` |
|
|
33
|
+
| `FileItem.Modified` / `FileItem.Created` are `DateTime?` | `FileItem.Modified` is `ModifiedMetadata` |
|
|
34
|
+
| `DeleteAsync(key, Models.Data.Player.DeleteOptions)` -- current API | `DeleteAsync(key, CloudSave.DeleteOptions)` -- deprecated overload |
|
|
35
|
+
| `ICustomDataService` methods require `customDataID` first param | Custom data methods with no ID param |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Subsystems
|
|
40
|
+
|
|
41
|
+
| Subsystem | Access | Description |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `Data.Player` | `CloudSaveService.Instance.Data.Player` | Key-value data for the signed-in player |
|
|
44
|
+
| `Data.Custom` | `CloudSaveService.Instance.Data.Custom` | Game-wide or server-written data; **read-only from client** |
|
|
45
|
+
| `Files.Player` | `CloudSaveService.Instance.Files.Player` | Binary file storage per player |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## IPlayerDataService Methods
|
|
50
|
+
|
|
51
|
+
`CloudSaveService.Instance.Data.Player`
|
|
52
|
+
|
|
53
|
+
**Options namespace:** All option classes (`SaveOptions`, `LoadOptions`, `DeleteOptions`, `DeleteAllOptions`, `ListAllKeysOptions`, `LoadAllOptions`, `QueryOptions`) are in `Unity.Services.CloudSave.Models.Data.Player`. Do **not** confuse with the deprecated root-level `CloudSave.SaveOptions` / `CloudSave.DeleteOptions`.
|
|
54
|
+
|
|
55
|
+
```csharp
|
|
56
|
+
// List all keys belonging to the current player (with metadata).
|
|
57
|
+
Task<List<ItemKey>> ListAllKeysAsync()
|
|
58
|
+
Task<List<ItemKey>> ListAllKeysAsync(ListAllKeysOptions options)
|
|
59
|
+
|
|
60
|
+
// Load specific keys. Returns dictionary of key -> Item.
|
|
61
|
+
Task<Dictionary<string, Item>> LoadAsync(ISet<string> keys)
|
|
62
|
+
Task<Dictionary<string, Item>> LoadAsync(ISet<string> keys, LoadOptions options)
|
|
63
|
+
|
|
64
|
+
// Load all keys for the current player.
|
|
65
|
+
Task<Dictionary<string, Item>> LoadAllAsync()
|
|
66
|
+
Task<Dictionary<string, Item>> LoadAllAsync(LoadAllOptions options)
|
|
67
|
+
|
|
68
|
+
// Save key-value pairs. Returns Dictionary<string, string> mapping each key to its new write-lock token.
|
|
69
|
+
Task<Dictionary<string, string>> SaveAsync(IDictionary<string, object> data)
|
|
70
|
+
Task<Dictionary<string, string>> SaveAsync(IDictionary<string, object> data, SaveOptions options)
|
|
71
|
+
|
|
72
|
+
// Save with per-key write locks bundled via SaveItem.
|
|
73
|
+
Task<Dictionary<string, string>> SaveAsync(IDictionary<string, SaveItem> data)
|
|
74
|
+
Task<Dictionary<string, string>> SaveAsync(IDictionary<string, SaveItem> data, SaveOptions options)
|
|
75
|
+
|
|
76
|
+
// Delete a specific key. Use Models.Data.Player.DeleteOptions (not the deprecated root DeleteOptions).
|
|
77
|
+
Task DeleteAsync(string key, Models.Data.Player.DeleteOptions options)
|
|
78
|
+
|
|
79
|
+
// Delete ALL keys for the current player.
|
|
80
|
+
Task DeleteAllAsync()
|
|
81
|
+
Task DeleteAllAsync(DeleteAllOptions options)
|
|
82
|
+
|
|
83
|
+
// Query player data with field filters.
|
|
84
|
+
Task<List<EntityData>> QueryAsync(Query query, QueryOptions options)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## ICustomDataService Methods
|
|
90
|
+
|
|
91
|
+
`CloudSaveService.Instance.Data.Custom`
|
|
92
|
+
|
|
93
|
+
Read-only from the client. Write via Cloud Code modules or `IAdminClient.CloudSaveData` from the `com.unity.services.apis` package. All methods require a `customDataID` parameter -- the namespace configured in the Unity Dashboard.
|
|
94
|
+
|
|
95
|
+
> **To write Custom Data from the editor or a deploy command**, use `IAdminClient.CloudSaveData.SetCustomItem` /
|
|
96
|
+
> `SetCustomItemBatch`. See [apis.md](apis.md) for setup and code templates. The client SDK has no write
|
|
97
|
+
> path for Custom Data.
|
|
98
|
+
|
|
99
|
+
```csharp
|
|
100
|
+
Task<List<ItemKey>> ListAllKeysAsync(string customDataID)
|
|
101
|
+
Task<Dictionary<string, Item>> LoadAllAsync(string customDataID)
|
|
102
|
+
Task<Dictionary<string, Item>> LoadAsync(string customDataID, ISet<string> keys)
|
|
103
|
+
|
|
104
|
+
// Query across custom data. Uses Models.Data.Custom.QueryOptions.
|
|
105
|
+
Task<List<EntityData>> QueryAsync(Query query, Models.Data.Custom.QueryOptions options = default)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## IPlayerFilesService Methods
|
|
111
|
+
|
|
112
|
+
`CloudSaveService.Instance.Files.Player`
|
|
113
|
+
|
|
114
|
+
Note: File methods use root-level `CloudSave.SaveOptions` and `CloudSave.DeleteOptions` (not the `Models.Data.Player` versions).
|
|
115
|
+
|
|
116
|
+
```csharp
|
|
117
|
+
// List all files for the current player.
|
|
118
|
+
Task<List<FileItem>> ListAllAsync()
|
|
119
|
+
|
|
120
|
+
// Save a file (byte array or stream).
|
|
121
|
+
Task SaveAsync(string key, byte[] bytes, SaveOptions options = default)
|
|
122
|
+
Task SaveAsync(string key, Stream stream, SaveOptions options = default)
|
|
123
|
+
|
|
124
|
+
// Load a file as a byte array.
|
|
125
|
+
Task<byte[]> LoadBytesAsync(string key)
|
|
126
|
+
|
|
127
|
+
// Load a file as a stream.
|
|
128
|
+
Task<Stream> LoadStreamAsync(string key)
|
|
129
|
+
|
|
130
|
+
// Delete a file.
|
|
131
|
+
Task DeleteAsync(string key, DeleteOptions options = default)
|
|
132
|
+
|
|
133
|
+
// Get metadata for a specific file.
|
|
134
|
+
Task<FileItem> GetMetadataAsync(string key)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Key Model Types
|
|
140
|
+
|
|
141
|
+
### `Item` (`Unity.Services.CloudSave.Models`)
|
|
142
|
+
|
|
143
|
+
| Property | Type | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `Key` | `string` | The data key |
|
|
146
|
+
| `Value` | `IDeserializable` | Deserialized value -- call `.GetAs<T>()` or `.GetAsString()` |
|
|
147
|
+
| `WriteLock` | `string` | Current write-lock token (use for optimistic concurrency) |
|
|
148
|
+
| `Created` | `DateTime?` | Creation timestamp |
|
|
149
|
+
| `Modified` | `DateTime?` | Last-modified timestamp |
|
|
150
|
+
|
|
151
|
+
### `ItemKey` (`Unity.Services.CloudSave.Models`)
|
|
152
|
+
|
|
153
|
+
| Property | Type | Description |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `Key` | `string` | The data key |
|
|
156
|
+
| `WriteLock` | `string` | Current write-lock token |
|
|
157
|
+
| `Modified` | `DateTime?` | Last-modified timestamp |
|
|
158
|
+
|
|
159
|
+
### `FileItem` (`Unity.Services.CloudSave.Models`)
|
|
160
|
+
|
|
161
|
+
| Property | Type | Description |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| `Key` | `string` | The file key |
|
|
164
|
+
| `Size` | `long` | File size in bytes |
|
|
165
|
+
| `WriteLock` | `string` | Current write-lock token |
|
|
166
|
+
| `ContentType` | `string` | MIME type of the file |
|
|
167
|
+
| `Created` | `DateTime?` | Creation timestamp |
|
|
168
|
+
| `Modified` | `DateTime?` | Last-modified timestamp |
|
|
169
|
+
|
|
170
|
+
### `SaveItem` (`Unity.Services.CloudSave.Models`)
|
|
171
|
+
|
|
172
|
+
Bundles a value and write lock for atomic save-with-lock operations.
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
new SaveItem(value: myObject, writeLock: previousItem.WriteLock)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### `Query` (`Unity.Services.CloudSave.Models`)
|
|
179
|
+
|
|
180
|
+
| Property | Type | Description |
|
|
181
|
+
|---|---|---|
|
|
182
|
+
| `Fields` | `List<FieldFilter>` | Filter conditions (required) |
|
|
183
|
+
| `ReturnKeys` | `HashSet<string>` | Project only these keys in results |
|
|
184
|
+
| `Offset` | `int` | Skip N results (pagination) |
|
|
185
|
+
| `Limit` | `int` | Max results to return |
|
|
186
|
+
| `SampleSize` | `int?` | Random sample size (optional) |
|
|
187
|
+
|
|
188
|
+
### `FieldFilter` (`Unity.Services.CloudSave.Models`)
|
|
189
|
+
|
|
190
|
+
```csharp
|
|
191
|
+
new FieldFilter(key: "level", value: 10, op: FieldFilter.OpOptions.GE, asc: true)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| `OpOptions` | Meaning |
|
|
195
|
+
|---|---|
|
|
196
|
+
| `EQ` | Equal |
|
|
197
|
+
| `NE` | Not equal |
|
|
198
|
+
| `LT` | Less than |
|
|
199
|
+
| `LE` | Less than or equal |
|
|
200
|
+
| `GT` | Greater than |
|
|
201
|
+
| `GE` | Greater than or equal |
|
|
202
|
+
|
|
203
|
+
### `EntityData` (`Unity.Services.CloudSave.Models`)
|
|
204
|
+
|
|
205
|
+
Query results return `List<EntityData>`, where each entry is one player's matching data.
|
|
206
|
+
|
|
207
|
+
| Property | Type | Description |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `Id` | `string` | Player ID |
|
|
210
|
+
| `Data` | `List<Item>` | Matching items for this player |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Access Classes
|
|
215
|
+
|
|
216
|
+
Namespace: `Unity.Services.CloudSave.Models.Data.Player`.
|
|
217
|
+
|
|
218
|
+
### `AccessClass` Enum
|
|
219
|
+
|
|
220
|
+
| Value | Meaning |
|
|
221
|
+
|---|---|
|
|
222
|
+
| `Default` (0) | Owner read/write -- private to the player |
|
|
223
|
+
| `Private` (1) | Alias for Default |
|
|
224
|
+
| `Protected` (2) | Owner read, server-only write |
|
|
225
|
+
| `Public` (3) | Any player can read, owner writes |
|
|
226
|
+
|
|
227
|
+
### Read Access Class Options (for Load/ListAllKeys)
|
|
228
|
+
|
|
229
|
+
- `DefaultReadAccessClassOptions()` -- read own Default-class data
|
|
230
|
+
- `PublicReadAccessClassOptions()` -- read own Public-class data
|
|
231
|
+
- `PublicReadAccessClassOptions(string playerId)` -- read **another player's** Public-class data
|
|
232
|
+
- `ProtectedReadAccessClassOptions()` -- read own Protected-class data
|
|
233
|
+
|
|
234
|
+
### Write Access Class Options (for Save/Delete)
|
|
235
|
+
|
|
236
|
+
- `DefaultWriteAccessClassOptions()` -- write to Default-class keys
|
|
237
|
+
- `PublicWriteAccessClassOptions()` -- write to Public-class keys
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Code Templates
|
|
242
|
+
|
|
243
|
+
### Save Player Data (Capture Write Lock Tokens)
|
|
244
|
+
|
|
245
|
+
```csharp
|
|
246
|
+
using Unity.Services.CloudSave;
|
|
247
|
+
using Unity.Services.CloudSave.Models;
|
|
248
|
+
using System.Collections.Generic;
|
|
249
|
+
|
|
250
|
+
var data = new Dictionary<string, object>
|
|
251
|
+
{
|
|
252
|
+
{ "level", 10 },
|
|
253
|
+
{ "gold", 500 },
|
|
254
|
+
{ "inventory", new string[] { "sword", "shield" } }
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// SaveAsync returns write-lock tokens for each saved key
|
|
258
|
+
Dictionary<string, string> writeLocks = await CloudSaveService.Instance.Data.Player.SaveAsync(data);
|
|
259
|
+
// writeLocks["level"] == "new-lock-token-for-level", etc.
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Load Specific Keys
|
|
263
|
+
|
|
264
|
+
```csharp
|
|
265
|
+
var keys = new HashSet<string> { "level", "gold" };
|
|
266
|
+
var result = await CloudSaveService.Instance.Data.Player.LoadAsync(keys);
|
|
267
|
+
|
|
268
|
+
if (result.TryGetValue("level", out var levelItem))
|
|
269
|
+
{
|
|
270
|
+
int level = levelItem.Value.GetAs<int>();
|
|
271
|
+
Debug.Log($"Level: {level}, Modified: {levelItem.Modified}");
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Save with Write Lock Using SaveItem
|
|
276
|
+
|
|
277
|
+
```csharp
|
|
278
|
+
using Unity.Services.CloudSave.Models;
|
|
279
|
+
|
|
280
|
+
// First load to get the current write lock
|
|
281
|
+
var items = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { "gold" });
|
|
282
|
+
var goldItem = items["gold"];
|
|
283
|
+
|
|
284
|
+
// Bundle value + write lock in SaveItem -- fails if another client updated in the meantime
|
|
285
|
+
var saveData = new Dictionary<string, SaveItem>
|
|
286
|
+
{
|
|
287
|
+
{ "gold", new SaveItem(value: 600, writeLock: goldItem.WriteLock) }
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
Dictionary<string, string> newLocks = await CloudSaveService.Instance.Data.Player.SaveAsync(saveData);
|
|
291
|
+
// newLocks["gold"] is the updated write-lock token
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Handle Write-Lock Conflicts
|
|
295
|
+
|
|
296
|
+
```csharp
|
|
297
|
+
using Unity.Services.CloudSave;
|
|
298
|
+
|
|
299
|
+
try
|
|
300
|
+
{
|
|
301
|
+
await CloudSaveService.Instance.Data.Player.SaveAsync(saveData);
|
|
302
|
+
}
|
|
303
|
+
catch (CloudSaveConflictException ex)
|
|
304
|
+
{
|
|
305
|
+
foreach (var detail in ex.Details)
|
|
306
|
+
{
|
|
307
|
+
Debug.LogError($"Conflict on '{detail.Key}': " +
|
|
308
|
+
$"you sent lock '{detail.AttemptedWriteLock}', " +
|
|
309
|
+
$"server has '{detail.ExistingWriteLock}'");
|
|
310
|
+
}
|
|
311
|
+
// Reload, merge, and retry
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Delete a Key
|
|
316
|
+
|
|
317
|
+
```csharp
|
|
318
|
+
using Unity.Services.CloudSave.Models.Data.Player;
|
|
319
|
+
|
|
320
|
+
// Delete with write-lock check
|
|
321
|
+
var options = new DeleteOptions { WriteLock = knownWriteLock };
|
|
322
|
+
await CloudSaveService.Instance.Data.Player.DeleteAsync("gold", options);
|
|
323
|
+
|
|
324
|
+
// Delete ALL player data
|
|
325
|
+
await CloudSaveService.Instance.Data.Player.DeleteAllAsync();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Save and Load with Access Classes (Public, Default, Protected)
|
|
329
|
+
|
|
330
|
+
> **Namespace note:** `SaveOptions`, `LoadOptions`, and `DeleteOptions` exist in both
|
|
331
|
+
> `Unity.Services.CloudSave` (root) and `Unity.Services.CloudSave.Models.Data.Player`.
|
|
332
|
+
> The access-class overloads live in `Models.Data.Player`. If you import both namespaces,
|
|
333
|
+
> use fully qualified names or a `using` alias to avoid ambiguity.
|
|
334
|
+
|
|
335
|
+
```csharp
|
|
336
|
+
using Unity.Services.CloudSave.Models.Data.Player;
|
|
337
|
+
|
|
338
|
+
// Save data as Public (other players can read it)
|
|
339
|
+
var publicData = new Dictionary<string, object> { { "displayName", "Hero123" }, { "rank", 42 } };
|
|
340
|
+
var publicOptions = new SaveOptions(new PublicWriteAccessClassOptions());
|
|
341
|
+
await CloudSaveService.Instance.Data.Player.SaveAsync(publicData, publicOptions);
|
|
342
|
+
|
|
343
|
+
// Read another player's Public-class data
|
|
344
|
+
var readOptions = new LoadOptions(new PublicReadAccessClassOptions(otherPlayerId));
|
|
345
|
+
var otherPlayerData = await CloudSaveService.Instance.Data.Player.LoadAsync(
|
|
346
|
+
new HashSet<string> { "displayName", "rank" }, readOptions);
|
|
347
|
+
Debug.Log($"Other player name: {otherPlayerData["displayName"].Value.GetAs<string>()}");
|
|
348
|
+
|
|
349
|
+
// Read own Protected-class data (written by server/Cloud Code)
|
|
350
|
+
var protectedOptions = new LoadOptions(new ProtectedReadAccessClassOptions());
|
|
351
|
+
var serverData = await CloudSaveService.Instance.Data.Player.LoadAllAsync(
|
|
352
|
+
new LoadAllOptions(new ProtectedReadAccessClassOptions()));
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Read Custom Data (Game-Wide, Read-Only)
|
|
356
|
+
|
|
357
|
+
```csharp
|
|
358
|
+
// Custom data is read-only from client -- written via Cloud Code or admin API.
|
|
359
|
+
// customDataID is the namespace configured in the Unity Dashboard.
|
|
360
|
+
var customData = await CloudSaveService.Instance.Data.Custom.LoadAllAsync("my-game-config");
|
|
361
|
+
|
|
362
|
+
if (customData.TryGetValue("seasonConfig", out var config))
|
|
363
|
+
{
|
|
364
|
+
var season = config.Value.GetAs<SeasonConfig>();
|
|
365
|
+
Debug.Log($"Current season: {season.Name}");
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Query Player Data
|
|
370
|
+
|
|
371
|
+
```csharp
|
|
372
|
+
using Unity.Services.CloudSave.Models;
|
|
373
|
+
using Unity.Services.CloudSave.Models.Data.Player;
|
|
374
|
+
|
|
375
|
+
// Find players with level >= 10, sorted ascending
|
|
376
|
+
var query = new Query(
|
|
377
|
+
fields: new List<FieldFilter>
|
|
378
|
+
{
|
|
379
|
+
new FieldFilter(key: "level", value: 10, op: FieldFilter.OpOptions.GE, asc: true)
|
|
380
|
+
},
|
|
381
|
+
returnKeys: new HashSet<string> { "level", "displayName" },
|
|
382
|
+
offset: 0,
|
|
383
|
+
limit: 20
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
var results = await CloudSaveService.Instance.Data.Player.QueryAsync(query, new QueryOptions());
|
|
387
|
+
|
|
388
|
+
foreach (var entity in results)
|
|
389
|
+
{
|
|
390
|
+
Debug.Log($"Player {entity.Id}:");
|
|
391
|
+
foreach (var item in entity.Data)
|
|
392
|
+
Debug.Log($" {item.Key} = {item.Value.GetAsString()}");
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Save and Load a File
|
|
397
|
+
|
|
398
|
+
```csharp
|
|
399
|
+
// Save
|
|
400
|
+
byte[] screenshotBytes = await CaptureScreenshot();
|
|
401
|
+
await CloudSaveService.Instance.Files.Player.SaveAsync("screenshot_latest", screenshotBytes);
|
|
402
|
+
|
|
403
|
+
// Load
|
|
404
|
+
byte[] loaded = await CloudSaveService.Instance.Files.Player.LoadBytesAsync("screenshot_latest");
|
|
405
|
+
|
|
406
|
+
// Get metadata
|
|
407
|
+
var meta = await CloudSaveService.Instance.Files.Player.GetMetadataAsync("screenshot_latest");
|
|
408
|
+
Debug.Log($"Size: {meta.Size}, ContentType: {meta.ContentType}, Modified: {meta.Modified}");
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### List All Keys
|
|
412
|
+
|
|
413
|
+
```csharp
|
|
414
|
+
var keys = await CloudSaveService.Instance.Data.Player.ListAllKeysAsync();
|
|
415
|
+
foreach (var key in keys)
|
|
416
|
+
{
|
|
417
|
+
Debug.Log($"Key: {key.Key}, WriteLock: {key.WriteLock}, Modified: {key.Modified}");
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Error Handling
|
|
424
|
+
|
|
425
|
+
```csharp
|
|
426
|
+
try { ... }
|
|
427
|
+
catch (CloudSaveConflictException ex)
|
|
428
|
+
{
|
|
429
|
+
// Write-lock conflict -- inspect per-key details
|
|
430
|
+
foreach (var detail in ex.Details)
|
|
431
|
+
Debug.LogError($"Key '{detail.Key}': attempted={detail.AttemptedWriteLock}, existing={detail.ExistingWriteLock}");
|
|
432
|
+
}
|
|
433
|
+
catch (CloudSaveValidationException ex)
|
|
434
|
+
{
|
|
435
|
+
// Input validation failure -- inspect per-field details
|
|
436
|
+
foreach (var detail in ex.Details)
|
|
437
|
+
Debug.LogError($"Field '{detail.Field}' key '{detail.Key}': {string.Join(", ", detail.Messages)}");
|
|
438
|
+
}
|
|
439
|
+
catch (CloudSaveRateLimitedException ex)
|
|
440
|
+
{
|
|
441
|
+
Debug.LogError($"Rate limited. Retry after {ex.RetryAfter}s");
|
|
442
|
+
}
|
|
443
|
+
catch (CloudSaveException ex)
|
|
444
|
+
{
|
|
445
|
+
Debug.LogError($"Cloud Save error: {ex.Message} (reason: {ex.Reason})");
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### `CloudSaveExceptionReason` Enum
|
|
450
|
+
|
|
451
|
+
| Reason | Value | Meaning |
|
|
452
|
+
|---|---|---|
|
|
453
|
+
| `Unknown` | 0 | Unknown error |
|
|
454
|
+
| `NoInternetConnection` | 1 | No network |
|
|
455
|
+
| `ProjectIdMissing` | 2 | Project ID not set |
|
|
456
|
+
| `PlayerIdMissing` | 3 | Player not signed in |
|
|
457
|
+
| `AccessTokenMissing` | 4 | No access token |
|
|
458
|
+
| `InvalidArgument` | 5 | Bad input |
|
|
459
|
+
| `Unauthorized` | 6 | Not authorized |
|
|
460
|
+
| `KeyLimitExceeded` | 7 | Too many keys stored |
|
|
461
|
+
| `NotFound` | 8 | Key not found |
|
|
462
|
+
| `TooManyRequests` | 9 | Rate limited |
|
|
463
|
+
| `ServiceUnavailable` | 10 | Service down |
|
|
464
|
+
| `Conflict` | 11 | Write-lock conflict |
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Asset Store Building Blocks
|
|
469
|
+
|
|
470
|
+
The following Building Blocks from the Unity Asset Store demonstrate Cloud Save patterns:
|
|
471
|
+
|
|
472
|
+
- **Achievements Building Block** — Reads/writes achievement records in Protected buckets via Cloud Code, with Access Control denying direct player writes. [Asset Store](https://assetstore.unity.com/packages/essentials/tutorial-projects/unity-building-block-achievements-341918)
|
|
473
|
+
- **Player Account Building Block** — Stores player profile data in Default and Public access classes with direct client writes for non-sensitive data. [Asset Store](https://assetstore.unity.com/packages/essentials/tutorial-projects/unity-building-block-player-account-341928)
|
|
474
|
+
- **Leaderboards Building Block** — Uses Cloud Save for player score data alongside Cloud Code modules. [Asset Store](https://assetstore.unity.com/packages/essentials/tutorial-projects/unity-building-block-leaderboards-341926)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Deployment Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Overview](#overview)
|
|
6
|
+
- [Supported Service Integrations](#supported-service-integrations)
|
|
7
|
+
- [Workflow](#workflow)
|
|
8
|
+
- [Deployment Definitions (.ddef)](#deployment-definitions-ddef)
|
|
9
|
+
- [JSON Schema](#json-schema)
|
|
10
|
+
- [Behavior](#behavior)
|
|
11
|
+
- [Programmatic API](#programmatic-api)
|
|
12
|
+
- [Entry Point: Deployments.Instance](#entry-point-deploymentsinstance)
|
|
13
|
+
- [IDeploymentWindow](#ideploymentwindow)
|
|
14
|
+
- [DeploymentResult and DeploymentStatus](#deploymentresult-and-deploymentstatus)
|
|
15
|
+
- [SeverityLevel](#severitylevel)
|
|
16
|
+
- [DeploymentProvider (Abstract)](#deploymentprovider-abstract)
|
|
17
|
+
- [IDeploymentItem](#ideploymentitem)
|
|
18
|
+
- [Code Template -- Register a Custom Provider](#code-template--register-a-custom-provider)
|
|
19
|
+
- [Code Template -- Trigger Deployment Programmatically](#code-template--trigger-deployment-programmatically)
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
Editor-only package providing the **Deployment Window** (Services > Deployment). Deploys cloud resources for multiple UGS services from one place, without leaving the Unity Editor.
|
|
24
|
+
|
|
25
|
+
- **Package:** `com.unity.services.deployment`
|
|
26
|
+
- This package is Editor-only. Runtime code does not reference it.
|
|
27
|
+
|
|
28
|
+
## Supported Service Integrations
|
|
29
|
+
|
|
30
|
+
Each service package registers its own file types with the Deployment Window. Install the relevant service package to enable its file types.
|
|
31
|
+
|
|
32
|
+
| Service | Package | File Type(s) | Min Version |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| Cloud Code Scripts | `com.unity.services.cloudcode` | `.js` | 2.1.0 |
|
|
35
|
+
| Cloud Code C# Modules | `com.unity.services.cloudcode` | `.ccmr` | 2.5.0 |
|
|
36
|
+
| Remote Config | `com.unity.remote-config` | `.rc` | 3.2.0 |
|
|
37
|
+
| Economy | `com.unity.services.economy` | `.ecc`, `.eci`, `.ecv`, `.ecr` | 3.2.1 |
|
|
38
|
+
| Leaderboards | `com.unity.services.leaderboards` | `.lb` | 2.0.0 |
|
|
39
|
+
| Game Server Hosting | `com.unity.services.multiplayer` | `.gsh` | 1.1.0 |
|
|
40
|
+
| Matchmaker | `com.unity.services.multiplayer` | `.mmq` | 1.0 |
|
|
41
|
+
| Game Overrides | `com.unity.services.tooling` | `.ugo` | 1.3.0 |
|
|
42
|
+
| Access Control | `com.unity.services.tooling` | `.ac` | 1.0 |
|
|
43
|
+
| Deployment Definitions | `com.unity.services.deployment` | `.ddef` | -- |
|
|
44
|
+
|
|
45
|
+
## Workflow
|
|
46
|
+
|
|
47
|
+
1. Add `com.unity.services.deployment` via Package Manager.
|
|
48
|
+
2. Open **Services > Deployment**.
|
|
49
|
+
3. Select the target environment in the dropdown.
|
|
50
|
+
4. Choose files to deploy (or select all).
|
|
51
|
+
5. Click **Deploy**.
|
|
52
|
+
|
|
53
|
+
## Deployment Definitions (.ddef)
|
|
54
|
+
|
|
55
|
+
A Deployment Definition scopes the Deployment Window to a subset of files. Useful for large projects with multiple environments or teams.
|
|
56
|
+
|
|
57
|
+
**File extension:** `.ddef`
|
|
58
|
+
|
|
59
|
+
**Create via:** Right-click in Project window > Create > Unity Gaming Services > Deployment Definition
|
|
60
|
+
|
|
61
|
+
### JSON Schema
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"name": "GameplayServices",
|
|
66
|
+
"excludePaths": [
|
|
67
|
+
"Assets/CloudCode/Experimental/**",
|
|
68
|
+
"Assets/Config/Archive/**"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Field | Type | Description |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `name` | `string` | Display name shown in the Deployment Window |
|
|
76
|
+
| `excludePaths` | `string[]` | Glob patterns for files/folders to skip |
|
|
77
|
+
|
|
78
|
+
### Behavior
|
|
79
|
+
|
|
80
|
+
- **Without a `.ddef`:** the Deployment Window discovers and deploys all UGS config files under `Assets/`.
|
|
81
|
+
- **With a `.ddef` selected:** only files within the definition's scope (minus excluded paths) are shown and deployed.
|
|
82
|
+
- Multiple `.ddef` files can exist in one project; select which one to use in the Deployment Window dropdown.
|
|
83
|
+
|
|
84
|
+
## Programmatic API
|
|
85
|
+
|
|
86
|
+
The separate **`com.unity.services.deployment.api`** package (v1.1) provides programmatic access to the Deployment Window. All types in the main `com.unity.services.deployment` package are `internal` -- there is no public C# API from the main package.
|
|
87
|
+
|
|
88
|
+
- **Namespace:** `Unity.Services.DeploymentApi.Editor`
|
|
89
|
+
|
|
90
|
+
### Entry Point: Deployments.Instance
|
|
91
|
+
|
|
92
|
+
| Member | Type | Description |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `Instance` | `Deployments` | Static singleton |
|
|
95
|
+
| `DeploymentProviders` | `ObservableCollection<DeploymentProvider>` | All registered service providers |
|
|
96
|
+
| `DeploymentWindow` | `IDeploymentWindow` | Programmatic window control |
|
|
97
|
+
| `EnvironmentProvider` | `IEnvironmentProvider` | Current environment |
|
|
98
|
+
| `ProjectIdProvider` | `IProjectIdentifierProvider` | Current project ID |
|
|
99
|
+
|
|
100
|
+
### IDeploymentWindow
|
|
101
|
+
|
|
102
|
+
| Member | Description |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `Deploy(items, token)` | Returns `Task<DeploymentResult<IDeploymentItem>>` -- deploy selected items |
|
|
105
|
+
| `Deploy(filePaths, token)` | Extension: deploy items by file path (same return type) |
|
|
106
|
+
| `GetAllDeploymentItems(includeDeploymentDefinitions)` | Extension: get all items across all providers |
|
|
107
|
+
| `GetFromFiles(filePaths)` | Get items matching given file paths |
|
|
108
|
+
| `GetDeploymentDefinitions()` | Get available `.ddef` items |
|
|
109
|
+
| `OpenWindow()` | Opens the Deployment Window (`EditorWindow`) |
|
|
110
|
+
| `GetChecked()` / `GetSelected()` | Get checked/selected items (window must be open) |
|
|
111
|
+
| `Check(items)` / `ClearChecked()` | Programmatically check items |
|
|
112
|
+
| `Select(items)` / `ClearSelection()` | Programmatically select items |
|
|
113
|
+
| `DeploymentStarting` | `event Action<IReadOnlyList<IDeploymentItem>>` -- before deployment |
|
|
114
|
+
| `DeploymentEnded` | `event Action<IReadOnlyList<IDeploymentItem>>` -- after deployment |
|
|
115
|
+
| `GetCurrentDeployment()` | Returns the active `DeploymentScope`, or null |
|
|
116
|
+
|
|
117
|
+
### DeploymentResult and DeploymentStatus
|
|
118
|
+
|
|
119
|
+
**DeploymentResult\<T\>**
|
|
120
|
+
|
|
121
|
+
| Member | Type | Description |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `Deployed` | `List<T>` | Items that were successfully deployed |
|
|
124
|
+
|
|
125
|
+
**DeploymentStatus** has static factory methods for common states:
|
|
126
|
+
|
|
127
|
+
| Factory | Description |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `Empty` / `UpToDate` / `ModifiedLocally` / `FailedToDeploy` | Static readonly instances |
|
|
130
|
+
| `GetDeployed(details)` / `GetDeploying(details)` | Deployment in-progress/complete |
|
|
131
|
+
| `GetFailedToDeploy(details)` / `GetFailedToFetch(details)` | Failure states |
|
|
132
|
+
| `GetFailedToLoad(e, path)` / `GetFailedToRead(e, path)` | File I/O failures |
|
|
133
|
+
| `GetFetched(details)` / `GetFetching(details)` | Fetch from remote states |
|
|
134
|
+
| `GetPartialDeploy(details)` / `GetPartialFetch(details)` | Partial completion |
|
|
135
|
+
|
|
136
|
+
### SeverityLevel
|
|
137
|
+
|
|
138
|
+
| Value | Meaning |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `None` (0) | No status |
|
|
141
|
+
| `Info` (1) | Informational |
|
|
142
|
+
| `Warning` (2) | Warning |
|
|
143
|
+
| `Error` (3) | Error |
|
|
144
|
+
| `Success` (4) | Success |
|
|
145
|
+
|
|
146
|
+
### DeploymentProvider (Abstract)
|
|
147
|
+
|
|
148
|
+
Subclass this to expose your own deployable assets to the Deployment Window.
|
|
149
|
+
|
|
150
|
+
| Member | Description |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `Service` | Display name for the service (abstract) |
|
|
153
|
+
| `DeployCommand` | Required deploy command (abstract) |
|
|
154
|
+
| `DeploymentItems` | `ObservableCollection<IDeploymentItem>` -- add/remove to populate the window |
|
|
155
|
+
| `Commands` | Additional context menu commands |
|
|
156
|
+
| `OpenCommand` | Double-click handler (optional) |
|
|
157
|
+
| `ValidateCommand` | Validation command (optional) |
|
|
158
|
+
| `SyncItemsWithRemoteCommand` | Sync-with-remote command (optional) |
|
|
159
|
+
|
|
160
|
+
### IDeploymentItem
|
|
161
|
+
|
|
162
|
+
| Member | Description |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `Name` | File name with extension |
|
|
165
|
+
| `Path` | Full asset path |
|
|
166
|
+
| `Progress` | Deploy progress 0-100 |
|
|
167
|
+
| `Status` | `DeploymentStatus` (message + severity) |
|
|
168
|
+
| `States` | `ObservableCollection<AssetState>` -- local asset validation states |
|
|
169
|
+
|
|
170
|
+
### Code Template -- Register a Custom Provider
|
|
171
|
+
|
|
172
|
+
```csharp
|
|
173
|
+
using UnityEditor;
|
|
174
|
+
using Unity.Services.DeploymentApi.Editor;
|
|
175
|
+
|
|
176
|
+
class MyServiceDeploymentProvider : DeploymentProvider
|
|
177
|
+
{
|
|
178
|
+
public override string Service => "MyService";
|
|
179
|
+
public override Command DeployCommand { get; } = new MyDeployCommand();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
[InitializeOnLoadMethod]
|
|
183
|
+
static void RegisterProvider()
|
|
184
|
+
{
|
|
185
|
+
Deployments.Instance.DeploymentProviders.Add(new MyServiceDeploymentProvider());
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> **Deploy commands that write to UGS services** must use `IAdminClient` from the
|
|
190
|
+
> `com.unity.services.apis` package (`com.unity.services.apis`). Authenticate with a service
|
|
191
|
+
> account via `adminClient.SetServiceAccount(keyId, keySecret)`, then call the appropriate
|
|
192
|
+
> admin API (e.g. `adminClient.CloudSaveData.SetCustomItem` for Cloud Save game data).
|
|
193
|
+
> See [apis.md](apis.md) for the full `IAdminClient` interface and code templates.
|
|
194
|
+
|
|
195
|
+
### Code Template -- Trigger Deployment Programmatically
|
|
196
|
+
|
|
197
|
+
```csharp
|
|
198
|
+
using Unity.Services.DeploymentApi.Editor;
|
|
199
|
+
|
|
200
|
+
// Deploy all items
|
|
201
|
+
var allItems = Deployments.Instance.DeploymentWindow.GetAllDeploymentItems();
|
|
202
|
+
var result = await Deployments.Instance.DeploymentWindow.Deploy(allItems);
|
|
203
|
+
|
|
204
|
+
// Deploy by file paths
|
|
205
|
+
await Deployments.Instance.DeploymentWindow.Deploy(new[] { "Assets/MyConfig.rc" });
|
|
206
|
+
|
|
207
|
+
// Listen for deployment events
|
|
208
|
+
Deployments.Instance.DeploymentWindow.DeploymentStarting += items =>
|
|
209
|
+
{
|
|
210
|
+
Debug.Log($"Deploying {items.Count} items...");
|
|
211
|
+
};
|
|
212
|
+
Deployments.Instance.DeploymentWindow.DeploymentEnded += items =>
|
|
213
|
+
{
|
|
214
|
+
Debug.Log($"Deployment complete: {items.Count} items");
|
|
215
|
+
};
|
|
216
|
+
```
|