Aller au contenu principal
Briques de base

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

Core

Core est le seul objet que vous créez ; tout le reste en découle. Vous lui passez une clé, et vous disposez du contexte, de l’identité, d’erreurs typées, du traçage et des lots. Chaque appel de module passe par lui ; aucun module n’en possède sa propre copie.

Quand l’utiliser

  • Vous devez savoir qui vous êtes et où vous êtes : identité, rôles, modules débloqués, projet, environnement et région.
  • Une fonction cloud doit écrire en tant que joueur. L’écriture est attribuée à ce joueur, et l’enregistrement mentionne à la fois la fonction et le joueur.
  • Une nouvelle tentative ne doit pas s’appliquer deux fois : les opérations par lot (batch) portent une clé d’idempotence.
  • Vous voulez aiguiller votre code selon l’erreur et pouvoir la retrouver : chaque exception est un Problem typé, avec un code stable.
  • Inutile si vous cherchez la messagerie, les appels ou l’état. Ce sont les primitives : Events, RPC et Data & Subscriptions, qui s’adressent à une Entity ; leurs audiences sont les Groups. Les envois et téléchargements de fichiers relèvent de Files & UGC, le montage et les espaces de noms de Sous le capot.

Qui fait quoi

ActeurCe qu’il fait sur cette page
tout acteurlit son identité, le contexte et ses rôles via Whoami
backend-serviceagit en tant que joueur ; regroupe des opérations idempotentes en lots
operatorlit les traces des appels échoués ou relancés

En bref

Whoami, le contexte ambiant et un lot qu’on peut relancer sans risque
var me  = PlayServ.Whoami();              // identity, roles, unlocked modules
var env = PlayServ.Context;               // project · env · region

// retries never double-apply: the batch carries an idempotency key
await PlayServ.Batch(key: orderId, b =>
{
    b.Inventory.Grant(playerId, "starter.pack");
    b.Inventory.Grant(playerId, "starter.emote");
});
const me = playserv.whoami();             // identity, roles, unlocked modules
const env = playserv.context;             // project · env · region

// retries never double-apply: the batch carries an idempotency key
await playserv.batch(orderId, (b) => {
  b.inventory.grant(playerId, 'starter.pack');
  b.inventory.grant(playerId, 'starter.emote');
});
me = playserv.whoami()                    # identity, roles, unlocked modules
env = playserv.context                    # project · env · region

# retries never double-apply: the batch carries an idempotency key
async with playserv.batch(key=order_id) as b:
    b.inventory.grant(player_id, "starter.pack")
    b.inventory.grant(player_id, "starter.emote")
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.

const FPSActor Me = Client->Whoami();               // identity, roles, unlocked modules
const FPSPlatformContext Env = Client->Context();   // project · env · region

// retries never double-apply: each keyed operation is safe to repeat
Client->Commerce->Entitlements->Grant(FPSIdempotencyKey(PackGrantId), PlayerId, PSKeys::Item::StarterPack);
Client->Commerce->Entitlements->Grant(FPSIdempotencyKey(EmoteGrantId), PlayerId, PSKeys::Item::StarterEmote);
// 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 — fonctionne tel quel dans Unity avec les types générés
var me  = PlayServ.Whoami();              // identity, roles, unlocked modules
var env = PlayServ.Context;               // project · env · region

// retries never double-apply: the batch carries an idempotency key
await PlayServ.Batch(key: orderId, b =>
{
    b.Inventory.Grant(playerId, "starter.pack");
    b.Inventory.Grant(playerId, "starter.emote");
});

L’identité vit dans Core : aucun appel ne prend de paramètre session ou ctx.

Le modèle

Ce que contient chaque erreur.

ChampCe que c’est
codele nom du refus, lisible par une machine. Stable : il reprend les codes existants de la plateforme au lieu d’en inventer de nouveaux
categoryla classe du refus ; elle vous dit si une nouvelle tentative a un sens
identifiant de tracel’id de cette occurrence précise. Toujours présent, erreurs locales comprises : le support n’a jamais besoin de reproduire le problème
explanationun texte destiné à un humain. Pas stable : il peut changer ou être traduit à tout moment
erreurs par champpour les refus de validation : le champ, le code et le message de chaque champ rejeté

Décidez selon le code, pas selon le texte

Mettez code et category dans votre logique. explanation s’adresse aux humains et change sans préavis. Un binding qui n’expose que le message est bogué.

Trois origines.

OrigineCe qui s’est passé
platformla plateforme a refusé, avec un code tiré de son catalogue
localle SDK a refusé avant l’envoi, avec un code tiré de sa propre liste publiée
unknownl’appel a été envoyé et aucune réponse n’est revenue : le résultat est inconnu

Vrai pour tout refus.

  • Une opération refusée n’a rien appliqué : une branche d’erreur n’a donc pas besoin de relire l’état pour compenser. Les deux exceptions le signalent là où elles se produisent : un timeout, dont le résultat est inconnu, et un lot dont la sémantique s’applique élément par élément.
  • L’erreur est la même quel que soit le chemin par lequel elle vous parvient : même code, même catégorie, même origine, que le binding lève une exception, renvoie un résultat ou rappelle un abonnement.
  • Un timeout correspond à l’origine unknown, pas à une réponse. La conduite à tenir est déclarée opération par opération.

Erreurs

Le même appel, refusé. rate_limited indique aussi quand une nouvelle tentative est permise
try { await PlayServ.Inventory.Grant(playerId, "starter.pack"); }
catch (Problem p) when (p.Code == "rate_limited")
{
    Hud.RetryAt(p.RetryAfter);
}
try { await playserv.inventory.grant(playerId, 'starter.pack'); }
catch (p) {
  if (Problem.code(p) === 'rate_limited') hud.retryAt(p.retryAfter);
  else throw p;
}
try:
    await playserv.inventory.grant(player_id, "starter.pack")
except Problem as p:
    if p.code == "rate_limited":
        hud.retry_at(p.retry_after)
    else:
        raise
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.

// UE builds run without exceptions — the completion carries the result, read explicitly
Client->Commerce->Entitlements->Grant(FPSIdempotencyKey(GrantId), PlayerId, PSKeys::Item::StarterPack,
    TPSOnResult<void>::CreateWeakLambda(this, [this](const TPSResult<void>& Result)
    {
        if (Result.IsRefused() && Result.Refusal().Code == FPSFailureCode::RateLimited)
        {
            Hud->RetryAt(Result.Refusal().RetryNotBefore);   // TOptional<FDateTime> — an instant, not a delay
        }
    }));
// 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 — fonctionne tel quel dans Unity avec les types générés
try { await PlayServ.Inventory.Grant(playerId, "starter.pack"); }
catch (Problem p) when (p.Code == "rate_limited")
{
    Hud.RetryAt(p.RetryAfter);
}

Un appelant sans le rôle est refusé, pas servi a minima. Pour une session joueur ou client, les lignes fn et adm échouent : appeler act as player, émettre une trace ou en lire une renvoie un Problem de code forbidden. L’identifiant est valide ; ce sont ses rôles qui ne donnent pas ce droit. Relancer avec la même clé d’idempotence n’y change rien.

Limites

Ne relancez pas un appel accepté

Les limites s’appliquent à l’admission. Un appel accepté a passé la limite et s’exécutera, quel que soit son temps d’attente. Le refus d’un appel ultérieur ne l’affecte pas : relancer l’appel accepté fait le travail deux fois.

Vous découvrez une limite en étant refusé. Le SDK n’expose ni la valeur en vigueur, ni la marge restante, ni d’avertissement à l’approche du seuil, et les joueurs ne voient jamais les limites. Le refus contient ce qu’il vous faut :

  • la catégorie, qui dit si une nouvelle tentative a un sens
  • à qui appartenait la limite
  • quand une nouvelle tentative est permise, et sur quelle fenêtre