Aller au contenu principal
Le modèle de votre jeu

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

Le schéma dans le code

Déclarez le modèle dans le code, publiez-le, récupérez les types. C’est ainsi que le développeur travaille avec le schéma : le panneau d’administration et le code décrivent le même modèle, et la génération de code boucle la boucle pour chaque moteur.

Quand l’utiliser

  • Votre modèle de données doit vivre dans le code et être relu comme du code : déclarer, schema diff, schema push.
  • Les types du moteur ne doivent jamais s’écarter du modèle déployé : schema codegen régénère le code Unreal C++ et Unity C#.
  • Un changement incompatible doit pouvoir être lu, et annulé, avant de s’exécuter : proposer → planifier → appliquer.
  • Vous réutilisez le même ensemble (Stat, Interactable) d’un projet à l’autre : déclarez-le une fois comme preset.
  • Inutile si un opérateur se contente d’ajuster des valeurs dans le panneau d’administration. La modification du modèle revient quand même dans le code par le diff.

Qui fait quoi

ActeurCe qu’il fait sur cette page
schema-authordéclare dans le code les entités, les parties et les énumérations, les compare au modèle déployé et les publie
operatorconsulte la vue d’ensemble dans le panneau, propose et applique les migrations
cile pipeline de build, qui tourne avec une clé backend-service : il publie le schéma à chaque merge, puis régénère les types des moteurs

En bref

Item avec une partie Stats intégrée et une énumération, publiés en un seul schéma
[Entity("item")]
public class Item
{
    public string Name = "";
    public Rarity Rarity;              // an enum declared the same way
    public Stats Stats = new();        // a part — embedded, no lifecycle of its own
}

[Part("stats")]
public class Stats { public int Power; public int Weight; }
@Entity('item')
export class Item {
  name = '';
  rarity!: Rarity;                   // an enum declared the same way
  stats = new Stats();               // a part — embedded, no lifecycle of its own
}

@Part('stats')
export class Stats { power = 0; weight = 0; }
@entity("item")
class Item:
    name: str = ""
    rarity: Rarity                     # an enum declared the same way
    stats: Stats = Stats()             # a part — embedded, no lifecycle of its own

@part("stats")
class Stats:
    power: int = 0
    weight: int = 0
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.

USTRUCT(PSPart = "stats")
struct FItemStats
{
    GENERATED_BODY()

    UPROPERTY() int32 Power;
    UPROPERTY() int32 Weight;
};

UCLASS(PSEntity = "item")
class UItem : public UObject
{
    GENERATED_BODY()

    UPROPERTY() FString    Name;
    UPROPERTY() EPSRarity  Rarity;       // an enum declared the same way
    UPROPERTY() FItemStats Stats;        // a part — embedded, no lifecycle of its own
};
// declarations compile into the same pushed model — playserv push from the UE project or CI
Unity C# is the same C# API here — les mêmes attributs compilent dans Unity, sur la version de référence 2021.3 — aucune syntaxe C# 12 ici
[Entity("item")]
public class Item
{
    public string Name = "";
    public Rarity Rarity;              // an enum declared the same way
    public Stats Stats = new();        // a part — embedded, no lifecycle of its own
}

[Part("stats")]
public class Stats { public int Power; public int Weight; }
playserv schema diff       # local declarations vs deployed schema
playserv schema push       # with a revision precondition — no blind overwrites
playserv schema codegen    # regenerate Unreal C++ / Unity C# types

La publication est une étape explicite

Enregistrer un fichier n’envoie rien. Une déclaration n’atteint le modèle déployé que lorsque playserv push (ou schema push) s’exécute, sur votre machine ou dans la CI, et elle emporte la révision par rapport à laquelle le diff a été calculé. Une modification faite dans le panneau apparaît comme un écart dans schema diff. Dans un sens comme dans l’autre, faire passer un changement reste toujours une commande que vous lancez vous-même.

Le modèle

Ce que contient une déclaration.

ContientCe que c’est
keyle nom stable par lequel on la désigne. Renommer dans le code reste un renommage, pas une suppression suivie d’une création
kindune entité, une partie ou une énumération, déclarée dans le code
mode de propriétéseed — le code crée l’enregistrement s’il n’existe pas, et une nouvelle publication ne touche pas aux valeurs : la console d’administration en devient ensuite propriétaire. Ou managed — le code en reste toujours propriétaire, chaque publication ramène les valeurs à ce qui est déclaré, et les modifications venant de la console d’administration sont refusées plutôt qu’appliquées puis perdues à la publication suivante
presetfacultatif : un ensemble réutilisable — un preset d’entité comme stats ou world-objects — déclaré une fois et appliqué comme un type. Il n’introduit aucune nouvelle sorte de déclaration

Ce que garantit une publication.

ToujoursCe que c’est
correspondancese fait par clé, jamais par symbole : si vous renommez le symbole puis publiez à nouveau, il reste un seul enregistrement, pas deux
le rapportindique exactement ce qui va changer avant l’application, puis ce qui a été écrasé
originereste identifiable : un enregistrement créé par une publication depuis le code se distingue de celui créé ailleurs
la révisionaccompagne la publication, qui s’applique en entier ou pas du tout

Ce que garantit la génération de code.

ToujoursCe que c’est
régénérationa lieu après chaque publication, et on ne modifie jamais à la main les types générés : régénérer puis comparer ne produit aucune différence
nommagesuit la déclaration, où qu’elle ait été écrite — le champ Rarity de Item devient UPSItem::Rarity dans Unreal et Item.Rarity dans Unity
les deux sensne divergent pas : tout ce qui est déclaré dans le code apparaît dans le panneau, et tout ce qu’un opérateur crée dans le panneau se compare proprement au code. schema diff montre donc chaque différence

Ce que garantit une migration.

ToujoursCe que c’est
quand elle est nécessairelorsqu’une déclaration persistante change et que ce changement réécrit des valeurs existantes
ce qu'elle déclareune version, un aperçu, une application ordonnée, un rollback en cas d’échec et un résultat final observable
coexistencetant que deux versions des données sont actives, les lectures et les écritures déclarent quelles versions elles acceptent — le runtime ne déduit jamais la compatibilité des noms de champs

Erreurs

  • Un appelant sans le rôle reçoit forbidden, et le modèle déployé reste intact : un refus n’est jamais une publication partielle. Ce refus diffère de celui d’une révision périmée, qui renvoie precondition_failed : le diff a été calculé par rapport à un schéma qui a changé depuis. Recalculez le diff et publiez à nouveau.
  • Une valeur hors des bornes déclarées est refusée à l’écriture, jamais ramenée dans l’intervalle, tout comme une chaîne qui dépasse sa longueur déclarée. La borner donnerait une valeur valide, mais fausse.
  • Une séquence UTF-8 invalide est rejetée à l’écriture, pas réparée.
  • Un changement persistant incompatible sans migration déclarée ne peut tout simplement pas être publié.

Limites

Les plafonds liés à la déclaration sont vérifiés au moment de la déclaration — au déploiement ou à la publication — plutôt qu’à la première utilisation, partout où le symptôme à l’exécution ne ressemblerait pas à un refus. Les valeurs chiffrées arriveront avec le chapitre sur les limites de la plateforme.