mobilegamedevcloudregression

Cloud Save в мобильной игре — чек-лист тестирования

Игрок прошёл первые 30 уровней на работе с iPad. Вечером запускает игру на iPhone — прогресса нет. Удаляет, переустанавливает — нет. Пишет в support, ставит одну звезду в сторе. Это худший пользовательский опыт в мобильной игре, и почти всегда виноват непротестированный cloud save.

Cloud save звучит просто — сохранили JSON в облако, достали на другом девайсе. На практике это 5+ систем, которые ломаются по-разному: платформенные API ведут себя не так, как в документации; конфликты разрешаются неочевидно; миграция между платформами почти всегда теряет покупки; save-файл может прийти повреждённым, а игрок — без бэкапа.

Этот гайд — что именно тестировать в cloud save мобильной игры.

1. Платформенные API: три подхода

iCloud (Apple). Два варианта:

  • NSUbiquitousKeyValueStore — лимит 1 МБ суммарно на приложение. Подходит только для очень компактных save’ов или метаданных (последний пройденный уровень, монеты). См. документацию Apple.
  • CloudKit — полноценная база, до 10 ГБ в free tier пользователя. Поддерживает разрешение конфликтов через CKRecord и change tokens. Для нормального save в игре — только этот вариант. См. CloudKit docs.

Google Play Games Services — Saved Games. До 3 МБ на snapshot, до 4 snapshot’ов на игру (можно использовать как backup slots). Конфликты — на стороне разработчика через SnapshotConflictResolutionPolicy. См. Saved Games guide.

Свой backend (Firebase / собственный API). Полный контроль, но больше сценариев теста. Самый частый выбор для cross-platform игр.

Что тестировать:

  • Save проходит при медленной сети (>2 сек latency) и реконнекте.
  • API-лимиты по размеру: что происходит, если save вырос до 4 МБ в Google Play Games — silent fail или ошибка?
  • Rate limits — Apple и Google не любят, когда пишешь в облако каждые 5 секунд.
  • Поведение когда iCloud или Google-аккаунт не залогинен или отключён в системе.

2. Конфликт между девайсами

Самый частый баг — игрок играет одновременно на двух девайсах (iPad на зарядке + iPhone в дороге), оба пишут в cloud. Что мержим?

Стратегии разрешения:

  • Last-write-wins — просто, но теряет прогресс одного из девайсов. Подходит только для игр с линейным прогрессом.
  • Highest-progress-wins — сравниваем по уровню, счёту, timestamp последнего события. Игрок не теряет прогресс, но может потерять покупки из «откатанной» сессии.
  • Merge logic — объединяем по полям (валюты — max, открытые уровни — union, инвентарь — union). Сложнее, но безопаснее.

Тест-кейсы:

  • Девайс A прошёл уровень 10, девайс B параллельно купил предмет. Запускаем поочерёдно — игрок не должен потерять ни уровень, ни покупку.
  • Девайс A в офлайне 3 дня, накопил прогресс. Девайс B всё это время играл в онлайн. Девайс A выходит в сеть — что побеждает?
  • Одновременный logout/login на двух девайсах в течение 1 секунды.
  • Conflict-resolution UI: показываем игроку выбор «оставить версию A / оставить версию B» или решаем сами тихо? Если показываем — UI должен быть понятен игроку, не быть дампом JSON.

3. Cross-platform миграция iOS ↔ Android

Самая болезненная область. Игрок купил премиум-валюту в iOS-версии, переехал на Android — где деньги?

Что переносится через cloud save (вне зависимости от стора):

  • Прогресс уровней, открытые контент, кастомизации, нагембейпленные ресурсы.
  • Игровая валюта, заработанная игроком (не купленная за реальные деньги).

Что НЕ переносится автоматически:

  • Купленная за реальные деньги валюта — IAP привязан к Apple ID и Google account. Если игрок купил на iOS и перешёл на Android, у Google нет записи о покупке. Решение: серверный кошелёк (валюта живёт на backend’е, IAP receipt верифицируется и пополняет cloud-кошелёк, а не локальный).
  • Активные подписки — даже технически нельзя переносить из-за политик сторов.

Тест-кейсы:

  • iOS → Android: создать прогресс + IAP на iOS, залогиниться той же учёткой (Apple ID, Google Sign-In, Facebook, custom account) на Android. Проверить: прогресс есть, IAP-валюта есть (если используется серверный кошелёк), активная подписка корректно отображается как «куплена на другой платформе».
  • Android → iOS — аналогично.
  • Игрок без linked-аккаунта (играет «гостем») переходит на новый девайс — прогресс должен предложиться к восстановлению через привязку аккаунта.

4. Save corruption и backup slots

Save может прийти битым: упала сеть в середине записи, кончилось место на диске, OOM при сериализации, баг в новой версии клиента, который пишет невалидный JSON. Что дальше?

Защита:

  • Чек-сумма (SHA-256 или хотя бы CRC32) в начале save’а — при чтении сверяем.
  • Atomic writes — сначала пишем в save.tmp, потом переименовываем в save.json. Прерванная запись не оставит битый файл.
  • Несколько слотов (Google Play Games даёт 4 snapshot’а — используйте как rolling backup).
  • Версионирование — каждый save содержит schema_version, чтобы новый клиент знал, как читать старый формат.

Тест-кейсы:

  • Принудительно скормить игре битый JSON (через подмену файла на устройстве) — приложение не должно крашиться, должно восстановиться из бэкапа.
  • Прервать запись save’а в середине (kill процесса, выключение питания) — следующий запуск не должен потерять прогресс.
  • Save из старой версии игры открывается в новой версии (migration).
  • Save из новой версии открывается в старой — клиент должен корректно отказать, а не интерпретировать поля наугад.

5. Удаление аккаунта и отключение iCloud / Google Play

Сценарии:

  • Игрок удалил Apple ID или вышел из iCloud в настройках iPhone. Игра запускается — что показывает? Обычно — локальный save без облака. Худший вариант — приложение крашится при попытке доступа к недоступному CloudKit-контейнеру.
  • Игрок отключил «iCloud Drive» для конкретного приложения в Settings → iCloud.
  • Игрок удалил игру, переустановил, не залогинен в Apple ID. Save должен предложиться к восстановлению через альтернативный login (Game Center, Facebook, custom).
  • На Android — игрок отозвал permission Google Play Games для игры.

6. Восстановление прогресса на новой установке

Главный момент пользовательского опыта. Игрок переустанавливает игру или меняет девайс — первый запуск должен вернуть прогресс.

Что тестировать:

  • Cold start: чистая установка → логин → cloud save подтянулся → прогресс восстановлен.
  • Если cloud save больше, чем тутор-прогресс новой установки — что мержим? Если игра уже включила игрока в туториал, а потом подтянулся save, нужно корректно перейти к восстановленному состоянию, а не оставить два прогресса.
  • Скорость восстановления (download save 200 КБ на 3G не должен занимать 30 секунд).
  • Loader и индикация — игрок не должен думать, что прогресс пропал.

7. Edge cases, которые ловят боль

  • Offline → online merge: игрок неделю играл оффлайн, накопил прогресс. Вышел в сеть — клиент пытается синхронизировать. Если в cloud есть более свежий save с другого девайса, что побеждает?
  • Версия save новее версии клиента: игрок обновился на iOS, попал в новую схему save’а. На Android’е старая версия клиента подтягивает этот save — нужно показать «обнови игру», а не ломаться.
  • Downgrade игры (тестовая ситуация — выкатили багу, откатываем версию). Save новой версии не должен заблокировать игрока в старой.
  • Часы на устройстве: timestamp save’а используется для разрешения конфликта. Игрок переводит часы на год назад — может «победить» более свежий cloud save. Используйте server timestamp при наличии backend’а.
  • Семейный аккаунт: дети iCloud Family могут шарить покупки, но не save’ы. Проверить, что save конкретного устройства не уезжает в семейный шаринг.

8. Инструменты для тестирования

  • Xcode → Devices and Simulators → Open Console — логи приложения с реального девайса, видно CloudKit-ошибки.
  • xcrun simctl — управление iCloud-аккаунтом симулятора.
  • Android Debug Bridge (adb shell pm clear <package>) — чистит локальные данные. Удобный способ симулировать переустановку без перезалива APK.
  • Прокси (Proxyman / Charles) — перехват запросов к собственному backend’у, подмена ответов (например, вернуть «save corrupted» от сервера для проверки error UI).
  • Network Link Conditioner (macOS / iOS) — эмуляция плохой сети при синхронизации.
  • Тестовые Apple ID и Google-аккаунты — несколько штук в команде, чтобы покрыть кросс-аккаунт сценарии.

9. Компактный чек-лист

Базовый минимум перед релизом:

  • Save проходит на 2G/3G и при дропе сети.
  • Конфликт двух девайсов разрешается по продуманной стратегии (документировать какой).
  • Cross-platform миграция: прогресс — да, IAP-валюта — через серверный кошелёк, подписка — корректное состояние.
  • Битый save восстанавливается из backup, приложение не крашится.
  • iCloud / Google аккаунт не залогинен — игра не падает, fallback на локальный save.
  • Переустановка возвращает прогресс за <10 секунд на 4G.
  • Save из новой версии не ломает старого клиента.
  • Часы устройства не используются как единственный источник для conflict resolution.

Cloud save — это та часть игры, где провал стоит игроку часов прогресса, а команде — однозвёздочного review в сторе. Стоит тестировать как платежи.