Cette page n’est pas encore traduite : la version anglaise est affichée.
Analytics
Tout ce qui doit être compté plus tard plutôt que vu tout de suite. Déclarez un événement de télémétrie typé, émettez-le, et il rejoint ceux de la plateforme : un niveau terminé, une étape d’entonnoir, un événement économique, la durée d’une session, un abandon pendant le tutoriel. Ce module émet ; il ne lit, n’agrège et n’expédie rien lui-même. La destination d’un lot relève du routeur, dans Extensibility.
Quand l’utiliser
- Quelque chose doit être compté plus tard : une étape d’entonnoir (funnel), un niveau terminé, un événement économique, la durée d’une session.
- La comparaison doit tenir d’un build à l’autre : un type porte une version de schéma, si bien qu’un entonnoir vieux d’un an ne mélange pas en silence deux sens d’un même champ.
- Le volume est élevé, et une ligne perdue est acceptable si vous l’avez dit : la télémétrie est le seul endroit du contrat où une perte déclarée est légitime.
- Inutile si quelqu’un doit réagir. Un événement de télémétrie n’a aucun abonné ; un fait que d’autres doivent entendre est un événement de jeu.
Qui fait quoi
| Acteur | Peut | Ne peut pas |
|---|---|---|
tout acteur | déclarer des types dans le schéma ; émettre en son propre nom, un par un ou par lot ; lire les types déclarés | remplir le contexte ; lire, interroger ou agréger ce qui a été émis |
backend-service | la même chose, plus émettre au nom d’un joueur par délégation | lire la télémétrie : il n’existe pas de permission de lecture, parce qu’il n’existe pas d’opération de lecture |
En bref
BossDefeated : des champs nommés et typés au lieu d’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))La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.
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 définition fait partie du schéma : ce qui arrive porte donc des champs nommés et typés, pas un blob JSON. Elle est déclarée comme un type à part entière, et non comme un événement de jeu muni d’un indicateur.
Le modèle
Ce que déclare un type d’événement de télémétrie.
| Déclare | Ce que c’est |
|---|---|
name | le nom propre du type |
fields | typés par le système de types de la plateforme ; un masque de champs s’y applique comme partout ailleurs |
version de schéma | obligatoire, et non déduite de la version du SDK : un entonnoir compare des événements collectés sous différents builds du jeu, et sans version, la comparaison mélange en silence ce qui n’est pas comparable |
sampling | la part des événements de ce type qui passe. Elle est déclarée sur le type, jamais choisie par l’implémentation en fonction de la charge : une part qui changerait d’elle-même rendrait les entonnoirs incomparables d’un jour à l’autre |
tolérance à la perte | si ce type tolère la perte |
comportement à la suppression | comment la suppression d’un joueur se répercute sur ce type : par suppression ou par anonymisation. Le studio déclare la politique ; le module l’applique |
Ce qui vaut pour tout événement de télémétrie.
| Toujours | Ce que c’est |
|---|---|
aucun destinataire | ni destinataire, ni groupe, ni abonnement. Si vous voulez en viser un, c’est le signe qu’il vous faut un événement de jeu |
le contexte | appartient à la plateforme : elle ajoute l’acteur ou un marqueur d’anonymat, la session, l’environnement, la version du build et l’instant selon l’horloge déclarée. L’appelant ne peut pas le remplir : s’il substitue l’acteur ou la version du build, ce sont les valeurs dérivées qui l’emportent sur celles qu’il a passées |
la part d'échantillonnage | voyage avec l’événement : sans elle, impossible de reconstituer le nombre absolu à partir de ce qui est arrivé |
la perte | est observable globalement : la part non livrée sur une période est accessible au consommateur ; l’observabilité élément par élément n’est pas promise, car aux volumes de la télémétrie, un message par perte deviendrait lui-même un flux |
l'émission | n’est pas idempotente : deux appels, ce sont deux faits, et elle n’accepte aucune clé d’idempotence, car supprimer le second ferait perdre des données. Il n’existe aucun moyen de connaître le résultat d’une émission perdue, et on n’en veut pas : la tolérance à la perte est déclarée à l’avance, pour tous les appels à la fois |
les événements | sont l’historique : le module n’en garde aucun de son côté |
Erreurs
- Un type non déclaré, ou un champ qui ne correspond pas au schéma du type, donne un refus de validation ; ni l’un ni l’autre n’est une surprise à l’exécution, puisqu’un type arrive dans la console d’administration à partir de sa déclaration.
- Un événement trop volumineux est un refus de validation ; les champs ne sont jamais tronqués en silence.
- La fréquence dépassée répond dans la catégorie rate limit, avec l’instant avant lequel réessayer ne sert à rien.
- Un rejet par échantillonnage n’est pas une erreur, pas plus qu’une perte admise. L’appel a été exécuté et le rejet est un comportement déclaré ; signaler l’un ou l’autre comme un échec rendrait le comportement déclaré impossible à distinguer d’une panne.
- Un lot est traité en entier ou élément par élément, et le choix est déclaré : jamais « selon ce qui s’est passé ».
Limites
Les chiffres arriveront avec le chapitre sur les limites de la plateforme ; ce qui se passe à chaque plafond est déjà fixé.
- La fréquence d’événements par acteur : un refus pour limite de débit, avec un délai. Un dépassement n’entraîne jamais de rejet silencieux : c’est soit ce refus, soit un rejet par échantillonnage déclaré.
- La taille d’un événement : un refus de validation, jamais des champs tronqués en silence.
- La taille d’un lot : rejeté avant l’envoi plutôt qu’appliqué en partie.
- Les types déclarés par projet : une nouvelle déclaration est rejetée au déploiement, pas à l’exécution.
- Les champs d’un type : de même, au déploiement.
- La durée de rétention : au-delà de la durée déclarée, l’événement n’est plus disponible.