Esta página aún no está traducida: se muestra la versión en inglés.
Esquema como código
Declara el modelo en código, súbelo y recibe los tipos de vuelta. Así llega un desarrollador al esquema: el panel de administración y el código editan el mismo modelo, y la generación de código cierra el ciclo para cada motor.
Cuándo usarlo
- Quieres que el modelo de datos viva en el código y se revise como código: declaras,
schema diff,schema push. - Los tipos del motor nunca deben desfasarse del modelo desplegado:
schema codegenvuelve a generar los tipos de Unreal C++ y Unity C#. - Un cambio incompatible tiene que poder leerse y cancelarse antes de ejecutarse: propuesta → plan → aplicación.
- Reutilizas un mismo paquete (
Stat,Interactable) en varios proyectos: lo declaras una sola vez como preset. - No lo necesitas si un operador solo ajusta valores en el panel de administración. Aun así, el cambio del modelo aparece en la comparación con el código.
Quién hace qué
| Actor | Qué hace en esta página |
|---|---|
schema-author | declara en código entidades, partes y enumeraciones, las compara con el modelo desplegado y las sube |
operator | revisa el resumen en el panel, propone y aplica migraciones |
ci | el pipeline de compilación, que se ejecuta con una clave backend-service: sube el esquema al hacer merge y después vuelve a generar los tipos de los motores |
En resumen
Item con una parte Stats incrustada y una enumeración, y se sube todo como un solo 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 = 0Todavía no está decidido cómo declarar con atributos en Go (O-1). Los ejemplos llegarán cuando se elija la forma de escribirlo.
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
Subir es un paso explícito
Guardar un archivo no sube nada. Una declaración llega al modelo desplegado solo cuando se ejecuta playserv push (o schema push), desde tu máquina o desde CI, y lleva consigo la revisión contra la que se hizo la comparación. Una edición en el panel aparece en schema diff como una divergencia; llevar un cambio en cualquiera de las dos direcciones siempre es un comando que ejecutas tú.
El modelo
Qué lleva una declaración.
| Lleva | Qué es |
|---|---|
key | el nombre estable con el que se hace referencia a ella. Renombrar en el código es un cambio de nombre, no un borrado seguido de una creación |
kind | una entidad, una parte o una enumeración, declarada en código |
modo de propiedad | seed: el código crea el registro si no existe y una nueva subida no toca los valores, así que a partir de ahí los administra la consola de administración; o managed: el registro siempre pertenece al código, cada subida lleva los valores a lo declarado y las ediciones desde la consola de administración se rechazan en lugar de aplicarse y perderse en la siguiente subida |
preset | opcional: un paquete reutilizable —un preset de entidad, como stats o world-objects— que se declara una vez y se aplica como un tipo. No introduce ningún tipo nuevo de declaración |
Qué garantiza una subida.
| Siempre | Qué es |
|---|---|
la correspondencia | se hace por clave, nunca por símbolo: si renombras el símbolo y vuelves a subir, queda un solo registro y no dos |
el informe | dice exactamente qué va a cambiar antes de aplicarlo y, después, qué sobrescribió |
el origen | se puede distinguir: un registro creado al subir desde el código se diferencia de uno creado de otra manera |
la revisión | viaja con la subida, y la subida se aplica completa o no se aplica |
Qué garantiza la generación de código.
| Siempre | Qué es |
|---|---|
la regeneración | ocurre después de cada subida, y los tipos generados nunca se editan a mano: si los regeneras y comparas, no hay cambios |
los nombres | siguen a la declaración, dondequiera que se haya escrito: el campo Rarity de Item se convierte en UPSItem::Rarity en Unreal y en Item.Rarity en Unity |
las dos direcciones | no se separan: todo lo que se declara en código aparece en el panel, y todo lo que un operador crea en el panel se compara limpiamente con el código, así que schema diff muestra cada diferencia |
Qué garantiza una migración.
| Siempre | Qué es |
|---|---|
cuándo hace falta | cuando un cambio en la declaración de datos persistentes reescribe valores que ya existen |
qué declara | una versión, una vista previa, una aplicación ordenada, una reversión si algo falla y un resultado final observable |
la coexistencia | mientras hay dos versiones de datos activas, las lecturas y escrituras declaran qué versiones aceptan: el runtime nunca deduce la compatibilidad a partir de los nombres de los campos |
Errores
- Quien llama sin el rol recibe
forbidden, y el modelo desplegado no se toca: un rechazo nunca deja una subida a medias. Es un rechazo distinto al de una revisión desactualizada, que esprecondition_failedy significa que la comparación se hizo contra un esquema que cambió desde entonces: vuelve a comparar y sube otra vez. - Un valor fuera de un límite declarado se rechaza al escribirlo, nunca se recorta, y lo mismo pasa con una cadena que supera su longitud declarada. Recortar daría un valor válido pero incorrecto.
- Una secuencia UTF-8 inválida se rechaza al escribirla, en lugar de repararse.
- Un cambio persistente incompatible sin una migración declarada no se puede publicar.
Límites
Los topes que dependen de la forma de la declaración se comprueban al declarar —al desplegar o al publicar— y no en el primer uso, en todos los casos en que el síntoma en tiempo de ejecución no parecería un rechazo. Las cifras llegarán con el capítulo sobre los límites de la plataforma.