Документация


Defold SDK
Официальная библиотека SpyMagic для Defold. Чистый Lua без нативных расширений: подключается одной строкой в зависимостях game.project, настраивается там же в секции [spymagic]. Очередь событий с сохранением между запусками, батчи, повторы при обрывах связи и функции под все встроенные события аналитики.
Требования
параметр значение
Defold1.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. Виден администратору компании в Настройки → Игры, кнопка «показать»
urlhttps://spymagic.pro/track/адрес трекера
platform0platform_id из таблицы платформ. 0 — определить автоматически: Android → 2 (Google Play), iOS → 5 (Apple Store), остальное → 1 (www). Для HTML5 на Яндекс.Играх / VK / CrazyGames укажите id явно или вызовите spymagic.set_platform()
timeout5таймаут одного HTTP-запроса, сек
batch_interval30раз в сколько секунд отправлять очередь. 0 — каждое событие отдельным запросом
max_batch_size30отправить сразу, когда накопилось столько событий. Пока sid ≤ 100 — порог 10
max_queue_size200предел офлайн-очереди, старые события выбрасываются
auto_send_init1слать init при spymagic.init(). На HTML5 пропускается, если init уже отправила страница
track_focus1события focus_lost / focus_gain через on_window_event
use_device_identifier1на Android / iOS uuid считается как uuid v5 от device_ident, переустановка не плодит нового игрока
persist_queue1хранить неотправленное между запусками (sys.save)
send_package_meta1добавлять к батчу служебное событие package_send
debug0печатать диагностику в консоль
Любой ключ можно переопределить при инициализации: 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_finishnarrative_*катсцены
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 в нужном формате.