Pular para o conteúdo principal
O modelo do seu jogo

Esta página ainda não foi traduzida: mostrando a versão em inglês.

Esquema como código

Declare o modelo em código, publique e receba os tipos de volta. É por aqui que o desenvolvedor chega ao esquema: o painel de administração e o código editam o mesmo modelo, e a geração de código fecha o ciclo para cada engine.

Quando usar

  • O modelo de dados deve morar no código e passar por revisão como código: declare, schema diff, schema push.
  • Os tipos da engine nunca podem se afastar do modelo publicado: schema codegen gera de novo o código Unreal C++ e Unity C#.
  • Uma mudança incompatível precisa poder ser lida e cancelada antes de rodar: propor → planejar → aplicar.
  • Você reaproveita o mesmo pacote (Stat, Interactable) em vários projetos: declare-o uma vez como preset.
  • Dispensável quando um operador só ajusta valores no painel de administração. Mesmo assim, a mudança no modelo aparece no diff contra o código.

Quem faz o quê

AtorO que faz nesta página
schema-authordeclara entidades, partes e enums em código, faz o diff e publica
operatorrevisa a visão geral no painel, propõe e aplica migrações
cio pipeline de build, rodando com uma chave backend-service: publica a cada merge e depois gera de novo os tipos da engine

Resumo

Como declarar Item com uma parte Stats embutida e um enum, publicados como um único esquema
[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
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

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 — os mesmos atributos compilam no Unity, com a versão 2021.3 como base — nada de sintaxe do C# 12 aqui
[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

A publicação é um passo explícito

Salvar um arquivo não envia nada. Uma declaração só chega ao modelo publicado quando playserv push (ou schema push) roda, na sua máquina ou no CI, e leva junto a revisão contra a qual o diff foi calculado. Uma edição feita no painel aparece como divergência em schema diff. Nos dois sentidos, a mudança só passa de um lado para o outro quando você executa um comando.

O modelo

O que uma declaração carrega.

CarregaO que é
keyo nome estável pelo qual ela é referenciada. Renomear no código é uma renomeação, e não uma exclusão seguida de criação
kinduma entidade, uma parte ou um enum, declarados em código
modo de posseseed: o código cria o registro se ele não existir, e uma nova publicação não mexe nos valores, que a partir daí pertencem ao console de administração. Ou managed: o código é sempre o dono, cada publicação leva os valores de volta ao que está declarado, e edições feitas no console de administração são recusadas, em vez de aplicadas e perdidas na publicação seguinte
presetopcional: um pacote reutilizável — um preset de entidade, como stats ou world-objects — declarado uma vez e aplicado como um tipo. Não cria nenhum tipo novo de declaração

O que uma publicação garante.

SempreO que é
correspondênciaé feita pela chave, nunca pelo símbolo: publicar de novo depois de renomear o símbolo deixa um registro, e não dois
o relatóriodiz exatamente o que vai mudar antes de aplicar e, depois, o que foi sobrescrito
origemé identificável: um registro criado por uma publicação a partir do código se distingue de um criado em outro lugar
a revisãovai junto com a publicação, que é aplicada inteira ou não é aplicada

O que a geração de código garante.

SempreO que é
regeneraçãoacontece depois de cada publicação, e os tipos gerados nunca são editados à mão: gerar de novo e depois fazer o diff não produz nenhuma mudança
nomesseguem a declaração, onde quer que ela tenha sido escrita — o campo Rarity de Item vira UPSItem::Rarity no Unreal e Item.Rarity no Unity
os dois sentidosnão se separam: tudo o que é declarado no código aparece no painel, e tudo o que um operador cria no painel gera um diff limpo contra o código. Por isso schema diff mostra todas as diferenças

O que uma migração garante.

SempreO que é
quando é necessáriaquando uma mudança em uma declaração persistente reescreve valores existentes
o que ela declarauma versão, uma prévia, uma aplicação em ordem, um rollback em caso de falha e um resultado final observável
coexistênciaenquanto duas versões dos dados estão ativas, leituras e escritas declaram quais versões aceitam — o runtime nunca deduz compatibilidade a partir dos nomes dos campos

Erros

  • Quem chama sem o papel (role) recebe forbidden, e o modelo publicado fica intacto: uma recusa nunca é uma publicação parcial. Essa recusa é diferente da revisão desatualizada, que vem como precondition_failed e significa que o diff foi calculado contra um esquema que mudou desde então. Faça o diff de novo e publique outra vez.
  • Um valor fora de um limite declarado é recusado na escrita, e nunca ajustado ao limite. O mesmo vale para uma string maior que o comprimento declarado. Ajustar o valor ao limite produziria um valor válido — e errado.
  • Uma sequência UTF-8 inválida é rejeitada na escrita, e não corrigida.
  • Uma mudança persistente incompatível sem migração declarada não pode ser publicada de jeito nenhum.

Limites

Os tetos que dependem da forma da declaração são verificados no momento da declaração — no deploy ou na publicação — e não no primeiro uso, sempre que o sintoma em runtime não pareceria uma recusa. Os números chegam com o capítulo sobre os limites da plataforma.