@musashishao/agent-kit 1.9.0 → 1.9.1

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 (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,527 @@
1
+ ---
2
+ name: cloud-save-sync
3
+ description: Cloud save and cross-device synchronization. Firebase Cloud Save, PlayFab, conflict resolution, offline-first patterns, and data migration for Unity mobile games.
4
+ ---
5
+
6
+ # Cloud Save Sync Skill
7
+
8
+ > **Purpose**: Enable seamless cross-device gameplay progression.
9
+
10
+ ## When to Use
11
+ - Implementing cloud save functionality
12
+ - Enabling cross-device play
13
+ - Handling save conflicts
14
+ - Building offline-first games
15
+
16
+ ---
17
+
18
+ ## 1. Save Data Architecture
19
+
20
+ ### Flexible Save Structure
21
+ ```csharp
22
+ [System.Serializable]
23
+ public class CloudSaveData
24
+ {
25
+ // Metadata
26
+ public string playerId;
27
+ public string deviceId;
28
+ public long timestamp; // Unix timestamp
29
+ public int saveVersion; // For migration
30
+ public string checksum; // Data integrity
31
+
32
+ // Game Data
33
+ public PlayerProgress player;
34
+ public InventoryData inventory;
35
+ public QuestProgress quests;
36
+ public SettingsData settings;
37
+
38
+ // Custom
39
+ public Dictionary<string, string> customData;
40
+ }
41
+
42
+ [System.Serializable]
43
+ public class PlayerProgress
44
+ {
45
+ public int level;
46
+ public long experience;
47
+ public int gold;
48
+ public int gems;
49
+ public string currentRealm; // For cultivation games
50
+ public float[] position; // Last position
51
+ public long playTimeSeconds;
52
+ }
53
+
54
+ [System.Serializable]
55
+ public class InventoryData
56
+ {
57
+ public InventorySlot[] slots;
58
+ public EquipmentSlot[] equipment;
59
+ public string[] unlockedRecipes;
60
+ public CardDeckData[] decks; // For card games
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 2. Cloud Save Manager
67
+
68
+ ### Core Implementation
69
+ ```csharp
70
+ public class CloudSaveManager : MonoBehaviour
71
+ {
72
+ public static CloudSaveManager Instance { get; private set; }
73
+
74
+ [SerializeField] private CloudProvider _provider;
75
+ [SerializeField] private float _autoSaveInterval = 300f; // 5 minutes
76
+
77
+ public bool IsOnline { get; private set; }
78
+ public bool IsSyncing { get; private set; }
79
+ public DateTime LastSyncTime { get; private set; }
80
+
81
+ public event Action<CloudSaveData> OnSaveLoaded;
82
+ public event Action OnSaveUploaded;
83
+ public event Action<SaveConflict> OnConflictDetected;
84
+ public event Action<string> OnSyncError;
85
+
86
+ private CloudSaveData _localSave;
87
+ private CloudSaveData _cloudSave;
88
+
89
+ private void Start()
90
+ {
91
+ StartCoroutine(AutoSaveLoop());
92
+ StartCoroutine(MonitorConnectivity());
93
+ }
94
+
95
+ #region Save Operations
96
+
97
+ public async Task SaveLocal(CloudSaveData data)
98
+ {
99
+ data.timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
100
+ data.deviceId = SystemInfo.deviceUniqueIdentifier;
101
+ data.checksum = CalculateChecksum(data);
102
+
103
+ string json = JsonUtility.ToJson(data);
104
+
105
+ // Save to local storage
106
+ PlayerPrefs.SetString("local_save", json);
107
+ PlayerPrefs.Save();
108
+
109
+ _localSave = data;
110
+
111
+ // Attempt cloud sync if online
112
+ if (IsOnline)
113
+ {
114
+ await SyncToCloud(data);
115
+ }
116
+ }
117
+
118
+ public async Task<CloudSaveData> LoadSave()
119
+ {
120
+ // Load local
121
+ string localJson = PlayerPrefs.GetString("local_save", "");
122
+ _localSave = string.IsNullOrEmpty(localJson)
123
+ ? new CloudSaveData()
124
+ : JsonUtility.FromJson<CloudSaveData>(localJson);
125
+
126
+ // Try load cloud
127
+ if (IsOnline)
128
+ {
129
+ try
130
+ {
131
+ _cloudSave = await _provider.FetchCloudSave();
132
+
133
+ // Check for conflict
134
+ if (_cloudSave != null && _localSave.timestamp > 0)
135
+ {
136
+ var resolution = ResolveConflict(_localSave, _cloudSave);
137
+ return resolution;
138
+ }
139
+
140
+ return _cloudSave ?? _localSave;
141
+ }
142
+ catch (Exception e)
143
+ {
144
+ OnSyncError?.Invoke(e.Message);
145
+ return _localSave;
146
+ }
147
+ }
148
+
149
+ return _localSave;
150
+ }
151
+
152
+ private async Task SyncToCloud(CloudSaveData data)
153
+ {
154
+ IsSyncing = true;
155
+
156
+ try
157
+ {
158
+ await _provider.UploadSave(data);
159
+ LastSyncTime = DateTime.UtcNow;
160
+ OnSaveUploaded?.Invoke();
161
+ }
162
+ catch (Exception e)
163
+ {
164
+ OnSyncError?.Invoke($"Cloud sync failed: {e.Message}");
165
+ }
166
+ finally
167
+ {
168
+ IsSyncing = false;
169
+ }
170
+ }
171
+
172
+ #endregion
173
+
174
+ #region Conflict Resolution
175
+
176
+ private CloudSaveData ResolveConflict(CloudSaveData local, CloudSaveData cloud)
177
+ {
178
+ // Same timestamp = no conflict
179
+ if (local.timestamp == cloud.timestamp)
180
+ return local;
181
+
182
+ // Auto-resolve: newer wins
183
+ if (Mathf.Abs(local.timestamp - cloud.timestamp) < 60) // Within 1 minute
184
+ {
185
+ return local.timestamp > cloud.timestamp ? local : cloud;
186
+ }
187
+
188
+ // Significant difference: ask player
189
+ var conflict = new SaveConflict
190
+ {
191
+ localSave = local,
192
+ cloudSave = cloud,
193
+ localTime = DateTimeOffset.FromUnixTimeSeconds(local.timestamp).LocalDateTime,
194
+ cloudTime = DateTimeOffset.FromUnixTimeSeconds(cloud.timestamp).LocalDateTime
195
+ };
196
+
197
+ OnConflictDetected?.Invoke(conflict);
198
+
199
+ // Default to newer while waiting for resolution
200
+ return local.timestamp > cloud.timestamp ? local : cloud;
201
+ }
202
+
203
+ public void ResolveConflictWith(ConflictChoice choice)
204
+ {
205
+ CloudSaveData chosen = choice switch
206
+ {
207
+ ConflictChoice.KeepLocal => _localSave,
208
+ ConflictChoice.KeepCloud => _cloudSave,
209
+ ConflictChoice.MergePreferLocal => MergeSaves(_localSave, _cloudSave),
210
+ ConflictChoice.MergePreferCloud => MergeSaves(_cloudSave, _localSave),
211
+ _ => _localSave
212
+ };
213
+
214
+ _localSave = chosen;
215
+ SaveLocal(chosen);
216
+ }
217
+
218
+ private CloudSaveData MergeSaves(CloudSaveData primary, CloudSaveData secondary)
219
+ {
220
+ var merged = JsonUtility.FromJson<CloudSaveData>(JsonUtility.ToJson(primary));
221
+
222
+ // Take higher values
223
+ merged.player.level = Mathf.Max(primary.player.level, secondary.player.level);
224
+ merged.player.experience = Mathf.Max(primary.player.experience, secondary.player.experience);
225
+ merged.player.gold = Mathf.Max(primary.player.gold, secondary.player.gold);
226
+ merged.player.playTimeSeconds = Mathf.Max(primary.player.playTimeSeconds, secondary.player.playTimeSeconds);
227
+
228
+ // Merge inventory (union of items)
229
+ merged.inventory = MergeInventories(primary.inventory, secondary.inventory);
230
+
231
+ // Merge quests (completed on either = completed)
232
+ merged.quests = MergeQuests(primary.quests, secondary.quests);
233
+
234
+ return merged;
235
+ }
236
+
237
+ #endregion
238
+ }
239
+
240
+ public struct SaveConflict
241
+ {
242
+ public CloudSaveData localSave;
243
+ public CloudSaveData cloudSave;
244
+ public DateTime localTime;
245
+ public DateTime cloudTime;
246
+ }
247
+
248
+ public enum ConflictChoice
249
+ {
250
+ KeepLocal,
251
+ KeepCloud,
252
+ MergePreferLocal,
253
+ MergePreferCloud
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 3. Firebase Cloud Save
260
+
261
+ ### Firebase Provider
262
+ ```csharp
263
+ public class FirebaseCloudProvider : CloudProvider
264
+ {
265
+ private DatabaseReference _db;
266
+ private FirebaseAuth _auth;
267
+
268
+ public override async Task Initialize()
269
+ {
270
+ var dependencyStatus = await FirebaseApp.CheckAndFixDependenciesAsync();
271
+ if (dependencyStatus != DependencyStatus.Available)
272
+ {
273
+ throw new Exception($"Firebase error: {dependencyStatus}");
274
+ }
275
+
276
+ _auth = FirebaseAuth.DefaultInstance;
277
+ _db = FirebaseDatabase.DefaultInstance.RootReference;
278
+ }
279
+
280
+ public override async Task<CloudSaveData> FetchCloudSave()
281
+ {
282
+ string userId = _auth.CurrentUser.UserId;
283
+
284
+ var snapshot = await _db.Child("saves").Child(userId).GetValueAsync();
285
+
286
+ if (!snapshot.Exists)
287
+ return null;
288
+
289
+ string json = snapshot.GetRawJsonValue();
290
+ return JsonUtility.FromJson<CloudSaveData>(json);
291
+ }
292
+
293
+ public override async Task UploadSave(CloudSaveData data)
294
+ {
295
+ string userId = _auth.CurrentUser.UserId;
296
+ string json = JsonUtility.ToJson(data);
297
+
298
+ await _db.Child("saves").Child(userId).SetRawJsonValueAsync(json);
299
+ }
300
+
301
+ public override async Task DeleteSave()
302
+ {
303
+ string userId = _auth.CurrentUser.UserId;
304
+ await _db.Child("saves").Child(userId).RemoveValueAsync();
305
+ }
306
+ }
307
+ ```
308
+
309
+ ### Firebase Security Rules
310
+ ```json
311
+ {
312
+ "rules": {
313
+ "saves": {
314
+ "$userId": {
315
+ ".read": "$userId === auth.uid",
316
+ ".write": "$userId === auth.uid",
317
+ ".validate": "newData.hasChildren(['playerId', 'timestamp', 'saveVersion'])"
318
+ }
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ## 4. PlayFab Integration
327
+
328
+ ### PlayFab Provider
329
+ ```csharp
330
+ public class PlayFabCloudProvider : CloudProvider
331
+ {
332
+ public override async Task<CloudSaveData> FetchCloudSave()
333
+ {
334
+ var tcs = new TaskCompletionSource<CloudSaveData>();
335
+
336
+ PlayFabClientAPI.GetUserData(
337
+ new GetUserDataRequest { Keys = new List<string> { "save_data" } },
338
+ result =>
339
+ {
340
+ if (result.Data.TryGetValue("save_data", out var record))
341
+ {
342
+ var data = JsonUtility.FromJson<CloudSaveData>(record.Value);
343
+ tcs.SetResult(data);
344
+ }
345
+ else
346
+ {
347
+ tcs.SetResult(null);
348
+ }
349
+ },
350
+ error => tcs.SetException(new Exception(error.ErrorMessage))
351
+ );
352
+
353
+ return await tcs.Task;
354
+ }
355
+
356
+ public override async Task UploadSave(CloudSaveData data)
357
+ {
358
+ var tcs = new TaskCompletionSource<bool>();
359
+
360
+ string json = JsonUtility.ToJson(data);
361
+
362
+ PlayFabClientAPI.UpdateUserData(
363
+ new UpdateUserDataRequest
364
+ {
365
+ Data = new Dictionary<string, string> { { "save_data", json } }
366
+ },
367
+ result => tcs.SetResult(true),
368
+ error => tcs.SetException(new Exception(error.ErrorMessage))
369
+ );
370
+
371
+ await tcs.Task;
372
+ }
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ## 5. Offline-First Pattern
379
+
380
+ ### Queue-Based Sync
381
+ ```csharp
382
+ public class OfflineQueueManager : MonoBehaviour
383
+ {
384
+ private Queue<PendingOperation> _pendingOperations = new();
385
+ private bool _isProcessing = false;
386
+
387
+ public void QueueSave(CloudSaveData data)
388
+ {
389
+ _pendingOperations.Enqueue(new PendingOperation
390
+ {
391
+ type = OperationType.Save,
392
+ data = data,
393
+ timestamp = DateTime.UtcNow
394
+ });
395
+
396
+ // Save queue locally
397
+ PersistQueue();
398
+
399
+ // Try process if online
400
+ if (CloudSaveManager.Instance.IsOnline)
401
+ {
402
+ ProcessQueue();
403
+ }
404
+ }
405
+
406
+ private async void ProcessQueue()
407
+ {
408
+ if (_isProcessing || _pendingOperations.Count == 0)
409
+ return;
410
+
411
+ _isProcessing = true;
412
+
413
+ while (_pendingOperations.Count > 0 && CloudSaveManager.Instance.IsOnline)
414
+ {
415
+ var operation = _pendingOperations.Peek();
416
+
417
+ try
418
+ {
419
+ await ExecuteOperation(operation);
420
+ _pendingOperations.Dequeue();
421
+ PersistQueue();
422
+ }
423
+ catch
424
+ {
425
+ // Keep in queue for retry
426
+ break;
427
+ }
428
+ }
429
+
430
+ _isProcessing = false;
431
+ }
432
+
433
+ private void OnConnectivityRestored()
434
+ {
435
+ ProcessQueue();
436
+ }
437
+ }
438
+
439
+ [System.Serializable]
440
+ public class PendingOperation
441
+ {
442
+ public OperationType type;
443
+ public CloudSaveData data;
444
+ public DateTime timestamp;
445
+ }
446
+
447
+ public enum OperationType { Save, Delete, Achievement }
448
+ ```
449
+
450
+ ---
451
+
452
+ ## 6. Data Migration
453
+
454
+ ### Version Migration
455
+ ```csharp
456
+ public class SaveMigrator
457
+ {
458
+ private static readonly int CURRENT_VERSION = 3;
459
+
460
+ public static CloudSaveData Migrate(CloudSaveData data)
461
+ {
462
+ while (data.saveVersion < CURRENT_VERSION)
463
+ {
464
+ data = MigrateStep(data);
465
+ }
466
+
467
+ return data;
468
+ }
469
+
470
+ private static CloudSaveData MigrateStep(CloudSaveData data)
471
+ {
472
+ switch (data.saveVersion)
473
+ {
474
+ case 1:
475
+ return MigrateV1ToV2(data);
476
+ case 2:
477
+ return MigrateV2ToV3(data);
478
+ default:
479
+ return data;
480
+ }
481
+ }
482
+
483
+ private static CloudSaveData MigrateV1ToV2(CloudSaveData data)
484
+ {
485
+ // V2 added gems currency
486
+ data.player.gems = 0;
487
+
488
+ // V2 split inventory into slots
489
+ if (data.customData.TryGetValue("old_inventory", out string oldInv))
490
+ {
491
+ data.inventory = ConvertOldInventory(oldInv);
492
+ data.customData.Remove("old_inventory");
493
+ }
494
+
495
+ data.saveVersion = 2;
496
+ return data;
497
+ }
498
+
499
+ private static CloudSaveData MigrateV2ToV3(CloudSaveData data)
500
+ {
501
+ // V3 added card decks
502
+ data.inventory.decks = new CardDeckData[0];
503
+
504
+ data.saveVersion = 3;
505
+ return data;
506
+ }
507
+ }
508
+ ```
509
+
510
+ ---
511
+
512
+ ## Anti-Patterns
513
+
514
+ | ❌ Don't | ✅ Do |
515
+ |----------|-------|
516
+ | Save on every change | Batch saves, use intervals |
517
+ | Ignore offline | Queue operations for retry |
518
+ | Auto-resolve conflicts silently | Show player clear options |
519
+ | No checksums | Validate data integrity |
520
+ | No versioning | Support migration |
521
+
522
+ ---
523
+
524
+ ## Related Skills
525
+ - `game-development/game-state-manager` - Local save patterns
526
+ - `firebase` - Firebase integration
527
+ - `game-development/liveops` - Remote config