Aller au contenu principal
Services autour du jeu

Cette page n’est pas encore traduite : la version anglaise est affichée.

Auth & Players

La connexion est une étape que vous pouvez surcharger, pas une boîte noire. Fournisseurs, sessions, liaison d’identités, bannissements. Chaque point du parcours — avant et après la connexion, avant et après une liaison, avant et après une fusion, à chaque changement de statut — est un point d’extension déclaré, d’un type déclaré : une barrière, qui peut refuser l’étape, ou un observateur, qui ne le peut pas.

Quand l’utiliser

  • Les joueurs doivent se connecter (appareil, e-mail, Apple, Google, Steam ou méthode personnalisée), et la création du compte à la première connexion (sign-in) est une simple option, pas un second parcours.
  • Un compte invité doit pouvoir évoluer plus tard : Link ajoute Steam sans perdre la progression, et une fusion (merge) réunit deux comptes en un seul joueur.
  • Une règle doit s’exécuter là où personne ne peut la contourner : une barrière (gate) par région avant la connexion, un pack de départ après la connexion qui a créé le joueur.
  • La modération doit agir immédiatement : révoquer les sessions, suspendre, bannir un appareil, avec un événement banned que reçoivent tous les systèmes actifs.
  • Le contexte déclaré (région, plateforme, build) doit parvenir à chaque hook suivant, sans que chacun doive relire le joueur pour l’obtenir.
  • Il n’existe rien de plus léger pour le remplacer. Tous les autres modules identifient leur appelant grâce à celui-ci, et auth ne peut pas être désactivé tant que l’un d’eux a besoin d’un acteur joueur : le configurateur de modules refuse et nomme les modules qui en dépendent.

Qui fait quoi

ActeurCe qu’il fait sur cette page
playerse connecte, lie ou délie des identités, renouvelle ses identifiants (credential), se déconnecte
moderatorrévoque des sessions ; bannit, suspend ou réintègre des joueurs
backend-servicefiltre la connexion par région ; crée les premières lignes d’un nouveau joueur ; consulte et révoque des sessions

En bref

Un appel SignIn par fournisseur, la création à la première connexion en option ; Link ajoute Steam
// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam);          // one player, many identities
// client — one call per provider; create-on-first-sign-in is a flag
const session = await PlayServ.auth.signIn(Provider.Device, { create: true });
await PlayServ.auth.link(Provider.Steam);    // one player, many identities
# client — one call per provider; create-on-first-sign-in is a flag
session = await playserv.auth.sign_in(Provider.DEVICE, create=True)
await playserv.auth.link(Provider.STEAM)     # one player, many identities
Bientôt — Go

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.

// client — one call per provider; create-on-first-sign-in is a flag
Client->Auth->SignInWithProvider(FPSProviderId::Device, Credential,
    TPSOnResult<FPSSession>::CreateWeakLambda(this, [this](const TPSResult<FPSSession>& Result)
    {
        if (!Result.HasValue()) { return; }
        // one player, many identities — add Steam to the same account
        Client->Auth->Providers->Link(FPSProviderId::Steam, SteamCredential);
    }));
// 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 — s’exécute tel quel dans Unity avec les types générés
// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam);          // one player, many identities

Chaque point se personnalise là où il est déclaré ; les formes que peut prendre un handler sont réunies dans Extensibility :

Une barrière avant la connexion refuse une région ; un observateur, après la connexion qui a créé le joueur, accorde un pack de départ
[Before(Auth.SignIn)]                          // a gate: it may refuse, and it is fail-closed
public static Verdict GateRegion(SignInAttempt a) =>
    a.Region == "sanctioned"
        ? Hook.Reject(Problem.Forbidden, "region not served")
        : Hook.Continue(a);

[After(Auth.SignIn, created: true)]            // an observer: it watches, it cannot refuse
public static async Task GrantStarterPack(Player player)
{
    await player.Inventory.Grant("chest.gold", count: 1);
}
// a gate: it may refuse, and it is fail-closed
export const gateRegion = before(Auth.signIn, (a: SignInAttempt) =>
  a.region === 'sanctioned'
    ? Hook.reject(Problem.forbidden, 'region not served')
    : Hook.continue(a));

// an observer: it watches, it cannot refuse
export const grantStarterPack = after(Auth.signIn, { created: true },
  async (player: Player) => {
    await player.inventory.grant('chest.gold', { count: 1 });
  });
@before(auth.sign_in)                          # a gate: it may refuse, and it is fail-closed
def gate_region(a: SignInAttempt) -> Verdict:
    if a.region == "sanctioned":
        return Hook.reject(Problem.FORBIDDEN, "region not served")
    return Hook.continue_(a)

@after(auth.sign_in, created=True)             # an observer: it watches, it cannot refuse
async def grant_starter_pack(player: Player):
    await player.inventory.grant("chest.gold", count=1)
Bientôt — Go

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.

S’exécute hors du moteur

Une surcharge comme un hook sont des fonctions cloud : elles s’exécutent sur la plateforme, pas dans le moteur. Écrivez-les en C#, TypeScript ou Python — Unreal s’abonne aux événements qu’elles produisent. Des fonctions écrites côté moteur sont prévues : graphes Blueprint et C++ (traduit vers un langage de script validé par la plateforme). Les unes comme les autres seront analysées et publiées comme n’importe quelle déclaration ; l’exécution reste sur la plateforme.

S’exécute hors du moteur

Une surcharge comme un hook sont des fonctions cloud : elles s’exécutent sur la plateforme, pas dans le moteur. Écrivez-les en C#, TypeScript ou Python — Unity s’abonne aux événements qu’elles produisent.

La forme déclarée est exactement ce qu’affiche le panneau : chaque point montre ses handlers, leur type et l’ordre résolu. Le type détermine ce qui se passe quand un handler échoue :

TypeQuand le handler lui-même échoueEn cas de refus
une barrièrel’étape est refusée : un contrôle de région injoignable ne vaut pas un contrôle de région réussiun code du catalogue de la plateforme, accompagné d’un motif lisible. L’appelant se fonde sur le code ; le texte du motif peut changer et être traduit
un observateurl’étape reste acquise : un pack de départ qui n’est pas arrivé coûte un coffre, pas la connexionil ne peut pas refuser

Un hook ne décide jamais qui s’est connecté

Une barrière répond par oui ou par non à propos d’une identité que la plateforme a déjà établie. Elle ne désigne pas le joueur, n’attribue pas d’identité et ne remplace pas la confirmation du fournisseur (provider).

Le modèle

device · subject (the guest)Steam · subject (via Link)another player, same persona player — one stable id, never reused linklink: anonymous → registered, one waymerge: terminal; its id keeps resolving
Une liaison, c’est fournisseur · sujet · joueur : elle fait évoluer un invité dans un seul sens, et après une fusion l’id du joueur fusionné continue de se résoudre

Un joueur porte une identité ; ce n’est pas une ligne de votre schéma, et son identifiant est stable et jamais réutilisé — y compris lors d’une fusion : l’id d’un joueur fusionné continue de se résoudre au lieu de devenir une référence pendante. Une liaison (link) est un triplet : fournisseur · sujet externe · joueur.

ToujoursCe que c’est
fournisseur + sujetest unique, et cette unicité est une source de conflit, pas une interdiction : quand « ce compte est déjà pris », la réponse consiste à choisir une fusion
au plus une liaison par fournisseur et par joueurun second compte du même fournisseur est un conflit
un sujet externen’est jamais l’identifiant d’un joueur : il appartient au fournisseur, et l’utiliser comme le nôtre lierait nos ids aux siens
type d'identité et statut d'accèssont deux axes distincts : anonymous contre registered en est un ; active / suspended / banned en est un autre, si bien que « anonyme banni » et « enregistré suspendu » s’expriment l’un comme l’autre
une session et un identifiantsont deux choses différentes : la session est l’enregistrement ; l’identifiant est ce que vous présentez. Révoquer une session invalide tous ses identifiants et ferme ses abonnements ouverts
plusieurs sessions simultanéeschacune se révoque indépendamment
les claims d'un identifiantsont du contexte déclaré — région, langue — et rien que du contexte. Une claim ne confère jamais d’autorité
une empreinte d'appareiln’est pas une identité : ce n’est jamais un motif d’admission, seulement un motif de refus, et elle est stockée et comparée sous une forme irréversible

Les trois machines à états.

MachineÉtats
le type d’identitéanonymous → registered, et la transition est à sens unique
le statut d’accèsactive ⇄ suspended, et active → banned → active pour une levée de bannissement
le joueuralive → merged, où merged est terminal : un joueur fusionné ne se reconnecte plus

Ce que vous déclarez.

DéclarationCe que c’est
politique de connexionsi la connexion anonyme est autorisée, et les autres règles qui encadrent la connexion. Elle se déclare comme un attribut sur le point de montage du module — ni dans un fichier de configuration à côté du code, ni construite au runtime
rôle par défautl’ensemble de rôles que reçoit un nouveau joueur à sa première connexion. Ce défaut n’a pas lui-même de valeur par défaut : si vous ne déclarez rien, les nouveaux joueurs arrivent sans aucun rôle, et c’est une déclaration légitime, pas un oubli
politique de sessionsce qui se passe quand le plafond de sessions simultanées est atteint : évincer la plus ancienne avec un événement, ou refuser la nouvelle. Pas de valeur par défaut
politique de suppressioncomment la suppression d’un joueur se répercute sur les données qui le référencent

Où se configure un fournisseur. Dans le plan opérateur, pas dans le code : les identifiants d’un store n’ont rien à faire dans un dépôt. Ce qui est déclaré arrive dans la console d’administration en lecture seule.

Ce qu’apporte l’attribution d’un rôle. Les rôles ne sont pas l’affaire du seul opérateur : l’interface propose l’attribution et le retrait d’un rôle pour un joueur, si bien qu’un jeu peut, depuis son propre code, promouvoir un officier de guilde ou confier ses pouvoirs à l’organisateur d’un tournoi.

ToujoursCe que c’est
ce n'est pas une auto-promotionl’attribution exige la permission atomique déclarée pour cela, et un acteur qui ne la détient pas reçoit un simple forbidden, pas une opération qui échoue en silence
l'attribution est idempotenteattribuer un rôle que le joueur détient déjà est un succès, pas un conflit : l’état, c’est l’ensemble des rôles, pas l’historique des appels. Contrairement à la connexion, cette opération n’a donc pas besoin de clé d’idempotence
un changement de rôle n'est pas instantanéil prend effet sans que l’identifiant soit réémis, et devient observable au plus tard à l’expiration du délai d’obsolescence déclaré pour le cache des droits. Un code qui attribue un rôle puis le vérifie aussitôt sur un client connecté doit donc tenir compte de ce délai

La composition des rôles et ce qu’ils débloquent sont décrits dans Access & Roles.

Erreurs

  • Sans identifiant, ou avec un identifiant expiré, la réponse est not authenticated, et un renouvellement règle le problème. Un identifiant révoqué reçoit la même réponse, mais là, le renouvellement n’y peut rien : seule une nouvelle connexion fonctionne.
  • Un identifiant déjà renouvelé et présenté une seconde fois est un conflit : c’est ce qui rend la rotation détectable au lieu de la tolérer en silence.
  • Un joueur banni ou suspendu, ainsi qu’une empreinte bannie, reçoivent forbidden, et il est inutile de réessayer.
  • Une paire fournisseur+sujet déjà prise est un conflit, qu’on peut réessayer après avoir choisi une fusion ; un second compte du même fournisseur est un conflit qu’aucune nouvelle tentative ne changera.
  • Délier la dernière méthode de connexion est un refus de validation : plus personne ne pourrait accéder au compte.
  • Fusionner un joueur déjà fusionné est un conflit : merged est terminal.
  • Un fournisseur indisponible répond unavailable, et il vaut la peine de réessayer avec un délai croissant (backoff) ; un fournisseur qui rejette l’identifiant répond not authenticated, ce qui justifie une seule nouvelle tentative, pas une boucle.
  • Un dépassement du nombre de tentatives de connexion reçoit une réponse de la catégorie limitation de débit (rate limit), avec un délai.

Limites

Les valeurs chiffrées arriveront avec le chapitre sur les limites de la plateforme ; ce qui se passe à chaque plafond est déjà fixé.

  • Sessions simultanées par joueur : selon la politique déclarée, éviction de la plus ancienne avec un événement, ou refus de la nouvelle. Il n’y a pas de valeur par défaut.
  • Tentatives de connexion par période, et tentatives de liaison d’une paire déjà prise : une limitation de débit avec un délai, et le compteur de tentatives reste dans l’historique.
  • Liaisons par joueur : la liaison d’un fournisseur supplémentaire est refusée comme un conflit.
  • Durée de vie d’un identifiant : not authenticated, qu’un renouvellement résout. Durée de vie d’un identifiant de renouvellement : seule une nouvelle connexion.
  • Rétention d’un joueur anonyme sans aucune connexion : suppression selon la politique déclarée, avec un événement. La politique se déclare explicitement ; il n’y a pas de valeur par défaut.
  • Entrées de la liste des empreintes bannies : un ajout est refusé, et les anciennes entrées ne sont jamais évincées en silence.