Эта страница ещё не переведена — показана английская версия.
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, пока не решено (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.[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 | какая доля событий этого типа проходит дальше. Сэмплирование объявляется на типе, а реализация никогда не выбирает его сама в зависимости от нагрузки: если доля менялась бы сама по себе, воронки за разные дни стали бы несравнимы |
допустимость потерь | допустимы ли для этого типа потери |
поведение при удалении | как удаление игрока отражается на этом типе — удалением или анонимизацией. Политику объявляет студия, а исполняет модуль |
Что верно для любого события телеметрии.
| Всегда | Что это |
|---|---|
адресата нет | ни получателя, ни группы, ни подписки. Если захотелось кому-то его адресовать, значит, нужно игровое событие |
контекст | заполняет платформа: добавляет актора или отметку анонимности, сессию, окружение, версию сборки и момент по объявленным часам. Вызывающий заполнить его не может: если он подставит актора или версию сборки, вместо переданных значений будут записаны выведенные |
доля сэмплирования | передаётся вместе с событием: без неё абсолютное число не восстановить по тому, что дошло |
потери | видны в агрегате: доля недоставленного за период доступна потребителю. Видимость каждой отдельной потери не обещается: при объёмах телеметрии сообщение о каждой потере само стало бы потоком данных |
отправка | не идемпотентна: два вызова — два факта, и ключ идемпотентности она не принимает, ведь подавить второй вызов значило бы потерять данные. Узнать результат потерянной отправки нельзя, и это не нужно: допустимость потерь объявляется заранее, сразу для всех вызовов |
события | и есть история: собственной истории модуль не хранит |
Ошибки
- Необъявленный тип и поле, которое не совпадает со схемой типа, — отказы валидации. Ни то ни другое не становится сюрпризом во время работы: тип попадает в админ-консоль из своего объявления.
- Слишком большое событие — отказ валидации; молча поля никогда не обрезаются.
- Превышена частота — ответ из категории ограничения частоты, в котором сказано, до какого момента повторять бессмысленно.
- Отброс при сэмплировании — не ошибка, как и допустимая потеря. Вызов выполнен, а отброс — объявленное поведение. Если сообщать о них как о сбое, объявленное поведение станет неотличимо от неисправности.
- Пакет обрабатывается целиком или поэлементно, и как именно — объявлено, а не «как получилось».
Ограничения
Конкретные числа появятся вместе с главой об ограничениях платформы, а поведение на каждом пределе зафиксировано уже сейчас.
- Частота событий на актора: отказ по ограничению частоты с указанием срока. Превышение частоты никогда не приводит к молчаливому отбросу: это либо такой отказ, либо объявленный отброс при сэмплировании.
- Размер одного события: отказ валидации, поля молча не обрезаются.
- Размер пакета: пакет отклоняется до отправки, а не применяется частично.
- Объявленные типы на проект: новое объявление отклоняется при деплое, а не во время работы.
- Поля в типе: так же, при деплое.
- Срок хранения: после объявленного срока событие больше недоступно.