Store

Central state management system utilizing the Twin-Proxy Vault for safe, high-performance synchronization between React UI and 3D simulation.
Published: 7/10/2026

Twin-Proxy Vault Architecture#

Aircada uses a Twin-Proxy Vault to separate UI and Engine state updates, preventing race conditions and protecting performance.

ReactStoreAPI: Optimistic and immediate updates for a responsive UI.

EngineStoreAPI: Deferred and batched updates to protect high-frequency physics and logic loops.

Engine Operator Guidelines#

How to handle state within Engine Operators using @aircada/spec.

Bound State (Local Ownership): Use @Property({ store: "path.to.key" }) to bind a class variable. Mutate directly (e.g., this.brightness += 1).

Recommendation: Prefer using typed property decorators like @FloatProperty({ store: "path" }) or @StringProperty({ store: "path" }) for better Studio integration and type safety.

Unbound State (Global Mutations): Use this.air.store.patch() or this.air.store.patchByPath() for headless mutations.

State Listeners: Use @Input({ onStore: "path.to.key" }) to react to store changes.

warning
Do NOT use patch() for properties bound via @Property, as it causes a 1-frame desync.
StateController.ts (typescript)
import { Operator, Context, FloatProperty, Input, AircadaContext } from "@aircada/spec";

@Operator({ name: "State Controller", type: "STATE_CONTROLLER" })
export class StateController {
    @Context() air!: AircadaContext;

    // 1. BOUND STATE: Automatically syncs this.brightness with vault path
    @FloatProperty({ store: "app.settings.brightness" })
    brightness: number = 100;

    // 2. STATE LISTENER: Triggered when the store value changes
    @Input({ onStore: "app.settings.brightness" })
    onBrightnessChange(newVal: number) {
        console.log("Brightness updated in Vault:", newVal);
    }

    // 3. UNBOUND STATE: Imperatively patching other parts of the global store
    @Input({ name: "Reset System" })
    reset() {
        this.air.store.patch({ app: { status: "ready" } });
    }
}
Engine operator using @Property binding, state listener, and imperatively patching global store.

The Store Interface#

Defining the global store structure using the @Store decorator.

All interactions should be typed against the global store interface, typically located in src/store.config.ts.

Use normalized collections (Dictionaries) instead of arrays for scalable game state.

Collection & Array Scaling (GC Protocol)#

Optimization strategies for handling large data collections and arrays in the Vault.

The Vault treats Arrays as singular primitive values. To update an array, a new reference must be passed, which incurs high Garbage Collection (GC) costs.

Scaling Protocol: Use Dictionaries (Record<string, Data>) for normalized state to allow surgical, zero-waste deep-patching.

warning
STRICT PROHIBITION: NEVER patch arrays by index. This permanently converts the Array to a JSON Object.