From 58477aec505eaae2438dd68adb92de3b7283f5b2 Mon Sep 17 00:00:00 2001 From: hypercross Date: Sat, 18 Jul 2026 21:59:09 +0800 Subject: [PATCH] docs: update API surface documentation --- docs/api-surface.md | 181 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 170 insertions(+), 11 deletions(-) diff --git a/docs/api-surface.md b/docs/api-surface.md index 728e424..ea2be7b 100644 --- a/docs/api-surface.md +++ b/docs/api-surface.md @@ -15,8 +15,8 @@ namespace OECS; public readonly struct Entity : IEquatable { public static Entity Null { get; } // ID=0, Version=0 - public uint Id { get; } // 24-bit identifier - public uint Version { get; } // 8-bit generation + 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); @@ -37,6 +37,9 @@ namespace OECS; public class World : IDisposable { + // --- Singleton entity --- + public static Entity SingletonEntity { get; } // ID=1, Version=1 + // --- Lifecycle --- public World(); @@ -49,11 +52,14 @@ public class World : IDisposable 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; @@ -63,12 +69,19 @@ public class World : IDisposable // --- Query Execution --- public void ForEach( QueryDescriptor query, - Action action) where T1 : struct; + ForEachAction action) where T1 : struct; // Overloads for 2–6 component types: - public void ForEach(QueryDescriptor, Action) + public void ForEach(QueryDescriptor, ForEachAction) where T1 : struct where T2 : struct; - // ... up to T6 + 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; } @@ -78,10 +91,10 @@ public class World : IDisposable public void MarkModified(Entity entity) where T : struct; public void PostChanges(); - public IObservable ObserveEntityChanges(); - public IObservable ObserveComponentChanges() + public Observable ObserveEntityChanges(); + public Observable ObserveComponentChanges() where T : struct; - public IObservable ObserveQuery(QueryDescriptor query); + public Observable ObserveQuery(QueryDescriptor query); // --- Relationships --- public IReadOnlyCollection GetSources(Entity target) @@ -94,6 +107,70 @@ public class World : IDisposable --- +## 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 @@ -180,6 +257,7 @@ namespace OECS; public class SystemGroup { + public SystemGroup(World world); public void Add(ISystem system); public void Remove(ISystem system); public void RunTimed(float deltaTime); @@ -212,6 +290,8 @@ 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; } } @@ -233,12 +313,31 @@ public interface IRelationship --- +## 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 +public readonly struct EntityChange : IEquatable { public Entity Entity { get; } public ChangeKind Kind { get; } @@ -257,6 +356,56 @@ public enum ChangeKind --- +## 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 @@ -269,13 +418,19 @@ public struct Position : IMessagePackSerializationCallbackReceiver { [Key(0)] public float X; [Key(1)] public float Y; + + public void OnBeforeSerialize() { } + public void OnAfterDeserialize() { } } [MessagePackObject] -public struct Velocity +public struct Velocity : IMessagePackSerializationCallbackReceiver { [Key(0)] public float X; [Key(1)] public float Y; + + public void OnBeforeSerialize() { } + public void OnAfterDeserialize() { } } // Define a system @@ -291,6 +446,8 @@ public class MovementSystem : ITickedSystem .Build(); } + public void Run(World world) => Run(world, Tick.Logical()); + public void Run(World world, Tick tick) { float dt = tick.DeltaTime; @@ -306,7 +463,7 @@ public class MovementSystem : ITickedSystem // Wire it up var world = new World(); -var group = new SystemGroup(); +var group = new SystemGroup(world); group.Add(new MovementSystem(world)); // Observe changes @@ -334,5 +491,7 @@ These types are implementation details and may change without notice: | `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. | \ No newline at end of file