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é
| Actor | Puede | No puede |
|---|---|---|
cualquier actor | declarar tipos en el esquema; emitir en su propio nombre, de a uno o en lote; leer los tipos declarados | completar el contexto; leer, consultar o agregar lo que se emitió |
backend-service | lo mismo, y además emitir en nombre de un jugador por delegación | leer la telemetría: no hay permiso de lectura porque no existe una operación de lectura |
En resumen
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))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.[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.
| Declara | Qué es |
|---|---|
name | el nombre propio del tipo |
fields | tipados 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 esquema | obligatoria 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 |
sampling | qué 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érdidas | si este tipo tolera pérdidas |
comportamiento ante eliminación | có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.
| Siempre | Qué es |
|---|---|
sin destinatario | no 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 contexto | lo 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 muestreo | viaja con el evento: sin ella, no se puede reconstruir el número absoluto a partir de lo que llegó |
la pérdida | se 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ón | no 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 eventos | son 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.