Pular para o conteúdo principal
Serviços em torno do jogo

Esta página ainda não foi traduzida: mostrando a versão em inglês.

Analytics

Tudo o que precisa ser contado depois, e não visto agora. Declare um evento de telemetria tipado, emita, e ele chega junto com os eventos da própria plataforma: uma fase concluída, uma etapa de funil, um evento econômico, a duração da sessão, um abandono no tutorial. Este módulo emite; ele mesmo não lê, não agrega e não envia nada para lugar nenhum. Para onde um lote vai é decisão do roteador, em Extensibility.

Quando usar

  • Algo precisa ser contado depois: uma etapa de funil (funnel), uma fase concluída, um evento econômico, a duração da sessão.
  • A comparação precisa sobreviver a novos builds do jogo: o tipo carrega uma versão de esquema, então um funil de um ano atrás não mistura em silêncio dois significados do mesmo campo.
  • O volume é alto, e perder uma linha é aceitável se você declarou isso: a telemetria é o único lugar do contrato em que a perda declarada é legítima.
  • Dispensável quando alguém precisa reagir. Um evento de telemetria não tem assinante nenhum; um fato que outros precisam ouvir é um evento de jogo.

Quem faz o quê

AtorPodeNão pode
qualquer atordeclarar tipos no esquema; emitir em nome próprio, um por vez ou em lote; ler os tipos declaradospreencher o contexto; ler, consultar ou agregar o que foi emitido
backend-serviceo mesmo, e também emitir em nome de um jogador por delegaçãoler a telemetria: não existe permissão de leitura, porque não existe operação de leitura

Resumo

Declarar e emitir BossDefeated: campos nomeados e tipados em vez de um blob 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))
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

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 — a mesma declaração e a mesma chamada Emit compilam no Unity, na base 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 });

A definição faz parte do esquema, então o que chega traz campos nomeados e tipados, e não um blob JSON. Ela é declarada como um tipo próprio, e não como um evento de jogo com uma flag.

O modelo

O que um tipo de evento de telemetria declara.

DeclaraO que é
nameo nome do próprio tipo
fieldstipados pelo sistema de tipos da plataforma; uma máscara de campos se aplica a eles como em qualquer outro lugar
versão do esquemaobrigatória, e não derivada da versão do SDK: um funil compara eventos coletados em builds diferentes do jogo, e sem versão a comparação mistura em silêncio o que não é comparável
samplingque fração dos eventos deste tipo passa. É declarada no tipo, nunca escolhida pela implementação conforme a carga: uma fração que mudasse sozinha tornaria os funis incomparáveis de um dia para o outro
tolerância a perdasse este tipo tolera perdas
comportamento na exclusãocomo a exclusão de um jogador chega a este tipo: apagando ou anonimizando. O estúdio declara a política; o módulo a executa

O que vale para todo evento de telemetria.

SempreO que é
nenhum destino de endereçamentonenhum destinatário, nenhum grupo, nenhuma assinatura. Querer endereçar alguém é sinal de que o que você precisa é de um evento de jogo
o contextoé da plataforma: ela adiciona o ator ou uma marca de anonimato, a sessão, o ambiente, a versão do build e o momento pelo relógio declarado. Quem chama não pode preencher o contexto: se tentar trocar o ator ou a versão do build, recebe os valores derivados no lugar dos que passou
a fração de amostragemvai junto com o evento: sem ela, não dá para reconstruir o número absoluto a partir do que chegou
a perdapode ser observada no agregado: a fração não entregue num período fica disponível para quem consome os dados; não há promessa de observar cada item, porque, no volume da telemetria, uma mensagem por perda viraria ela mesma um fluxo
a emissãonão é idempotente: duas chamadas são dois fatos, e ela não aceita chave de idempotência, porque suprimir a segunda perderia dados. Não existe como saber o resultado de uma emissão perdida, e nem se quer isso: a tolerância a perdas é declarada de antemão, para todas as chamadas de uma vez
os eventossão o histórico: o módulo não guarda nenhum histórico próprio

Erros

  • Um tipo não declarado e um campo que não bate com o esquema do tipo são recusas de validação; nenhum dos dois é surpresa em runtime, porque um tipo chega ao console de administração a partir da sua declaração.
  • Um evento grande demais é uma recusa de validação; os campos nunca são cortados em silêncio.
  • A frequência excedida responde na categoria de limite de frequência, com o momento antes do qual não adianta tentar de novo.
  • O descarte por amostragem não é um erro, e uma perda admissível também não. A chamada foi executada, e o descarte é um comportamento declarado; reportar qualquer um dos dois como falha tornaria o comportamento declarado indistinguível de um defeito.
  • Um lote é tratado inteiro ou por elemento, e qual dos dois é declarado, nunca “o que acontecer”.

Limites

Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.

  • Frequência de eventos por ator: recusa por limite de frequência, com prazo. Exceder a frequência nunca descarta em silêncio: ou é essa recusa, ou é um descarte por amostragem declarado.
  • Tamanho de um evento: recusa de validação, nunca campos cortados em silêncio.
  • Tamanho do lote: rejeitado antes do envio, em vez de aplicado em parte.
  • Tipos declarados por projeto: uma nova declaração é rejeitada no deploy, não em runtime.
  • Campos num tipo: o mesmo, no deploy.
  • Período de retenção: passado o período declarado, o evento deixa de estar disponível.