← GameSmile

GameSmile SDK 1.4

Общий интерфейс сохранений, игрока, входа, достижений, лидербордов, рекламы и жизненного цикла игры. Работает в iframe GameSmile, включая песочницу без доступа к cookies и localStorage. Существующий контракт сохранений 1.0 совместим. Коммерческая реклама и платежи пока не подключены; рекламные вызовы временно обслуживает собственное продвижение игр GameSmile.

Скачать SDK и автономный гостевой пример ZIP · Типы TypeScript

Открыть лабораторию: сбои, конфликты и восстановление. Тестовое облако и аккаунты находятся только в памяти страницы, реальный API не вызывается. Можно подключить свою HTML5-сборку и скачать журнал без содержимого сохранений. Автопроверка проверяет обработчик SDK, а не сертифицирует игру.

1. Подключите библиотеку

<script src="https://gamesmile.ru/static/gamesmile-sdk-v1.js"></script>

Можно скачать этот файл и положить в свой билд. Не используйте SDK GameSmile в сборке для Яндекса/VK: подключайте его в адаптере нашей площадки. Секреты и ключи API не нужны. Идентификатор игры назначает страница запуска, а не игровой код.

2. Сначала загрузите прогресс

const sdk = window.GameSmileSDK;
try {
  const info = await sdk.init();
  const result = await sdk.storage.load();
  // null означает подтверждённое отсутствие сохранения.
  const progress = result.data ?? { schemaVersion: 1, level: 1 };
  startGame(progress, { canSave: result.canSave });
} catch (error) {
  showRetry(error.code); // Не начинайте записывать пустой прогресс!
}

init() возвращает {version, game, player:{signed,name}, capabilities, maxSaveBytes}. Это снимок входа. После смены аккаунта перезагрузите игру: старый экземпляр SDK нельзя переиспользовать для нового пользователя.

3. Сохраните в ключевой точке

try {
  const saved = await sdk.storage.save(progress);
  showStatus(saved.synced ? 'Сохранено в облаке' : 'Только в этом браузере');
  if (saved.reloadRequired) showReloadPrompt();
} catch (error) {
  showSaveError(error.code);
}

Передавайте обычный JSON-объект до 256 КБ в UTF-8, включая собственный номер формата. Вложенные массивы разрешены. undefined, NaN/Infinity, BigInt, функции, Date, циклы, геттеры и toJSON отклоняются с INVALID_DATA: SDK не должен молча терять поля или менять их смысл. Сохранение заменяет объект целиком; SDK не знает, как объединить инвентарь или два прохождения. Данные копируются при вызове save, последующее изменение объекта не меняет запрос. Деньги и покупки нельзя подтверждать данными клиентского сейва.

Вызовы одного экземпляра SDK выполняются в порядке поступления, даже при Promise.all. Очередь вмещает 16 операций, включая выполняющуюся; переполнение возвращает BUSY. Используйте await для зависимых действий и сохраняйте в контрольных точках, не каждый кадр. Ошибка отдельной операции не отменяет остальные: результат каждого вызова нужно обработать.

Результаты и ошибки

При сетевой ошибке записи SDK пытается оставить отдельную локальную резервную копию и возвращает reloadRequired:true. При следующей загрузке pendingLocal:true сообщает о ней. Получите её через await sdk.storage.getPending() (ответ {data}), покажите игроку выбор между ней и актуальным облаком. После успешного load() выбранные данные можно передать в save(). Подтверждённая запись этой копии очищает её резерв. Автоматического слияния нет.

Перенос гостя после входа

Готовое окно выбора прогресса

Необязательный модуль gamesmile-sdk-recovery-v1.js подключается в HTML игры после SDK; типы также входят в ZIP. Он предлагает текущее сохранение, несинхронизированный резерв и перенос гостя при пустом облаке. Облако заменяется только по явно подписанной кнопке и с проверкой ревизии. Выбор облака не удаляет резерв; отмена ничего не записывает. При сбое или конфликте окно не повторяет запись автоматически.

pauseGame(); // остановите движок и собственные автосохранения
try {
  const result = await window.GameSmileSDKRecovery.open(window.GameSmileSDK, {
    describe: data => 'Уровень: ' + data.level // краткое описание для выбора
  });
  if (!result.cancelled) {
    // Проверьте schemaVersion и формат перед передачей данных движку.
    restoreGame(result.data, { canSave: result.canSave });
  }
} catch (error) {
  showSaveError(error.code);
}
// Возобновляйте игру только после безопасного выбора и с учётом паузы SDK.

Окно сначала перечитывает сохранение. Пока оно открыто, не вызывайте другие методы storage из игры и не продолжайте автосохранения. После отмены не записывайте прежний прогресс: сначала повторно выберите актуальную копию. Таймаут транспорта требует перезагрузки игры. Стенд доступен в ZIP по адресу static/sdk-lab-v1.html; тестовые данные исчезают при перезагрузке всей лаборатории, но сохраняются при перезапуске её iframe.

После входа перезагрузите игру и вызовите load(). Если guestAvailable и data === null, покажите отдельную кнопку «Перенести гостевой прогресс». Только по её нажатию вызовите await sdk.storage.importGuest(). Площадка запросит подтверждение; уже существующее облако не перезаписывается. После успешного переноса вызовите load(). Отмена возвращает {status:'cancelled', imported:false}.

Автоматического переноса старых произвольных ключей игр нет: их владелец неизвестен. Подключение SDK само по себе также не переносит сейвы Яндекса/VK.

Живой пример

Песочница ниже сохраняет только отдельный демонстрационный счётчик в этом браузере, даже если вы вошли на сайт. В аккаунт ничего не отправляется. Нажмите «+1», сохраните и перезагрузите пример.

Перед публикацией

Проверьте гостя → перезагрузку, вход → новое устройство, выход → другой аккаунт, две вкладки, отказ сети и обновление формата сейва. Гостевой стенд доступен в ZIP; авторизованная приёмка требует тестового запуска на площадке. Ломающие изменения будут выходить отдельной версией SDK.

Сервисы версии 1.1

Аналитика версии 1.3

На рабочей странице игры features.analytics сообщает о подключении анонимной диагностики SDK. Вызывайте ready после загрузки ресурсов, start при начале партии и stop при выходе в меню или завершении. Повторный start без stop не создаёт ещё одну партию. В меню, собственном окне восстановления и на паузе движка вызывайте stop, затем start при продолжении: SDK не видит внутреннее состояние вашей игры.

Учитываются сессии SDK, готовность, старты/остановки, активное время и количество сбоев запросов SDK. Скрытая вкладка и пауза площадки исключаются; большой разрыв таймера не засчитывается как игра. Отчёт отправляется примерно раз в 15 секунд и при переходах. Это оценка: при закрытии вкладки/потере сети возможна потеря последних секунд. Ошибки аналитики не блокируют игру. Повторные отчёты не суммируются дважды. Нет ID игрока, текста исключений, URL или содержимого сейва; случайный ID действует на один экземпляр игры, записи хранятся до 90 дней с очисткой при новых сессиях.

Графики доступны владельцу площадки и одобренной студии только по её играм. Дни группируются по началу сессии в UTC; сессии не равны уникальным людям, stop не означает победу. Данные клиента не являются античитом. Игры со старым bridge не включаются автоматически; ошибка загрузки до инициализации SDK в этот отчёт не попадает. Лаборатория и гостевой пример не отправляют настоящую аналитику.

События игры в версии 1.4

После lifecycle.ready() и lifecycle.start() игра может отмечать этапы вызовом analytics.track(name,key,value?). Допустимые name: level_start, level_complete, game_over, checkpoint, mechanic. Key — стабильный технический код до 40 знаков, например level-3; необязательное value — целое число. Не передавайте имена, тексты, URL, содержимое сохранений и другие персональные данные.

await sdk.analytics.track('level_start', 'level-3');
await sdk.analytics.track('level_complete', 'level-3', 87);
await sdk.analytics.track('mechanic', 'hint-used');

Событие принимается только от уже заведённой анонимной SDK-сессии этой игры, не более 1000 записей за сессию. Ошибка аналитики не должна останавливать игру: при необходимости перехватывайте отказ. Эти данные помогают найти место ухода игроков, но не являются античитом или подтверждением награды.

Игровые ивенты версии 1.4.1

Студия назначает расписание в карточке своей игры. Игра получает только активные ивенты через sdk.gameEvents.getActive(). Идентификатор игры подставляет площадка; передавать адрес API или ключ игры из игрового кода не нужно. Результат содержит время сервера, рекомендуемый интервал обновления и массив событий. Игра должна сама реализовать указанный сценарий и параметры; SDK не создаёт механику игры.

const sdk = window.GameSmileSDK;
const info = await sdk.init();
if (info.features.gameEvents) {
  const active = await sdk.gameEvents.getActive();
  for (const event of active.events) {
    if (event.scenario === 'timed-run-v1') {
      // Игра показывает уже реализованный в ней режим с event.parameters.
    }
  }
  // Во время долгой сессии повторите запрос после active.refreshAfterSeconds.
}

Если запрос не удался, обычная игра продолжается без ивента. Этот ответ не подтверждает прохождение и не даёт права на предметную награду. В автономном гостевом стенде сервис недоступен.

Проверяйте (await sdk.init()).features. Наличие метода в библиотеке не означает, что услуга подключена. Поля: storage, player, lifecycle, achievements, leaderboards, fullscreen, analytics, gameEvents, ads, payments. Возможности зависят от игры и браузера; даже доступный сервис может временно отказать.

const info = await sdk.init();
sdk.on('pause', () => { pauseGame(); muteAudio(); });
sdk.on('resume', () => { resumeGame(); restoreAudio(); });
sdk.on('accountChanged', () => showReloadPrompt());
await sdk.lifecycle.ready(); // ресурсы загружены, можно играть
await sdk.lifecycle.start(); // началась партия
await sdk.lifecycle.stop();  // меню / завершение партии

on() возвращает функцию отписки. Пауза учитывает одновременно скрытую вкладку, окно входа, смену аккаунта и потерянный ответ площадки. Подписчик pause немедленно получает текущее состояние, если игра уже на паузе, в том числе при запуске в фоновой вкладке. SDK сообщает о состоянии, но сам не умеет остановить ваш движок или звук. При features.analytics события ready/start/stop участвуют в диагностике игровой активности; это не серверная проверка честности результата.

Реклама и покупки: обязательная граница

ads.showInterstitial() и ads.showRewarded() используют провайдер площадки. Сейчас это собственная карточка другой игры GameSmile; после подключения рекламной сети игровой код и вызов SDK не меняются. Rewarded-промис успешно завершается только после появления и нажатия кнопки получения награды; раннее закрытие возвращает CANCELLED. Выдавайте бонус только после ответа с rewarded:true, никогда в catch или finally.

try {
  const ad = await sdk.ads.showRewarded();
  if (ad.rewarded === true) giveBonus();
} catch (error) {
  // CANCELLED / NOT_AVAILABLE / BUSY — бонус не выдаётся
}

payments.getCatalog(), purchase(id), getPurchases() и consume(id) по-прежнему возвращают NOT_AVAILABLE. Собственная рекламная пауза не является коммерческим показом РСЯ и не приносит площадке денег.

Локальная проверка

Распакуйте ZIP и запустите python -m http.server 8000 в его папке, откройте http://localhost:8000/. Это автономный гостевой стенд: он не имитирует облачную покупку, вход или рекламу. Для проверки своей игры замените содержимое iframe-примера, подключив локальный файл SDK. Полную авторизованную проверку проходите в тестовом запуске GameSmile; плагины Unity/Godot/Construct пока не входят в пакет.