# API Surface: OECS This document describes the public API surface of the OECS library. It serves as a contract for implementation and a reference for consumers. All types reside in the `OECS` namespace unless otherwise noted. --- ## Entity ```csharp namespace OECS; public readonly struct Entity : IEquatable { public static Entity Null { get; } // ID=0, Version=0 public uint Id { get; } // lower 24-bit identifier public uint Version { get; } // upper 8-bit generation public bool IsNull { get; } // true for Entity.Null public bool Equals(Entity other); public override bool Equals(object? obj); public override int GetHashCode(); public override string ToString(); // "Entity(42:v3)" public static bool operator ==(Entity left, Entity right); public static bool operator !=(Entity left, Entity right); } ``` --- ## World ```csharp namespace OECS; public class World : IDisposable { // --- Singleton entity --- public static Entity SingletonEntity { get; } // ID=1, Version=1 // --- Lifecycle --- public World(); // --- Entity Management --- public Entity CreateEntity(); public void DestroyEntity(Entity entity); public bool IsAlive(Entity entity); // --- Component Management --- public void AddComponent(Entity entity, T component) where T : struct; public void RemoveComponent(Entity entity) where T : struct; public ref T GetComponent(Entity entity) where T : struct; public T ReadComponent(Entity entity) where T : struct; public bool TryGetComponent(Entity entity, out T value) where T : struct; public bool HasComponent(Entity entity) where T : struct; // --- Singleton --- public void SetSingleton(T component) where T : struct; public ref T GetSingleton() where T : struct; public T ReadSingleton() where T : struct; public bool HasSingleton() where T : struct; public void RemoveSingleton() where T : struct; // --- Queries --- public QueryBuilder Query(); // --- Query Execution --- public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct; // Overloads for 2–6 component types: public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct; public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct where T3 : struct; public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct where T3 : struct where T4 : struct; public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct; public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct; // --- Commands --- public CommandQueue Commands { get; } public void ExecuteCommands(); // --- Reactivity --- public void MarkModified(Entity entity) where T : struct; public void PostChanges(); public Observable ObserveEntityChanges(); public Observable ObserveComponentChanges() where T : struct; public Observable ObserveQuery(QueryDescriptor query); // --- Relationships --- public IReadOnlyCollection GetSources(Entity target) where T : struct, IRelationship; // --- Cleanup --- public void Dispose(); } ``` --- ## ForEachAction Delegates Custom delegate types for query iteration with `ref` parameters. The built-in `Action<...>` delegates do not support `ref` parameters. ```csharp namespace OECS; public delegate void ForEachAction(Entity entity, ref T1 c1) where T1 : struct; public delegate void ForEachAction(Entity entity, ref T1 c1, ref T2 c2) where T1 : struct where T2 : struct; public delegate void ForEachAction(Entity entity, ref T1 c1, ref T2 c2, ref T3 c3) where T1 : struct where T2 : struct where T3 : struct; public delegate void ForEachAction(Entity entity, ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4) where T1 : struct where T2 : struct where T3 : struct where T4 : struct; public delegate void ForEachAction(Entity entity, ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct; public delegate void ForEachAction(Entity entity, ref T1 c1, ref T2 c2, ref T3 c3, ref T4 c4, ref T5 c5, ref T6 c6) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct; ``` --- ## EntityIterator Extension methods on `World` for `foreach`-style iteration over queries. Returns `ref struct` iterators that support zero-allocation iteration with `ref` access to components. Supports 1–3 component types. ```csharp namespace OECS; public static class EntityIterator { public static Select1 Select( this World world, QueryDescriptor query) where T1 : struct; public static Select2 Select( this World world, QueryDescriptor query) where T1 : struct where T2 : struct; public static Select3 Select( this World world, QueryDescriptor query) where T1 : struct where T2 : struct where T3 : struct; } ``` Usage: ```csharp using var iter = world.Select(query); foreach (ref var item in iter) { item.Current1.X += item.Current2.X * dt; } ``` --- ## QueryBuilder ```csharp namespace OECS; public class QueryBuilder { public QueryBuilder With() where T : struct; public QueryBuilder Without() where T : struct; public QueryDescriptor Build(); } ``` --- ## QueryDescriptor ```csharp namespace OECS; public class QueryDescriptor { public IReadOnlySet With { get; } public IReadOnlySet Without { get; } } ``` --- ## ISystem ```csharp namespace OECS; public interface ISystem { QueryDescriptor Query { get; } void Run(World world); } ``` --- ## ITickedSystem ```csharp namespace OECS; public interface ITickedSystem : ISystem { void Run(World world, Tick tick); } ``` --- ## Tick ```csharp namespace OECS; public readonly struct Tick { public TickType Type { get; } public float DeltaTime { get; } public static Tick Timed(float deltaTime); public static Tick Logical(); } public enum TickType { Timed, Logical } ``` --- ## SystemGroup ```csharp namespace OECS; public class SystemGroup { public SystemGroup(World world); public void Add(ISystem system); public void Remove(ISystem system); public void RunTimed(float deltaTime); public void RunLogical(); public int Count { get; } } ``` --- ## ICommand ```csharp namespace OECS; public interface ICommand { void Execute(World world); } ``` --- ## CommandQueue ```csharp namespace OECS; public class CommandQueue { public void Enqueue(T command) where T : struct, ICommand; public void ExecuteAll(World world); public void Clear(); public void ClearErrors(); public int Count { get; } public IReadOnlyList Errors { get; } } ``` --- ## IRelationship ```csharp namespace OECS; public interface IRelationship { Entity Source { get; } Entity Target { get; } } ``` --- ## Relationship\ A convenience base struct for relationship components. The type parameters are phantom types that differentiate relationship kinds at the type level, enabling type-safe queries and reverse lookups. ```csharp namespace OECS; [MessagePackObject] public struct Relationship : IRelationship { [Key(0)] public Entity Source { get; set; } [Key(1)] public Entity Target { get; set; } } ``` --- ## EntityChange ```csharp namespace OECS; public readonly struct EntityChange : IEquatable { public Entity Entity { get; } public ChangeKind Kind { get; } public Type? ComponentType { get; } // null for entity-level changes } public enum ChangeKind { EntityAdded, EntityRemoved, ComponentAdded, ComponentRemoved, ComponentModified } ``` --- ## WorldSerializer Saves and loads a `World` to/from a MessagePack stream. Components are serialized by their runtime type. ```csharp namespace OECS; public static class WorldSerializer { public static void Save(World world, Stream stream); public static void Load(World world, Stream stream); } ``` --- ## WorldSnapshot / EntitySnapshot / ComponentEntry Serialization types used by `WorldSerializer`. These are public but intended primarily for serialization infrastructure. ```csharp namespace OECS; [MessagePackObject] public class WorldSnapshot { [Key(0)] public EntitySnapshot[] Entities { get; set; } } [MessagePackObject] public class EntitySnapshot { [Key(0)] public uint Id { get; set; } [Key(1)] public uint Version { get; set; } [Key(2)] public ComponentEntry[] Components { get; set; } public Entity Entity { get; } } [MessagePackObject] public class ComponentEntry { [Key(0)] public string TypeName { get; set; } [Key(1)] public byte[] Data { get; set; } } ``` --- ## Usage Example ```csharp using OECS; using R3; // Define components [MessagePackObject] public struct Position : IMessagePackSerializationCallbackReceiver { [Key(0)] public float X; [Key(1)] public float Y; public void OnBeforeSerialize() { } public void OnAfterDeserialize() { } } [MessagePackObject] public struct Velocity : IMessagePackSerializationCallbackReceiver { [Key(0)] public float X; [Key(1)] public float Y; public void OnBeforeSerialize() { } public void OnAfterDeserialize() { } } // Define a system public class MovementSystem : ITickedSystem { public QueryDescriptor Query { get; } public MovementSystem(World world) { Query = world.Query() .With() .With() .Build(); } public void Run(World world) => Run(world, Tick.Logical()); public void Run(World world, Tick tick) { float dt = tick.DeltaTime; world.ForEach(Query, (Entity entity, ref Position pos, ref Velocity vel) => { pos.X += vel.X * dt; pos.Y += vel.Y * dt; world.MarkModified(entity); }); } } // Wire it up var world = new World(); var group = new SystemGroup(world); group.Add(new MovementSystem(world)); // Observe changes world.ObserveComponentChanges() .Subscribe(change => Console.WriteLine($"{change.Entity} moved")) .AddTo(disposables); // Create entities var player = world.CreateEntity(); world.AddComponent(player, new Position { X = 0, Y = 0 }); world.AddComponent(player, new Velocity { X = 1, Y = 0 }); // Run a tick group.RunTimed(0.016f); // ~60 FPS ``` --- ## Internal Types (not part of public API) These types are implementation details and may change without notice: | Type | Purpose | |---|---| | `SparseSet` | Dense/sparse array pair for component storage. | | `ComponentStore` | Registry of `SparseSet` instances by type. | | `ChangeBuffer` | Accumulates `EntityChange` during system run, posts to R3 subjects. | | `ChangeSet` | Deduplicated change accumulator within a single batch. | | `RelationshipIndex` | Reverse lookup from target entity to source entities. | | `EntityAllocator` | Free-list + bump allocator for entity IDs. | | `QueryExecutor` | Query iteration logic with smallest-set driver optimization. |