Accessor Roles¶
Every WorldAccessor carries an AccessorRole that controls which components it can read/write, whether it can make structural changes, whether it can allocate heap, and which RNG stream it can pull from. The role is set at creation and never changes.
System-owned accessors derive their role from their SystemPhase (see System-owned accessors below). Non-system code creates accessors via world.CreateAccessor(AccessorRole) and picks the role explicitly.
Every rule below is asserted at the call site. Crossing a role boundary throws an immediate TrecsException rather than producing silent desync later.
The three roles¶
Fixed— owns the deterministic simulation. Reads and writes simulation state and allocates persistent heap. Render-only state ([VariableUpdateOnly]) is off-limits. Default for[ExecuteIn(SystemPhase.Fixed)]systems — which is the implicit default for anyISystem.Variable— drives presentation. Reads simulation state and reads/writes the[VariableUpdateOnly]render state. Cannot mutate simulation state or the heap. Default for the three presentation phases (EarlyPresentation,Presentation,LatePresentation) and for input systems (input systems get extra permissions — see Input System).Unrestricted— escape hatch for non-system code (lifecycle hooks, event callbacks, networking, debug tooling, scripting bridges). Bypasses all role rules.
Capability matrix¶
| Capability | Fixed |
Variable |
Unrestricted |
|---|---|---|---|
Read sim component (non-[VariableUpdateOnly]) |
✅ | ✅ | ✅ |
Write sim component (non-[VariableUpdateOnly]) |
✅ | ❌ | ✅ |
Read [VariableUpdateOnly] component |
❌ | ✅ | ✅ |
Write [VariableUpdateOnly] component |
❌ | ✅ | ✅ |
Heap mutation (Alloc, Write, Set, Clone, Acquire, Dispose, EnsureCapacity) |
✅ | ❌ | ✅ |
Structural change (AddEntity / RemoveEntity / SetTag / UnsetTag) on a non-VUO template |
✅ | ❌ | ✅ |
Structural change on a [VariableUpdateOnly] template |
❌ | ✅ | ✅ |
Read set (Set<T>().Read — Contains, Count, iterate) |
✅ | ✅ | ✅ |
Mutate set (Set<T>().DeferredAdd / DeferredRemove / DeferredClear, Set<T>().Write) |
✅ | ❌ | ✅ |
SetSystemPaused |
✅ | ❌ | ✅ |
FixedRng |
✅ | ❌ | ✅ |
VariableRng |
❌ | ✅ | ✅ |
VUO field vs VUO template¶
[VariableUpdateOnly] applies at two scopes that behave differently. The distinction matters for the structural-change rows above.
-
[VariableUpdateOnly]on a component field — the component is render-only state.Fixedcannot read or write it;Variable/ input /Unrestrictedcan. The structural-change rule is unaffected — entities of the parent template are still simulation state, soFixedandUnrestrictedcreate / remove / partition-transition them. -
[VariableUpdateOnly]on a template class — the entire template is render-cadence state (cameras, view-only helpers). The structural-change rule inverts:Fixedis rejected;Variable/ input /Unrestrictedcreate / remove / partition-transition them. These groups are skipped from the determinism checksum.
See the Components attribute reference for field-level usage.
Picking a role for a standalone accessor¶
Non-system code (services, scene initializers, editor inspectors, tests) creates its own accessor via World.CreateAccessor(AccessorRole role) (called on the Trecs World instance). Pick the role that matches the work:
Fixed— Service classes for the deterministic simulation. For example, a stats service that subscribes toOnAdded/OnRemovedfor a tag and bumps a global score component — see Sample 15 — Reactive Events.Variable— UI, camera controllers, rendering services that read both sim and render state.Unrestricted— Scene initialization, debug menus, editor tooling.
System-owned accessors vs standalone accessors¶
System-owned accessors map their SystemPhase to a role automatically:
SystemPhase |
AccessorRole |
|---|---|
Fixed |
Fixed |
Input / EarlyPresentation / Presentation / LatePresentation |
Variable |
The presentation and input phases collapse into the single Variable role because they share the same access rules — only their execution-order positions differ.
System code never calls World.CreateAccessor(...) — it gets the role from its [ExecuteIn(...)] attribute. Use CreateAccessor only for the standalone cases listed above.
Related¶
- Shared Heap Data — the heap-specific subset of these rules, plus deterministic ID minting and the seeder / provider patterns for shared blobs.
- Input System — how
[Input]components andAddInput<T>work with input systems. - Time & RNG —
FixedRngvsVariableRngdeterministic streams. - Pausing & Disabling Systems — when to use
SetSystemPausedvsSetSystemEnabled.