Общий интерфейс сохранений, игрока, входа, достижений, лидербордов, рекламы и жизненного цикла игры. Работает в iframe GameSmile, включая песочницу без доступа к cookies и localStorage. Существующий контракт сохранений 1.0 совместим. Коммерческая реклама и платежи пока не подключены; рекламные вызовы временно обслуживает собственное продвижение игр GameSmile.
Скачать SDK и автономный гостевой пример ZIP · Типы TypeScript
Открыть лабораторию: сбои, конфликты и восстановление. Тестовое облако и аккаунты находятся только в памяти страницы, реальный API не вызывается. Можно подключить свою HTML5-сборку и скачать журнал без содержимого сохранений. Автопроверка проверяет обработчик SDK, а не сертифицирует игру.
<script src="https://gamesmile.ru/static/gamesmile-sdk-v1.js"></script>
Можно скачать этот файл и положить в свой билд. Не используйте SDK GameSmile в сборке для Яндекса/VK: подключайте его в адаптере нашей площадки. Секреты и ключи API не нужны. Идентификатор игры назначает страница запуска, а не игровой код.
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 нельзя переиспользовать для нового пользователя.
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 для зависимых действий и сохраняйте в контрольных точках, не каждый кадр. Ошибка отдельной операции не отменяет остальные: результат каждого вызова нужно обработать.
load(): {status:'empty'|'cloud'|'local', data, canSave, guestAvailable}. Ошибка не превращается в пустое сохранение.save(): {status:'cloud'|'local', synced}. Только synced:true подтверждает запись на сервере.SAVE_CONFLICT: прогресс изменён другим устройством/вкладкой. Заново загрузите его и покажите игроку выбор; не повторяйте старую запись автоматически.ACCOUNT_CHANGED: другой аккаунт или выход. Остановите запись и предложите перезагрузку.LOAD_REQUIRED: сначала необходима успешная загрузка.NETWORK, UNAVAILABLE: нет подтверждённого результата; учитывайте доступность локальной копии.TIMEOUT: ответ площадки потерян; запрос мог дойти. После таймаута операции экземпляр SDK блокирует последующие вызовы с RELOAD_REQUIRED и сообщает pause с причиной transport. Перезагрузите игру и перечитайте состояние, не повторяйте запись вслепую. Только неудачный init можно повторить без перезагрузки.LOCAL_UNAVAILABLE, TOO_LARGE, INVALID_DATA, BUSY: соответственно недоступно хранилище, превышен размер, неверные данные или заполнена очередь / занят сервис площадки.NOT_EMBEDDED: открыта игра без страницы запуска площадки. Используйте тестовый запуск, а не file://.При сетевой ошибке записи 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.
На рабочей странице игры features.analytics сообщает о подключении анонимной диагностики SDK. Вызывайте ready после загрузки ресурсов, start при начале партии и stop при выходе в меню или завершении. Повторный start без stop не создаёт ещё одну партию. В меню, собственном окне восстановления и на паузе движка вызывайте stop, затем start при продолжении: SDK не видит внутреннее состояние вашей игры.
Учитываются сессии SDK, готовность, старты/остановки, активное время и количество сбоев запросов SDK. Скрытая вкладка и пауза площадки исключаются; большой разрыв таймера не засчитывается как игра. Отчёт отправляется примерно раз в 15 секунд и при переходах. Это оценка: при закрытии вкладки/потере сети возможна потеря последних секунд. Ошибки аналитики не блокируют игру. Повторные отчёты не суммируются дважды. Нет ID игрока, текста исключений, URL или содержимого сейва; случайный ID действует на один экземпляр игры, записи хранятся до 90 дней с очисткой при новых сессиях.
Графики доступны владельцу площадки и одобренной студии только по её играм. Дни группируются по началу сессии в UTC; сессии не равны уникальным людям, stop не означает победу. Данные клиента не являются античитом. Игры со старым bridge не включаются автоматически; ошибка загрузки до инициализации SDK в этот отчёт не попадает. Лаборатория и гостевой пример не отправляют настоящую аналитику.
После 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 записей за сессию. Ошибка аналитики не должна останавливать игру: при необходимости перехватывайте отказ. Эти данные помогают найти место ухода игроков, но не являются античитом или подтверждением награды.
Студия назначает расписание в карточке своей игры. Игра получает только активные ивенты через 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 участвуют в диагностике игровой активности; это не серверная проверка честности результата.
player.get() — проверенный снимок {signed,name}; смена аккаунта возвращает ACCOUNT_CHANGED.auth.open() — окно входа через доступных провайдеров. Только после действия игрока и сохранения прогресса. Возвращает {status:'opened'|'signed'}; opened не означает успешный вход. После OAuth страница игры загружается заново.achievements.list() — каталог и локальный прогресс этой игры. unlock(code), progress(code,total), sync() — получение, абсолютный накопленный прогресс и повтор синхронизации. Значение total — целое неотрицательное, не приращение. Ответ {status,synced,progress}; только synced:true подтверждает профиль на сервере. Неизвестный код — UNKNOWN_ACHIEVEMENT. Каталог должен быть заведён на площадке до интеграции.leaderboards.get(), leaderboards.submit(score) — {top,mine}. Сейчас одна таблица на игру, большее число лучше; отправка доступна после входа, число целое неотрицательное. Результат хранит максимум. Клиентские рекорды/достижения не защищены от подделки и не подходят для денежных призов без серверной проверки игрового результата.fullscreen.request(), fullscreen.exit() — запрос браузеру. Вызывайте из нажатия кнопки; браузер вправе вернуть USER_GESTURE_REQUIRED или NOT_AVAILABLE.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 пока не входят в пакет.