Struct EntityCommands
A parallel system's handle on its own worker slot of the tick's entity-command buffer (#1099). Queue a spawn or a destroy from inside a parallel chunk, without a transaction; the engine applies the buffer in one fence phase.
public ref struct EntityCommands
- Inherited Members
Remarks
The id you get back is real and final. Spawn<TArch>(params ReadOnlySpan<ComponentValue>) returns the EntityId the entity will have, assigned from this producer's key block, so it can be written straight into another entity's component with no remap pass anywhere in the engine — the corpse-and-loot case, which placeholder schemes reach only through a playback remap that has to cover ids embedded in recorded payloads. The link also survives the tick boundary, which a placeholder does not.
But the entity is not alive yet. Until the apply phase runs, the id is valid and permanently reserved while the entity is not alive, not
openable and not query-visible. None of those answers throws — they answer false or empty. See the validity contract on
Spawn<TArch>(params ReadOnlySpan<ComponentValue>).
Stack-only by construction. A ref struct cannot be captured into a lambda, boxed or stored on the heap, so a handle cannot outlive its
system body or be smuggled onto another thread — the compiler enforces the slot-disjointness invariant the no-atomics design depends on.
Obtain one from ctx.Commands, which supplies the caller's worker slot and chunk index.
Properties
IsValid
False for a default handle — a lifecycle hook, or a runtime with no database engine. Every method on an invalid handle is a no-op.
public readonly bool IsValid { get; }
Property Value
Pending
Commands accepted into this slot so far this tick. Zero for an invalid handle.
public readonly int Pending { get; }
Property Value
Methods
Destroy(EntityId)
Queues one destroy.
public bool Destroy(EntityId id)
Parameters
Returns
- bool
trueif queued;falseif the slot is at its ceiling or the id was null.
Remarks
Idempotent at apply: a second command for the same id, from this or any other slot, is a no-op. Destroying an entity spawned earlier in the same tick works — spawns apply before destroys — so the spawn-then-destroy pair collapses to nothing observable rather than to a dangling row.
SpawnMany<TArch>(int, scoped Span<EntityId>, params ReadOnlySpan<ComponentValue>)
Queues count spawns of TArch sharing one value set — one header and one payload copy for the whole run —
and writes their ids into ids.
public int SpawnMany<TArch>(int count, scoped Span<EntityId> ids, params ReadOnlySpan<ComponentValue> values) where TArch : Archetype<TArch>
Parameters
countintEntities to create. Must be positive and no larger than one key block; a larger request is refused.
idsSpan<EntityId>Destination for the new ids. Must hold at least
count; a shorter span refuses the whole command.valuesReadOnlySpan<ComponentValue>Initial component values, shared by every entity in the run.
Returns
- int
The number queued —
count, or 0 if nothing was. Never partial: a run is queued whole or not at all.
Type Parameters
TArchThe archetype to spawn.
Remarks
Returns a count and fills a caller span rather than returning a range, because a range would be a lie in the general case: ids are contiguous within one key block, and the block boundary is not something a caller can see. The run itself IS contiguous — a request that would straddle a boundary takes a fresh block rather than splitting — but that is an implementation guarantee, not a shape the API should bake in.
Spawn<TArch>(params ReadOnlySpan<ComponentValue>)
Queues one spawn of TArch and returns the new entity's final id.
public EntityId Spawn<TArch>(params ReadOnlySpan<ComponentValue> values) where TArch : Archetype<TArch>
Parameters
valuesReadOnlySpan<ComponentValue>Initial component values. Components not covered are zero-initialised and disabled, as with
Transaction.Spawn.
Returns
- EntityId
The entity's final id, or Null when the command was not queued — this slot is at its ceiling, the archetype is not registered, the realm the values name cannot hold it, or more than 255 values were supplied. Never throws: this runs inside parallel chunks, where an exception becomes a system failure and, under a strict tick-abort policy, can cancel the rest of the tick.
Type Parameters
TArchThe archetype to spawn. Must be registered with the database and initialised.
Remarks
A null id is a lost game action, and an application has to treat it as one — do not decrement the loot budget, do not mark the corpse
looted. Nothing else is required: no retry, no backoff. The idiomatic shape is to check IsNull on the value you were going
to store anyway. Even the ignored case degrades safely: an Null written into a component is what IsAlive already
answers false for, so a discarded failure becomes an unowned loot drop rather than a dangling reference.
The validity contract. Between this call and the apply, the returned id is final and permanently reserved; the entity is not alive, cannot be opened, and does not appear in any query or spatial result; and none of those operations throws.