Эта страница ещё не переведена — показана английская версия.
Схема в коде
Объявите модель в коде, отправьте её на платформу и получите типы обратно. Так со схемой работа ет разработчик: админ-панель и код описывают одну и ту же модель, а кодогенерация замыкает круг для каждого движка.
Когда это нужно
- Модель данных должна жить в коде и проходить ревью как код: объявили,
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, пока не решено (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[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 показывает каждое различие |
Что гарантирует миграция.
| Всегда | Что это |
|---|---|
когда она нужна | когда меняется объявление хранимых данных и это изменение переписывает уже существующие значения |
что она объявляет | версию, предпросмотр, упорядоченное применение, откат при сбое и наблюдаемый результат по завершении |
сосуществование | пока живы две версии данных, операции чтения и записи объявляют, какие версии они принимают. Среда выполнения никогда не выводит совместимость из имён полей |
Ошибки
- Вызывающий без нужной роли (role) получает
forbidden, и развёрнутая модель остаётся нетронутой: отказ никогда не оборачивается частичной отправкой. Это не то же самое, что устаревшая ревизия. Она возвращаетprecondition_failedи означает, что diff считался по схеме, которая с тех пор изменилась, — пересчитайте diff и отправьте снова. - Значение за пределами объявленных границ при записи отклоняется, а не обрезается до границы. То же со строкой длиннее объявленной. После обрезки значение было бы допустимым, но неверным.
- Некорректная последовательность UTF-8 при записи отклоняется, а не исправляется.
- Несовместимое изменение хранимых данных без объявленной миграции опубликовать нельзя вообще.
Ограничения
Потолки, которые задаёт сама форма объявлений, проверяются в момент объявления — при деплое или публикации, а не при первом использовании, если во время работы превышение не выглядело бы как отказ. Конкретные числа появятся вместе с главой об ограничениях платформы.