Перейти до основного вмісту
Модель гри

Цю сторінку ще не перекладено — показано англійську версію.

Схема в коді

Оголосіть модель у коді, надішліть її на платформу й отримайте типи назад. Так зі схемою працює розробник: адмін-панель і код описують одну й ту саму модель, а кодогенерація замикає коло для кожного рушія.

Коли це потрібно

  • Модель даних має жити в коді й проходити рев’ю як код: оголосили, schema diff, schema push.
  • Типи в рушії ніколи не повинні розходитися з розгорнутою моделлю: schema codegen заново генерує Unreal C++ і Unity C#.
  • Несумісну зміну треба прочитати й за потреби скасувати, перш ніж вона виконається: запропонувати → спланувати → застосувати.
  • Той самий набір (Stat, Interactable) потрібен у кількох проєктах: оголосіть його один раз як пресет.
  • Не потрібно, якщо оператор лише підправляє значення в адмін-панелі. Зміну моделі, зроблену там, diff однаково поверне в код.

Хто що робить

АкторЩо робить на цій сторінці
schema-authorоголошує в коді сутності, частини й переліки, порівнює їх із розгорнутою моделлю та надсилає
operatorпереглядає огляд у панелі, пропонує й застосовує міграції
ciконвеєр збирання з ключем backend-service: після злиття надсилає схему (schema), а потім заново генерує типи для рушіїв

Коротко

Item із вбудованою частиною Stats і переліком — усе йде на платформу однією схемою
[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
Незабаром — Go

Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.

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 — ті самі атрибути компілюються в Unity на базовій версії 2021.3 — синтаксису C# 12 тут немає
[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

Надсилання — окремий крок

Коли ви зберігаєте файл, нічого не завантажується. Оголошення (declaration) потрапляє в розгорнуту модель, лише коли запускається playserv push (або schema push) — на вашій машині чи в CI. Разом із ним іде ревізія, відносно якої рахували diff. Правка в панелі з’явиться в schema diff як розбіжність. Хоч би в який бік ви переносили зміну, це завжди команда, яку ви запускаєте самі.

Модель

З чого складається оголошення.

МіститьЩо це
keyстабільне ім’я, за яким до оголошення звертаються. Перейменування в коді так і лишається перейменуванням, а не видаленням із повторним створенням
kindсутність (entity), частина або перелік, оголошені в коді
режим володінняseed — код створює запис, якщо його немає, а повторне надсилання значень не чіпає, тож далі ними розпоряджається адмін-консоль. Або managed — запис завжди належить коду: кожне надсилання приводить значення до оголошених, а правки з адмін-консолі платформа відхиляє, щоб вони не застосувалися й не зникли під час наступного надсилання
presetнеобов’язково: набір для повторного використання — пресет сутності, наприклад stats або world-objects. Його оголошують один раз і застосовують як тип. Нового виду оголошень він не додає

Що гарантує надсилання.

ЗавждиЩо це
зіставленняза ключем, а не за символом: якщо перейменувати символ і надіслати схему знову, запис лишиться один, а не два
звітпоказує, що саме зміниться, ще до застосування, а після — що було перезаписано
походженнярозрізняється: запис, який створило надсилання з коду, можна відрізнити від створеного іншим шляхом
ревізіяіде разом із надсиланням, а саме надсилання застосовується повністю або не застосовується зовсім

Що гарантує кодогенерація.

ЗавждиЩо це
повторна генераціявідбувається після кожного надсилання, а згенеровані типи ніколи не правлять вручну: якщо згенерувати їх наново й порівняти, змін не буде
іменаідуть за оголошенням, хоч би де його написали: поле Rarity в Item стає UPSItem::Rarity в Unreal і Item.Rarity в Unity
два напрямкине розходяться: усе, що оголошено в коді, з’являється в панелі, а все, що оператор створив у панелі, чисто порівнюється з кодом. Тому schema diff показує кожну відмінність

Що гарантує міграція.

ЗавждиЩо це
коли вона потрібнаколи змінюється оголошення збережуваних даних і ця зміна переписує наявні значення
що вона оголошуєверсію, попередній перегляд, упорядковане застосування, відкат у разі збою та видимий результат після завершення
співіснуванняпоки працюють дві версії даних, операції читання й запису оголошують, які версії вони приймають. Середовище виконання ніколи не виводить сумісність з імен полів

Помилки

  • Той, хто викликає без потрібної ролі, отримує forbidden, а розгорнута модель лишається недоторканою: відмова ніколи не буває частковим надсиланням. Це інша відмова, ніж застаріла ревізія: та повертає precondition_failed і означає, що diff рахували для схеми, яка відтоді змінилася. Порівняйте ще раз і надішліть знову.
  • Значення поза оголошеною межею платформа відхиляє під час запису й ніколи не обрізає до межі. Так само з рядком, довшим за оголошену довжину. Обрізання дало б значення, яке коректне, але хибне.
  • Недійсну послідовність UTF-8 платформа відхиляє під час запису, а не виправляє.
  • Несумісну зміну збережуваних даних без оголошеної міграції опублікувати взагалі неможливо.

Обмеження

Обмеження, що випливають з оголошень, платформа перевіряє ще на етапі оголошення — під час розгортання чи публікації, а не при першому використанні, якщо під час роботи симптом не був би схожий на відмову. Конкретні числа з’являться разом із розділом про ліміти платформи.