Skip to content

Core engine

Extension API. Supported engine primitives for custom execution and world lifecycle extensions.

Archetype

Archetype(components)

Convenience handler for archetype operations

sig_from_components staticmethod

sig_from_components(components)

Generate an archetype signature from a list of component instances. Returns a tuple of component types sorted by name for consistent signatures.

projection_columns staticmethod

projection_columns(components)

Canonical schema column list for a component projection (spec §165).

Projection is type-level — only the schema matters — so this accepts component types directly. Instances are accepted for back-compat and normalized to their types.

Commit-identity columns are excluded: they are storage metadata the read layer filters on, not part of the user-facing shape — and legacy epoch-0 tables do not have them, so projections must not ask.

add_components staticmethod

add_components(sig, component_types)

Generate a new archetype signature by adding components to an existing signature.

remove_components staticmethod

remove_components(sig, component_types)

Generate a new archetype signature by removing components from an existing signature.

get_name staticmethod

get_name(sig)

Generate a compact, filesystem-safe name for an archetype. We avoid extremely long identifiers by using only a short descriptor and a schema hash. This ensures stable uniqueness without exceeding path limits.

The hash covers the FULL storage schema, commit columns included — schema-is-identity is what turns the commit-identity amendment into new-generation table ids instead of in-place evolution.

get_legacy_name staticmethod

get_legacy_name(sig)

The v0.2 table id for a signature (base schema without commit columns).

Read-only fallback: stores resolve this name when the current-generation table does not exist, so pre-#273 history stays queryable. Nothing ever writes to a legacy table id.

get_archetype_schema staticmethod

get_archetype_schema(sig)

Get the schema for an archetype from a list of component types. Combines the base archetype schema with prefixed component schemas.

get_legacy_schema staticmethod

get_legacy_schema(sig)

The v0.2 storage schema for a signature (no commit columns).

to_row_dict staticmethod

to_row_dict(entity_id, tick, components, world_id, run_id)

Convert entity components to a row dictionary for archetype storage.

Parameters:

Name Type Description Default
entity_id int

The unique entity identifier

required
tick int

The simulation tick

required
components list[Component]

List of component instances

required
world_id str

The world identifier

required
run_id str

The run identifier

required

Returns:

Type Description
dict[str, Any]

Dict containing the row data for this entity

ArchetypeSignature module-attribute

ArchetypeSignature = tuple[type['Component'], ...]

AsyncWorld

AsyncWorld(
    *,
    world_id,
    name,
    querier,
    updater,
    system,
    resources,
    hooks,
    run_id=None,
    tick=0,
    next_entity_id=1,
    entity2sig=None,
    spawn_cache=None,
    despawn_cache=None,
    lineage=None,
    commit_coordinator=None,
)

Initialize the fully parallel async world.

active_signatures property

active_signatures

Get the union of all archetypes that need processing this tick.

run async

run(run_config, **input_kwargs)

Runs the world for the given run configuration.

step async

step(run_config, **input_kwargs)

Executes one full, parallel simulation tick.

Tick Lifecycle
  1. pre_tick hook fires
  2. For each archetype (parallel): a. Query previous state (tick N-1) b. Materialize mutations (spawn/despawn caches) c. Execute processors (priority order) d. Persist to store
  3. Increment tick
  4. post_tick hook fires (tick is now N+1)

Note: Messages enqueued in tick N are dequeued in tick N+1.

materialize_mutations

materialize_mutations(df, sig)

Apply pending despawns and concat pending spawns onto df, consuming both caches.

Diagnostic/compatibility surface. The step path does NOT use this combined form: it applies despawns before processors, concats spawns after them (initial conditions), and consumes the caches only after the append commits (see _compute_archetype/_commit_archetype).

create_entity async

create_entity(components)

Spawn a new entity with an auto-assigned id. Fires OnSpawn.

create_entities async

create_entities(entities)

Spawn multiple entities in one batch. Fires one OnSpawn per entity.

All spawn rows are appended in a single cache operation per archetype signature, preserving the initial-conditions contract: every entity's first row is its raw spawn values at the materialization tick; processors first apply on the following tick (x_0 is given, x_{t+1} = f(x_t)).

Parameters:

Name Type Description Default
entities list[list[Component]]

A list of component lists, one per entity to spawn.

required

Returns:

Type Description
list[int]

A list of entity IDs in the same order as entities.

reserve_entity_ids

reserve_entity_ids(n)

Reserve n entity IDs without spawning.

The returned IDs are guaranteed never to collide with auto-assigned ones: they are drawn from the same monotonic counter as create_entity / create_entities, so interleaved calls produce disjoint ranges.

Reserved IDs must be materialised via spawn_with_reserved_id before the next step() that processes their archetype, otherwise they remain invisible (no spawn_cache row, no entity2sig entry).

Use this when external systems need to reference an entity by ID before the spawn lands on the ledger (e.g. to break a circular dependency between two entities that reference each other).

Parameters:

Name Type Description Default
n int

Number of IDs to reserve (must be >= 1).

required

Returns:

Type Description
list[int]

A sorted list of n reserved entity IDs.

Raises:

Type Description
ValueError

If n is less than 1.

spawn_with_reserved_id async

spawn_with_reserved_id(entity_id, components)

Materialise a previously reserved entity ID.

Registers the entity in entity2sig and spawn_cache exactly as create_entity would, then fires OnSpawn. The entity will appear as a raw spawn row at the next materialization tick.

Cancellation contract: if you want to cancel a reserved spawn before it materialises, call remove_entity(entity_id) — it will find the pending spawn_cache row and remove it cleanly, matching the semantics of cancelling a same-tick spawn from create_entity.

Parameters:

Name Type Description Default
entity_id int

A previously reserved ID (from reserve_entity_ids).

required
components list[Component]

Initial component values.

required

Raises:

Type Description
ValueError

If entity_id is already registered (double-spawn guard — prevents silent overwrites of live entities).

remove_entity async

remove_entity(entity_id)

Despawn an entity. Cancels a pending same-tick spawn if present, otherwise queues a despawn row for the current tick. Fires OnDespawn iff the entity existed.

update_entity async

update_entity(entity_id, components)

Overlay component values on an existing entity without changing its archetype.

The entity keeps its current signature. Only the supplied component fields are overwritten. Used for value mutations (e.g., Position.x += 1) as distinct from add_components which extends the signature.

add_components async

add_components(entity_id, components)

Attach additional components to an existing entity. Fires OnComponentAdded iff the signature actually changes.

remove_components async

remove_components(entity_id, component_types)

Detach components from an existing entity. Fires OnComponentRemoved iff the signature actually changes.

query_archetype async

query_archetype(
    sig,
    run_config_or_ticks=None,
    *,
    ticks=None,
    entity_ids=None,
    components=None,
    run_id=None,
    run_config=None,
)

Facade Method to query an archetype table by signature.

The signature is canonicalized (sorted by class name) before lookup, so callers may supply types in any order. If the canonical signature has never been active in this world (not in the live registry AND not in the store's durable record) an UnknownSignatureError is raised — a distinct failure from 'the signature exists but has zero rows at this tick' which returns an empty DataFrame.

Defaults to the world's most recent run_id when not provided. Accepts an optional run_config for instrumentation; ignored by the base querier.

get_components async

get_components(components, entity_ids=None)

Query all active entities that contain the requested component types.

Passthrough to the querier's query_components method.

execute async

execute(df, sig, **input_kwargs)

Execute system processors, passing resources for type-safe dependency injection.

update async

update(df, sig, run_config, tick=None)

Update the store with the given archetypes.

When coordinated persistence is active, the update carries the tick's commit identity. Capability is determined before writing so an error cannot cause the same rows to be appended twice.

add_hook

add_hook(event_type, fn, *, mode='blocking')

Register a handler for lifecycle events.

Parameters:

Name Type Description Default
event_type type[_HookEventT]

Event dataclass to listen for: PreTick, PostTick, OnSpawn, OnDespawn, OnComponentAdded, or OnComponentRemoved.

required
fn AsyncHookHandler[_HookEventT]

Async callable that accepts the matching event instance.

required
mode FireMode

"blocking" awaits the handler inline. "spawn" schedules it with asyncio.create_task so the tick does not wait.

'blocking'

Returns:

Type Description
HookHandle

HookHandle to pass back to remove_hook when the handler should be

HookHandle

unregistered.

Example

from archetype.core.hooks import PostTick

async def log_tick(event: PostTick) -> None: print(f"tick {event.tick} done, {len(event.results)} archetypes")

handle = world.add_hook(PostTick, log_tick)

... later ...

world.remove_hook(handle)

remove_hook

remove_hook(handle)

Unregister a hook by handle.

The operation is idempotent. Passing a handle that was already removed, or a handle minted by another world, is a no-op.

AsyncSystem

AsyncSystem()

Async version of SyncSystem that processes archetypes concurrently.

Key innovation: Each archetype is processed through all its relevant processors concurrently with other archetypes, while maintaining priority-based ordering within each archetype.

This provides the same semantic guarantees as SyncSystem but with per-archetype parallelism.

add_processor async

add_processor(proc)

Add an async processor to the system.

remove_processor async

remove_processor(proc_type)

Remove all processors of the given type.

execute async

execute(
    df, sig, resources=None, debug=False, **input_kwargs
)

Process a single archetype through all relevant processors.

This is where the concurrency happens - each archetype gets its own task but within each archetype, processors run in priority order (same as sync).

Parameters:

Name Type Description Default
df DataFrame

The DataFrame to process

required
sig ArchetypeSignature

The archetype signature

required
resources Resources | None

Type-safe resource container (available as kwarg to processors)

None
debug bool

Enable debug logging for processor execution

False
**input_kwargs

Additional kwargs passed to processors

{}

AsyncQueryManager

AsyncQueryManager(store)

get_archetype async

get_archetype(sig, world_id, run_id)

Get all archetypes that contain all of the specified component types for provided world_id and run_id.

query_archetype async

query_archetype(
    sig,
    world_id,
    ticks=None,
    entity_ids=None,
    components=None,
    run_id=None,
    commit_tokens=None,
    **kwargs,
)

Query active entities for the provided archetype signature.

The signature is canonicalized (sorted by class name) before table resolution, so callers may supply types in any order and will always find the same table.

Raises UnknownSignatureError when the canonical signature has never been registered in the durable store — distinct from 'the signature exists but has zero rows at this tick', which returns an empty DataFrame.

_world_validated=True may be passed by the world's query_archetype facade after it has already verified the sig against live and store sigs. When set, the querier skips its own redundant check so that sigs that are live (in spawn cache) but not yet committed do not cause a false positive.

query_components async

query_components(
    components,
    world_id,
    run_id,
    *,
    ticks=None,
    entity_ids=None,
    commit_tokens=None,
)

Query all entities that contain the requested component types.

Discovers matching archetype signatures, queries each, projects to the requested component columns, and unions the results.

If a requested component type does not exist in ANY registered archetype, a KeyError is raised naming the missing component so callers can detect the ManipProprio-vs-ManipAction style confusion (querying a component that belongs to a different archetype).

list_signatures async

list_signatures()

Delegate to the underlying store's signature registry.

AsyncUpdateManager

AsyncUpdateManager(store, validate_flag=False)