Skip to content
Sieluna edited this page Aug 17, 2026 · 3 revisions

Sia.NET

Sia is an Entity Component System (ECS) for .NET. Components hold data, entities group components, and systems process matching entities.

This wiki describes the current Sia.NET API, targeting .NET 10. Keep the runtime and source-generator versions aligned.

Core terms

Term Plain-language meaning
World Owns component storage, entities, events, addons, and queries.
Entity A pooled handle to one item in a world.
Component A small value such as position, velocity, or health.
Query Selects entities containing required components.
System Runs behavior over a query.

Minimal example

Reference the Sia project, then run:

using System;

using Sia;

file static class Program
{
    private record struct Position(float X, float Y);
    private record struct Velocity(float X, float Y);

    public static void Main()
    {
        using var world = new World();

        var entity = world.Create(HList.From(
            new Position(0, 0),
            new Velocity(2, 1)));

        world.Query(Matchers.Of<Position, Velocity>())
            .ForSlice(static (ref Position position, ref Velocity velocity) => {
                position.X += velocity.X;
                position.Y += velocity.Y;
            });

        Console.WriteLine(entity.Get<Position>());
        entity.Destroy();
    }
}

The world stores the entity in an archetype containing Position and Velocity. The query selects that archetype and updates both components by reference.

Systems

API Purpose
ISystem, SystemBase Matcher, trigger/filter, lifecycle, execution, and child systems.
SystemChain Immutable system composition and configuration.
SystemDescriptor Set membership and Before/After dependencies.
SystemStage Planned runtime stage created for one world.
Scheduler Dependency-ordered labeled schedules and lifecycle callbacks.
FSystem Functional component callbacks with optional runner/barrier.
var chain = SystemChain.Empty
    .Add<InputSystem>()
    .Add<MovementSystem>()
    .Configure<MovementSystem>(descriptor =>
        descriptor.After<InputSystem>());

using var stage = chain.CreateStage(world);
stage.Tick();
  • A matcher without triggers processes the full query each tick.
  • A trigger union, optionally with a filter union, creates an event-collected reactive system; collected entities are deduplicated and cleared after execution.
  • Planning uses stable dependency ordering. Missing targets create no edge; cycles throw SystemCycleException.
  • The core scheduler pipeline is First -> PreUpdate -> Update -> PostUpdate -> Last; Startup is manual.
  • WithRunner(ParallelRunner.Default) is asynchronous unless followed by WithBarrier().
  • Dispose stages before their world.

Actors

A world mutated or ticked from more than one thread needs a WorldActor: bind it once, and every other thread posts work into its mailbox instead of calling Send/Execute directly.

using var actor = new WorldActor(world);
var ownerThread = Task.Run(() => actor.Run());

actor.PostTickSchedule(scheduler, CoreSchedules.Update);

actor.CompleteAndDispose(TimeSpan.FromSeconds(5));
ownerThread.Wait();

ActorRuntime shares one worker pool across many actors instead of a dedicated thread per world. See Architecture for the mailbox model.

Essential rules

  • Create the complete initial component set with HList.From(...) when possible.
  • Check Contains<T>() before optional Get<T>() access.
  • Do not retain an entity after Destroy() or cache its slot.
  • Do not add, remove, or destroy entities while iterating their host.
  • Pass world explicitly to generated views and commands.
  • Dispose stages and worlds deterministically.
  • Do not mutate a world bound to a WorldActor from any thread but its own; post the work instead.

Read Code Generators for observable component properties, Prelude for optional indexes and hierarchies, Reactive for reducer-driven composition, and Architecture for the compact internal model.

Clone this wiki locally