Entity schemas
Entity schemas
Section titled “Entity schemas”Entity schemas describe what data each entity type carries and how it participates in network sync. They live as one .yaml file per entity in the directory set by entity_schema in golem.yaml (default schemas/entities/).
File shape
Section titled “File shape”entity: Player # PascalCase type name in your game
vars: health: { tag: 1, type: double, sync: tick } display_name: { tag: 2, type: string, sync: once }entity: The type name; generated code uses it in type names (e.g.SyncedPlayerforPlayeringo-server).vars: Snake_case keys; each must carry an explicittag(see below) so the wire format stays stable when you rename or reorder variables.
Position
Section titled “Position”Every entity automatically has position fields. You do not declare them in YAML; they are injected by the framework and synchronized like tick fields.
In the default 2D mode, generated entities have pos_x and pos_y. The generated Go server struct exposes Position() (float32, float32) and SetPosition(x, y float32), while JS, Go, and C# clients expose posX / posY, PosX() / PosY(), and PosX / PosY.
In 3D mode (simulation.dimensions: 3 in golem.yaml), generated entities also have pos_z. Go server code uses Position3D() (float32, float32, float32) and SetPosition3D(x, y, z float32) for full 3D movement. Position() still returns the XY projection so existing interest management can keep using circular fields of interest. JS clients expose posZ, Go clients expose PosZ(), and C# / Unity clients expose PosZ.
The names pos_x, pos_y, and in 3D projects pos_z, are reserved—using them as var names in your schema results in an error.
Tags (tag)
Section titled “Tags (tag)”Every variable must have a tag — a positive integer that maps to a stable Protobuf field number in the serialized wire format. Tags must be unique within an entity:
vars: health: { tag: 1, type: double, sync: tick } # required; must be unique within this entity display_name: { tag: 2, type: string, sync: once }You can number tags freely starting from 1. You can leave gaps, and you can renumber them only if you discard all persisted snapshots at the same time (the schema fingerprint will change). Missing or duplicate tags are a bake error.
Global entities
Section titled “Global entities”If an entity should be visible to every client regardless of interest management distance, set global: true:
entity: Scoreboardglobal: true
vars: score_a: { tag: 1, type: int32 } score_b: { tag: 2, type: int32 }Global entities still have a position, but it is not used for FOI visibility checks. When interest management is not enabled, global has no effect—every entity reaches every client anyway.
Excluding from snapshots (persistent)
Section titled “Excluding from snapshots (persistent)”By default every entity type is included in snapshots. Set persistent: false on types that should be left out—short-lived projectiles, session-specific markers, enemies that respawn from static data:
entity: Projectilepersistent: false
vars: speed: { tag: 1, type: float, sync: tick }After golem-bake, the generated type returns false from IsPersistent(). The snapshot system checks this automatically. Omitting persistent defaults to true.
After golem-bake, go-server emits a Go struct named SyncedPlayer (for an entity named Player) with typed Get and Set methods for every field you declared—Health(), SetHealth(v float64), DisplayName(), SetDisplayName(s string), plus the implicit position methods. In 2D code that is usually Position() / SetPosition(x, y). In 3D code, use Position3D() / SetPosition3D(x, y, z) when height matters.
Sync modes (sync)
Section titled “Sync modes (sync)”| Value | Behavior |
|---|---|
tick (default) | Field is tracked every tick for changes and included in delta serialization when dirty. |
once | Skips the per-tick dirty bitmask; templates treat these fields as full snapshot / spawn-time data. |
Omitting sync means tick.
Field types (type)
Section titled “Field types (type)”Values use the Protocol Buffers scalar vocabulary (bake maps them straight into the generated serializers). Supported examples include:
double, float, int32, int64, uint32, uint64, sint32, sint64, bool, string, bytes
Unknown types fail at bake time. The same names appear in command schemas.
Collection fields
Section titled “Collection fields”Entity vars can also hold lists or dictionaries using the following syntax:
| Type expression | Go type | TypeScript type |
|---|---|---|
list<int32> | []int32 | number[] |
list<Item> | []Item | Item[] |
dict<string, int32> | map[string]int32 | Record<string, number> |
dict<string, Item> | map[string]Item | Record<string, Item> |
T in list<T> and V in dict<K,V> can be any proto scalar or a custom type you define under types_schema in golem.yaml (default schemas/types/). K must be a proto scalar. See Custom types & collections for how to define custom types and the delta semantics for collections.
- At least one entity schema must exist; an empty
entity_schemadirectory is an error. - Every variable must have a
taginteger ≥ 1, unique within its entity. Missing or duplicate tags are a bake error. - The names
pos_xandpos_yare reserved for implicit position fields. In 3D projects,pos_zis reserved too. Using reserved names invarsis a bake error. list<T>anddict<K,V>fields can reference any proto scalar or a custom type defined in your types directory (defaultschemas/types/). Referencing an unknown custom type is a bake error.
After edits, run golem-bake to regenerate outputs.