Перейти к основному содержимому
Сервисы вокруг игры

Эта страница ещё не переведена — показана английская версия.

Analytics

Всё, что нужно посчитать потом, а не увидеть сейчас. Объявите типизированное событие телеметрии, отправьте его — и оно окажется рядом с событиями самой платформы: пройденный уровень, шаг воронки, экономическое событие, длина сессии, отвал на обучении. Этот модуль только отправляет: сам он ничего не читает, не агрегирует и никуда не доставляет. Куда уйдёт пакет событий, решает маршрутизатор в Extensibility.

Когда это нужно

  • Что-то нужно посчитать потом: шаг воронки, пройденный уровень, экономическое событие, длину сессии.
  • Сравнение должно переживать смену сборок игры: у типа есть версия схемы, поэтому воронка годичной давности не смешивает молча два смысла одного поля.
  • Объём большой, и потерять строку допустимо, если вы так объявили: телеметрия — единственное место в контракте, где объявленные потери законны.
  • Не нужен, если кто-то должен отреагировать. У события (event) телеметрии подписчиков нет вообще, а факт, о котором должны узнать другие, — это игровое событие.

Кто что делает

АкторМожетНе может
любой акторобъявлять типы в схеме; отправлять события от своего имени по одному или пакетом (batch); читать объявленные типызаполнять контекст; читать, запрашивать или агрегировать отправленное
backend-serviceто же самое, а также отправлять события от имени игрока по делегированиючитать телеметрию: разрешения на чтение нет, потому что нет и операции чтения

Коротко

Объявление и отправка BossDefeated: именованные типизированные поля вместо JSON-блоба
[Event("boss_defeated")]
public class BossDefeated
{
    public string BossId = "";
    public int PartySize;
    public float FightSeconds;
}

PlayServ.Analytics.Emit(new BossDefeated { BossId = "hydra", PartySize = 4, FightSeconds = 212f });
@Event('boss_defeated')
export class BossDefeated {
  bossId = '';
  partySize = 0;
  fightSeconds = 0;
}

PlayServ.analytics.emit(new BossDefeated({ bossId: 'hydra', partySize: 4, fightSeconds: 212 }));
@event("boss_defeated")
class BossDefeated:
    boss_id: str = ""
    party_size: int = 0
    fight_seconds: float = 0.0

playserv.analytics.emit(BossDefeated(boss_id="hydra", party_size=4, fight_seconds=212.0))
Скоро — Go

Как объявлять через атрибуты в Go, пока не решено (O-1). Примеры появятся, когда выберут способ записи.

USTRUCT(PSEvent = (Name = "boss_defeated"))
struct FBossDefeated
{
    GENERATED_BODY()

    UPROPERTY() FString BossId;
    UPROPERTY() int32   PartySize;
    UPROPERTY() float   FightSeconds;
};
// declarations compile into the same pushed model — playserv push from the UE project or CI

// the declared type becomes a generated member under the Emit node
Client->Analytics->Emit->BossDefeated({ TEXT("hydra"), /*PartySize*/ 4, /*FightSeconds*/ 212.f });
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — то же объявление и вызов Emit компилируются в Unity на базовой версии 2021.3
[Event("boss_defeated")]
public class BossDefeated
{
    public string BossId = "";
    public int PartySize;
    public float FightSeconds;
}

PlayServ.Analytics.Emit(new BossDefeated { BossId = "hydra", PartySize = 4, FightSeconds = 212f });

Определение события — часть схемы, поэтому приходят именованные типизированные поля, а не JSON-блоб. Объявляется оно как отдельный вид, а не как игровое событие с флагом.

Модель

Что объявляет тип события телеметрии.

ОбъявлениеЧто это
nameсобственное имя типа
fieldsтипизированы системой типов платформы; маска полей применяется к ним так же, как везде
версия схемыобязательна и не выводится из версии SDK. Воронка сравнивает события, собранные в разных сборках игры, и без версии сравнение молча смешивает несравнимое
samplingкакая доля событий этого типа проходит дальше. Сэмплирование объявляется на типе, а реализация никогда не выбирает его сама в зависимости от нагрузки: если доля менялась бы сама по себе, воронки за разные дни стали бы несравнимы
допустимость потерьдопустимы ли для этого типа потери
поведение при удалениикак удаление игрока отражается на этом типе — удалением или анонимизацией. Политику объявляет студия, а исполняет модуль

Что верно для любого события телеметрии.

ВсегдаЧто это
адресата нетни получателя, ни группы, ни подписки. Если захотелось кому-то его адресовать, значит, нужно игровое событие
контекстзаполняет платформа: добавляет актора или отметку анонимности, сессию, окружение, версию сборки и момент по объявленным часам. Вызывающий заполнить его не может: если он подставит актора или версию сборки, вместо переданных значений будут записаны выведенные
доля сэмплированияпередаётся вместе с событием: без неё абсолютное число не восстановить по тому, что дошло
потеривидны в агрегате: доля недоставленного за период доступна потребителю. Видимость каждой отдельной потери не обещается: при объёмах телеметрии сообщение о каждой потере само стало бы потоком данных
отправкане идемпотентна: два вызова — два факта, и ключ идемпотентности она не принимает, ведь подавить второй вызов значило бы потерять данные. Узнать результат потерянной отправки нельзя, и это не нужно: допустимость потерь объявляется заранее, сразу для всех вызовов
событияи есть история: собственной истории модуль не хранит

Ошибки

  • Необъявленный тип и поле, которое не совпадает со схемой типа, — отказы валидации. Ни то ни другое не становится сюрпризом во время работы: тип попадает в админ-консоль из своего объявления.
  • Слишком большое событие — отказ валидации; молча поля никогда не обрезаются.
  • Превышена частота — ответ из категории ограничения частоты, в котором сказано, до какого момента повторять бессмысленно.
  • Отброс при сэмплировании — не ошибка, как и допустимая потеря. Вызов выполнен, а отброс — объявленное поведение. Если сообщать о них как о сбое, объявленное поведение станет неотличимо от неисправности.
  • Пакет обрабатывается целиком или поэлементно, и как именно — объявлено, а не «как получилось».

Ограничения

Конкретные числа появятся вместе с главой об ограничениях платформы, а поведение на каждом пределе зафиксировано уже сейчас.

  • Частота событий на актора: отказ по ограничению частоты с указанием срока. Превышение частоты никогда не приводит к молчаливому отбросу: это либо такой отказ, либо объявленный отброс при сэмплировании.
  • Размер одного события: отказ валидации, поля молча не обрезаются.
  • Размер пакета: пакет отклоняется до отправки, а не применяется частично.
  • Объявленные типы на проект: новое объявление отклоняется при деплое, а не во время работы.
  • Поля в типе: так же, при деплое.
  • Срок хранения: после объявленного срока событие больше недоступно.