Saltar al contenido principal
Servicios alrededor del juego

Esta página aún no está traducida: se muestra la versión en inglés.

Analytics

Todo lo que hay que contar después, no ver en el momento. Declara un evento de telemetría tipado, emítelo, y llega junto a los de la propia plataforma: un nivel completado, un paso de un embudo, un evento económico, la duración de una sesión, un abandono en el tutorial. Este módulo emite; no lee, no agrega ni envía nada por su cuenta. A dónde viaja un lote lo decide el router, en Extensibility.

Cuándo usarlo

  • Hay algo que se debe contar después: un paso de un embudo, un nivel completado, un evento económico, la duración de una sesión.
  • La comparación tiene que sobrevivir a los distintos builds del juego: cada tipo lleva una versión de esquema, así que un embudo de hace un año no mezcla en silencio dos significados de un mismo campo.
  • El volumen es alto y perder una fila es aceptable si así lo declaraste: la telemetría es el único lugar del contrato donde una pérdida declarada es legítima.
  • No lo necesitas si alguien tiene que reaccionar. Un evento de telemetría no tiene ningún suscriptor; un hecho del que otros deben enterarse es un evento de juego.

Quién hace qué

ActorPuedeNo puede
cualquier actordeclarar tipos en el esquema; emitir en su propio nombre, de a uno o en lote; leer los tipos declaradoscompletar el contexto; leer, consultar o agregar lo que se emitió
backend-servicelo mismo, y además emitir en nombre de un jugador por delegaciónleer la telemetría: no hay permiso de lectura porque no existe una operación de lectura

En resumen

Declarar y emitir BossDefeated: campos con nombre y tipo en lugar de un 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))
Próximamente — Go

Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.

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 — la misma declaración y la misma llamada a Emit compilan en Unity sobre la versión 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 });

La definición es esquema, así que lo que llega trae campos con nombre y tipo, no un blob JSON. Se declara como un tipo propio, no como un evento de juego con una marca.

El modelo

Qué declara un tipo de evento de telemetría.

DeclaraQué es
nameel nombre propio del tipo
fieldstipados con el sistema de tipos de la plataforma; se les aplica una máscara de campos (field mask), como en todas partes
versión de esquemaobligatoria y no se deriva de la versión del SDK: un embudo compara eventos recopilados con distintos builds del juego, y sin versión la comparación mezcla en silencio cosas que no se pueden comparar
samplingqué proporción de los eventos de este tipo pasa. Se declara en el tipo; la implementación nunca la elige según la carga, porque una proporción que cambiara sola haría que los embudos de distintos días no se pudieran comparar
tolerancia a pérdidassi este tipo tolera pérdidas
comportamiento ante eliminacióncómo afecta a este tipo la eliminación de un jugador: se borra o se anonimiza. El estudio declara la política y el módulo la aplica

Qué se cumple para todo evento de telemetría.

SiempreQué es
sin destinatariono hay destinatario, ni grupo, ni suscripción. Si quieres dirigirlo a alguien, es señal de que lo que necesitas es un evento de juego
el contextolo pone la plataforma: agrega el actor o una marca de anonimato, la sesión, el entorno, la versión del build y el momento según el reloj declarado. Quien llama no puede completarlo: si intenta sustituir el actor o la versión del build, se usan los valores derivados en lugar de los que pasó
la proporción de muestreoviaja con el evento: sin ella, no se puede reconstruir el número absoluto a partir de lo que llegó
la pérdidase puede observar en conjunto: el consumidor tiene disponible la proporción no entregada en un período. No se promete observarla elemento por elemento, porque con los volúmenes de la telemetría un mensaje por cada pérdida se convertiría a su vez en un flujo
la emisiónno es idempotente: dos llamadas son dos hechos, y no acepta clave de idempotencia, porque suprimir la segunda haría perder datos. No hay forma de saber el resultado de una emisión perdida, ni se busca tenerla: la tolerancia a pérdidas se declara de antemano, para todas las llamadas a la vez
los eventosson el historial: el módulo no guarda uno propio

Errores

  • Un tipo no declarado y un campo que no coincide con el esquema del tipo son rechazos de validación; ninguno es una sorpresa en runtime, porque un tipo llega a la consola de administración desde su declaración.
  • Un evento demasiado grande es un rechazo de validación; los campos nunca se recortan en silencio.
  • Superar la frecuencia responde en la categoría de límite de frecuencia e indica hasta cuándo no tiene sentido reintentar.
  • Un descarte por muestreo no es un error, y una pérdida admisible tampoco. La llamada se ejecutó y el descarte es un comportamiento declarado; informar cualquiera de los dos como una falla haría imposible distinguir el comportamiento declarado de un error real.
  • Un lote se procesa completo o elemento por elemento, y cuál de los dos se declara: nunca “lo que haya pasado”.

Límites

Las cifras llegarán con el capítulo sobre los límites de la plataforma; lo que pasa al alcanzar cada tope ya está definido.

  • Frecuencia de eventos por actor: un rechazo por límite de frecuencia con un plazo. Superar la frecuencia nunca descarta eventos en silencio: o hay ese rechazo o hay un descarte por muestreo declarado.
  • Tamaño de un evento: un rechazo de validación, nunca campos recortados en silencio.
  • Tamaño del lote: se rechaza antes de enviarlo, no se aplica a medias.
  • Tipos declarados por proyecto: una declaración nueva se rechaza al desplegar, no en runtime.
  • Campos en un tipo: lo mismo, al desplegar.
  • Período de retención: pasado el período declarado, el evento deja de estar disponible.