Defold SDK
Официальная библиотека SpyMagic для Defold. Чистый Lua без нативных расширений: подключается одной строкой в зависимостях
game.project, настраивается там же в секции [spymagic].
Очередь событий с сохранением между запусками, батчи, повторы при обрывах связи и функции под все встроенные события аналитики.
Требования
| параметр |
значение |
| Defold | 1.4 и новее (нужны http.request с таймаутом, json, timer, sys.save; для uuid v5 — crypt.hash_sha1, есть с 1.2.184) |
| Платформы | HTML5, Android, iOS, Windows / macOS / Linux |
| Зависимости | нет, нативных расширений нет |
| Размер | один Lua-модуль spymagic/spymagic.lua |
Установка
1
Откройте game.project, раздел Project → Dependencies, и добавьте адрес архива:
https://spymagic.pro/static/sdk/defold/spymagic-defold-1.0.0.zip
2
В редакторе выполните Project → Fetch Libraries. В проекте появится папка spymagic.
3
Подключайте модуль: local spymagic = require "spymagic.spymagic".
Тот же архив можно
скачать и положить рядом с проектом — Defold принимает и локальный путь к zip в зависимостях.
Настройка
Все параметры живут в game.project в секции [spymagic]. После Fetch Libraries секция появится в редакторе настроек проекта, либо допишите её в текстовом виде:
[spymagic]
token = 3.ваш_ключ_проекта
platform = 0
batch_interval = 30
| ключ |
по умолчанию |
описание |
| token | — | ключ проекта, уходит в заголовке Mig-Token. Виден администратору компании в Настройки → Игры, кнопка «показать» |
| url | https://spymagic.pro/track/ | адрес трекера |
| platform | 0 | platform_id из таблицы платформ. 0 — определить автоматически: Android → 2 (Google Play), iOS → 5 (Apple Store), остальное → 1 (www). Для HTML5 на Яндекс.Играх / VK / CrazyGames укажите id явно или вызовите spymagic.set_platform() |
| timeout | 5 | таймаут одного HTTP-запроса, сек |
| batch_interval | 30 | раз в сколько секунд отправлять очередь. 0 — каждое событие отдельным запросом |
| max_batch_size | 30 | отправить сразу, когда накопилось столько событий. Пока sid ≤ 100 — порог 10 |
| max_queue_size | 200 | предел офлайн-очереди, старые события выбрасываются |
| auto_send_init | 1 | слать init при spymagic.init(). На HTML5 пропускается, если init уже отправила страница |
| track_focus | 1 | события focus_lost / focus_gain через on_window_event |
| use_device_identifier | 1 | на Android / iOS uuid считается как uuid v5 от device_ident, переустановка не плодит нового игрока |
| persist_queue | 1 | хранить неотправленное между запусками (sys.save) |
| send_package_meta | 1 | добавлять к батчу служебное событие package_send |
| debug | 0 | печатать диагностику в консоль |
Любой ключ можно переопределить при инициализации: spymagic.init({ token = "...", platform = spymagic.PLATFORM.YANDEX_GAMES, debug = true }).
Как это работает
Контекст. spymagic.init() вызывайте из скрипта, который живёт всю игру (bootstrap-коллекция): таймеры батчей и сохранения создаются в его контексте и умрут вместе с ним.
uuid. Определяется один раз и хранится через sys.save. На HTML5 сначала берётся localStorage.uuid (его заводит JS-сниппет), на мобильных — uuid v5 от идентификатора устройства, иначе случайный uuid v4.
sid и время. Каждому событию присваивается порядковый номер внутри сессии и unix-время, сервер восстанавливает порядок даже при плохой связи.
Очередь и батчи. Событие попадает в очередь. Очередь уходит одним POST-запросом каждые batch_interval секунд, при достижении max_batch_size, при принудительной отправке, при потере фокуса и сворачивании окна.
Повторы. Ответ 2xx — доставлено. Статус 0 или 5xx — события возвращаются в голову очереди. 4xx — батч выбрасывается.
Персист. Неотправленное сохраняется раз в 2 секунды; на следующем запуске хвост прошлой сессии уходит первым.
Тело запроса (формат format = 1) такое же, как у остальных SDK:
POST https://spymagic.pro/track/
Content-Type: application/json
Mig-Token: <token>
{
"format": 1,
"uuid": "b2ce6279-cb36-45a1-b26c-ad40d4004e44",
"user_id": 10455,
"platform": 10,
"events": [
{"sid": 0, "event": "init", "detail": {"platform": 10, "version": "1.4.0", "language": "ru", "device": {...}, "screen": {...}}, "time": 1757800000},
{"sid": 1, "event": "hit", "detail": {"url": "lobby"}, "time": 1757800004},
{"sid": 2, "event": "package_send", "detail": {"events_num": 2}, "time": 1757800030}
]
}
Идентификация игрока
| функция |
когда вызывать |
spymagic.register(user_id, user_name) | один раз в жизни игрока — аккаунт только что создан. Считается установкой |
spymagic.auth(user_id, extra) | каждый последующий запуск. SDK сам добавит платформу, версию, язык, устройство и install_id |
spymagic.logout() | выход из аккаунта, очередь отправляется сразу |
spymagic.user_set(key, value) | свойства профиля: name, mail, password, gender, sound, music — см. Игроки |
spymagic.set_user_id(user_id) | если id известен без register/auth. register и auth вызывают его сами |
user_id — целое число, аналитика хранит его как bigint. Строковые id заменяйте числовым суррогатом на сервере.
Функции и события
Модуль возвращает таблицу с функциями ниже, константами
spymagic.PLATFORM и
spymagic.EVENTS. Описание каждого события — в разделе
События.
| функция |
событие |
для чего |
| Техника |
send_init(extra) | init | старт сессии; вызывается автоматически из init() |
loading(step, state) | loading | этап загрузки 1..N, time to interactive |
hit(url, props) | hit | смена сцены |
click(x, y, scene) | click | тепловые карты |
error(text) | error | клиентская ошибка |
http_request(url, method, status, time) | http_request | запросы к вашему серверу, отчёт по пингу |
on_window_event(event) | focus_lost / focus_gain | передавайте сюда события из window.set_listener |
| Прогресс — описание |
levelup(level) | levelup | уровень аккаунта |
tutorial(step), tutorial_complete() | tutorial, tutorial_complete | воронка туториала |
battle_result(result, point, time, location, power, is_auto, is_surrender) | battle_result | итог боя: result 1/0/-1, point — номер уровня |
level_result(...) | level_result | то же для игр с уровнями |
story_point(step) | story_point | точки сюжета |
narrative_start / narrative_step / narrative_skip / narrative_finish | narrative_* | катсцены |
dialogue(id), dialogue_skip(id, step) | dialogue, dialogue_skip | диалоги |
feature(text) | feature | открытие фичи |
| Экономика — платежи, ресурсы |
resource({ gold = 1100, gems = 25 }) | resource | срез ресурсов на руках |
purchase_init(offer_code, price_code, price_quantity) | purchase_init | нажата кнопка покупки |
purchase_method(method, offer_code) | purchase_method | выбор способа оплаты (свой эквайринг) |
purchase(offer_code, price_code, price_quantity, extra) | purchase | платёж подтверждён, очередь отправляется сразу |
purchase_canceled(transaction), purchase_error(error, transaction) | purchase_canceled, purchase_error | отмена и ошибки |
virtual_purchase(offer_id, offer_rewards, price_currency, price_code, price_quantity) | virtual_purchase | покупка за игровую валюту |
| Реклама — описание |
rewarded_init(section_id) | rewarded_init | запрошен rewarded-блок |
rewarded_received(section_id) | rewarded_received | ролик досмотрен |
rewarded_error(section_id, error) | rewarded_error | сеть не отдала рекламу |
| A/B и прочее — эксперименты |
experiment(experiment_id, group_id) | experiment | игрок попал в группу эксперимента |
social(key, value) | social | социальные действия |
| Произвольные события |
track(event, detail, is_forced_send) | любое | кастомное событие, detail — таблица любой вложенности |
track_now(event, detail) | любое | отправить немедленно, минуя очередь |
| Управление |
init(options) | — | инициализация; options переопределяет game.project |
set_platform(id), set_user_id(id), set_batch_interval(sec) | — | смена параметров в рантайме |
set_enabled(bool) | — | жёсткий выключатель (песочница, QA) |
set_track_enabled(bool, source) | — | мягкий выключатель, например по заголовку вашего сервера |
flush() | — | отправить очередь сейчас |
on_log(fn) | — | диагностика: fn(message, data) |
get_uuid(), get_user_id(), get_platform(), get_queue_length() | — | текущее состояние |
Пример
local spymagic = require "spymagic.spymagic"
function init(self)
spymagic.init() -- читает [spymagic] из game.project, шлёт init
window.set_listener(function(_, event)
spymagic.on_window_event(event) -- focus_lost / focus_gain, flush при сворачивании
end)
spymagic.loading(1)
-- сервер вернул аккаунт
if account.is_new then
spymagic.register(account.id, account.name)
else
spymagic.auth(account.id)
end
for experiment_id, group_id in pairs(account.experiments) do
spymagic.experiment(experiment_id, group_id)
end
spymagic.loading(2)
spymagic.hit("lobby")
end
function on_battle_finished(is_victory, level, seconds, power)
spymagic.battle_result(is_victory and 1 or -1, level, seconds, 0, power)
spymagic.hit(is_victory and "win" or "defeat")
end
function on_levelup(level, gold, gems)
spymagic.levelup(level)
spymagic.resource({ gold = gold, gems = gems })
end
function on_payment_confirmed(offer, price)
spymagic.purchase(offer, "RUB", price)
end
function on_boss_unlocked(boss_id)
spymagic.track("boss_unlocked", { boss_id = boss_id })
end
Готовый пример лежит в архиве библиотеки: example/example.script.
HTML5
init со страницы. Для веб-игр init стоит отправить до загрузки движка — так считается честная скорость загрузки. В архиве лежит html/spymagic-init.js: вставьте его в свой HTML-шаблон (game.project → html5 → htmlfile) перед dmloader.js. Скрипт заводит uuid в localStorage, шлёт init и выставляет флаг window.spymagic.init_sent; Lua-модуль подхватывает тот же uuid и второй init не отправляет. Плейсхолдер {{project.version}} Defold подставит сам.
Кросс-игровая кука. Запрос со страницы идёт с credentials: "include", и аналитика может поставить общую куку mig_uid. Из http.request движка такое невозможно.
Платформа. Один HTML5-билд может стоять на нескольких порталах. Определите портал и вызовите spymagic.set_platform(spymagic.PLATFORM.YANDEX_GAMES) сразу после init(), либо задайте platform в game.project для каждой сборки.
Диагностика
Включите debug = 1 в game.project — в консоль пойдут отправленные батчи, ошибки HTTP и выброшенные события.
Через spymagic.on_log(fn) заведите сбои доставки в свой логгер. Отчётов не больше 20 за сессию, переполнение очереди репортится с прореживанием 1, 10, 100.
Доехавшие события смотрите в разделе
События проекта — батч уходит не позже чем через batch_interval секунд, либо вызовите
spymagic.flush().
FAQ
| Таймеры не тикают, батчи не уходят |
spymagic.init() вызван из скрипта, который выгрузился вместе с коллекцией. Переносите вызов в bootstrap-коллекцию. |
| Что можно положить в detail? |
Любую Lua-таблицу, которую примет json.encode: строки, числа, булевы, вложенные таблицы и массивы. |
| Как не слать данные из тестовых сборок? |
spymagic.set_enabled(false) сразу после init(). Лучше — отдельный проект в аналитике с другим токеном. |
| Сервер ответил 401 |
Неверный токен или проект удалён. Проверьте token — ключ целиком, с префиксом до точки. |
| Своя система событий, не хочу вызывать десятки функций |
Достаточно track(event, detail) с именами из spymagic.EVENTS — типизированные функции только формируют detail в нужном формате. |