Skip to main content
Your game's model

Not translated yet — showing English.

Entity presets

A preset is a named bundle of entity aspects — data, states, RPC, events, hooks — packaged for one game case. You apply a preset, tune its numbers, or derive your own. Applying one adds aspects to your type; it does not put your type underneath anything — a preset is not a module and has nothing of its own to inherit from. Stats, abilities, projectiles, drop tables and world objects are five presets, not five subsystems: the same declaration, the same sync, the same hook order.

When to use it

  • A thing in your game carries numbers that clamp, regenerate, and fire a transition at their bounds.
  • An action needs cost, cooldown, phases and effects, reachable from one client verb.
  • Something goes in flight, and its hit must be judged fairly for a lagged shooter.
  • Loot must come from weighted odds that replay exactly when a player disputes a drop.
  • The map has furniture — doors, buttons, traps, destructibles — with states that must survive a mid-round join.
  • Skip presets when an entity is plain synced data. Declare the fields and stop.

Who does what

ActorOn this page
schema-authordeclares stats, abilities, projectiles, drop tables, world objects
room-ownertunes preset numbers, rolls drop tables, creates world objects
playercasts abilities, fires shots, picks up loot, interacts with objects

At a glance

Derive Crate from two shipped presets, then create one per line and tune it to 250 HP
// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }

// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);
// derived once, in the schema
@Entity('crate', { persistence: Persistence.Runtime, presets: [Preset.WorldObjects, Preset.Stats] })
export class Crate { @Stat({ max: 100, atMin: 'broken' }) hp: Stat; }

// then one line per crate, on the room host
const crate = await room.create<Crate>({ at: pos, tune: (c) => { c.hp.max = 250; } });
# derived once, in the schema
@entity("crate", persistence=Persistence.RUNTIME, presets=[Preset.WORLD_OBJECTS, Preset.STATS])
class Crate:
    hp = stat(max=100, at_min="broken")

# then one line per crate, on the room host
crate = await room.create(Crate, at=pos, tune={"hp.max": 250})
Coming soon — Go

Attribute-style declaration in Go is still open (O-1) — the samples land when the carrier is decided.

// declarations compile into the same pushed model — playserv push from the UE project or CI
UCLASS(PSEntity = (Name = "crate", Persistence = "Runtime", Presets = "world-objects, stats"))
class UCrate : public UObject
{
    GENERATED_BODY()

    UPROPERTY(PSStat = (Max = 100, AtMin = "broken")) FPSStat Hp;
};

// then one line per crate, on the room host (dedicated server / master-client)
Room->Entities->Of<UCrate>()->Create(FPSIdempotencyKey(CrateId),
    [SpawnPosition](UCrate& Crate) { Crate.Position = SpawnPosition; });   // Position — from the world-objects preset
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — the same C# declaration; creating is a room-host surface, and a Unity client sees the crate arrive
// derived once, in the schema
[Entity("crate", Persistence = Persistence.Runtime, Presets = new[] { Preset.WorldObjects, Preset.Stats })]
public class Crate { [Stat(Max = 100, AtMin = "broken")] public Stat Hp; }

// then one line per crate, on the room host
var crate = await room.Create<Crate>(at: pos, tune: c => c.Hp.Max = 250);

The model

A preset introduces no new notions. Everything it adds is expressible by the means entity already gives — aspects, machines, hooks, persistence.

What each preset gives, and where it is tuned.

PresetWhat the contract gives itWhere it is tuned
statsan aspect of numeric characteristics with bounds, regeneration and modifiers, plus a hook on reaching a threshold — 0 HP becomes a machine transition rather than an if in your codethe field's declaration; the numbers stay live-editable in the panel
abilitiesan aspect of a set of abilities, a machine of application phases, and cost and cooldownthe ability's declaration
projectilesa type with runtime persistence, a ballistics aspect, and a hit eventthe projectile's declaration — one attribute swap changes the flight model
dropsan aspect of a drop table with weights, and a hook after deaththe table's entries and weights
world objectsa state machine of an interactive object, and an aspect of the interaction conditionthe preset's declaration, or per instance at creation
inventoryan owned type with a ref to a catalog item, a stack aspect with an increment, and a per-owner cap with declared overflowthe type's declaration

Inventory also has a page of its own.

What is true of every preset.

AlwaysWhat it is
where it sitson an entity, as aspects: its data syncs like any other data, its states are entity states, its RPCs are entity RPCs, and its hooks run in entity hook order
tuningis live config rather than a redeploy, which is why the panel shows a stat bound, a cooldown and a drop weight in one tree
deriving your ownis composition, not subclassing: Crate is world-objects plus stats, and the derived thing is still aspects on an entity

Errors

  • Declaring a stat, an ability, a projectile, a drop table or a world object is a schema act — fn or adm. A player or client key attempting one gets forbidden, and nothing is declared or half-declared. That is a different refusal from the ones a player meets inside a call they were allowed to make — on cooldown, cannot pay, a missing item:key.bronze — each of which carries its own code.

The rest of a preset's refusals are entity's. Two are specific to presets:

  • A partially filled preset is a validation failure at deploy. A preset carries a coherent set: half a declaration is refused before it ships rather than behaving oddly in a match.
  • A preset cannot be marked with a property its own mechanics contradict — an aspect the client has no rules for cannot be declared predictable, and that too is caught at deploy.

Limits

The ceilings are entity's — aspects per type, machines per type, the stored instance's size, the rate of changes to one instance. The one a preset declares itself is the per-owner cap an owned preset carries, with one of three boundary behaviours and no default: refuse · redirect into a declared owner bucket · discard with event. The numbers arrive with the platform limits chapter.