diff --git a/src/Flecs.NET.Bindings/icon.png b/src/Flecs.NET.Bindings/icon.png new file mode 100644 index 00000000..40e2a7fb Binary files /dev/null and b/src/Flecs.NET.Bindings/icon.png differ diff --git a/src/Flecs.NET.Examples/Cpp/Entities/Basics.cs b/src/Flecs.NET.Examples/Cpp/Entities/Basics.cs index c1579361..f27b2cb2 100644 --- a/src/Flecs.NET.Examples/Cpp/Entities/Basics.cs +++ b/src/Flecs.NET.Examples/Cpp/Entities/Basics.cs @@ -1,4 +1,4 @@ -#if CPP_ENTITIES_BASICS +#if Cpp_Entities_Basics using Flecs.NET.Core; diff --git a/src/Flecs.NET.Examples/Flecs.NET.Examples.csproj b/src/Flecs.NET.Examples/Flecs.NET.Examples.csproj index 7c173e11..7373e15e 100644 --- a/src/Flecs.NET.Examples/Flecs.NET.Examples.csproj +++ b/src/Flecs.NET.Examples/Flecs.NET.Examples.csproj @@ -7,9 +7,9 @@ enable true - + - EXAMPLE + Example $(DefineConstants);$(Example) diff --git a/src/Flecs.NET.Examples/Program.cs b/src/Flecs.NET.Examples/Program.cs index 9e8d4430..0f8708c6 100644 --- a/src/Flecs.NET.Examples/Program.cs +++ b/src/Flecs.NET.Examples/Program.cs @@ -1,6 +1,6 @@ -#if EXAMPLE +#if Example Console.WriteLine("To run an example, use \"dotnet run --project Flecs.NET.Examples -p\""); -Console.WriteLine("Example: \"dotnet run --project src/Flecs.NET.Examples --property:Example=CPP_ENTITIES_BASICS\""); +Console.WriteLine("Example: \"dotnet run --project src/Flecs.NET.Examples --property:Example=Cpp_Entities_Basics\""); #endif diff --git a/src/Flecs.NET/Core/AppBuilder.cs b/src/Flecs.NET/Core/AppBuilder.cs index 8514d23d..82b0ea62 100644 --- a/src/Flecs.NET/Core/AppBuilder.cs +++ b/src/Flecs.NET/Core/AppBuilder.cs @@ -38,36 +38,64 @@ public AppBuilder(ecs_world_t* world) Desc.target_fps = 60; } + /// + /// Cleans up resources. + /// public void Dispose() { Managed.FreeGcHandle(_initHandle); _initHandle = default; } + /// + /// Sets the target fps. + /// + /// + /// public ref AppBuilder TargetFps(float value) { Desc.target_fps = value; return ref this; } + /// + /// Sets the delta time. + /// + /// + /// public ref AppBuilder DeltaTime(float value) { Desc.delta_time = value; return ref this; } + /// + /// Sets the number of threads to use. + /// + /// + /// public ref AppBuilder Threads(int value) { Desc.threads = value; return ref this; } + /// + /// Sets the number of frames to run. + /// + /// + /// public ref AppBuilder Frames(int value) { Desc.frames = value; return ref this; } + /// + /// Enable ecs access over http for the explorer. + /// + /// + /// public ref AppBuilder EnableRest(ushort port = 0) { Desc.enable_rest = Macros.True; @@ -75,12 +103,22 @@ public ref AppBuilder EnableRest(ushort port = 0) return ref this; } + /// + /// Periodically collect statistics. + /// + /// + /// public ref AppBuilder EnableMonitor(bool value = true) { Desc.enable_monitor = Macros.Bool(value); return ref this; } + /// + /// Sets a callback to be run before starting the main loop. + /// + /// + /// public ref AppBuilder Init(Ecs.AppInitAction value) { Managed.FreeGcHandle(_initHandle); @@ -90,12 +128,21 @@ public ref AppBuilder Init(Ecs.AppInitAction value) return ref this; } + /// + /// Context for storing custom data. + /// + /// + /// public ref AppBuilder Ctx(void* value) { Desc.ctx = value; return ref this; } + /// + /// Runs the app. + /// + /// public int Run() { fixed (ecs_app_desc_t* appDesc = &Desc) diff --git a/src/Flecs.NET/Core/EnumType.cs b/src/Flecs.NET/Core/EnumType.cs index 448559e5..442312cf 100644 --- a/src/Flecs.NET/Core/EnumType.cs +++ b/src/Flecs.NET/Core/EnumType.cs @@ -8,8 +8,13 @@ namespace Flecs.NET.Core { public static unsafe class EnumType { - public static EnumPair[] Data { get; private set; } + private static EnumPair[] _data = Array.Empty(); + /// + /// Inits entities for an enum type and it's members. + /// + /// + /// public static void Init(ecs_world_t* world, ulong id) { ecs_cpp_enum_init(world, id); @@ -19,7 +24,7 @@ public static void Init(ecs_world_t* world, ulong id) if (RuntimeFeature.IsDynamicCodeSupported) { Array values = type.GetEnumValues(); - Data = new EnumPair[values.Length]; + _data = new EnumPair[values.Length]; for (int i = 0; i < values.Length; i++) { @@ -31,7 +36,7 @@ public static void Init(ecs_world_t* world, ulong id) using NativeString nativeName = (NativeString)member.ToString(); ulong enumEntity = ecs_cpp_enum_constant_register(world, id, 0, nativeName, value); - Data[i] = new EnumPair(value, enumEntity); + _data[i] = new EnumPair(value, enumEntity); } } else @@ -40,15 +45,22 @@ public static void Init(ecs_world_t* world, ulong id) } } + /// + /// Gets an id for an enum member. + /// + /// + /// + /// + /// public static ulong Id(T member, ecs_world_t* world) { Type.Id(world); int value = Convert.ToInt32(member, CultureInfo.InvariantCulture); - for (int i = 0; i < Data.Length; i++) + for (int i = 0; i < _data.Length; i++) { - EnumPair enumPair = Data[i]; + EnumPair enumPair = _data[i]; if (enumPair.Value == value) return enumPair.Id; } @@ -56,7 +68,7 @@ public static ulong Id(T member, ecs_world_t* world) throw new InvalidOperationException("Failed to find entity associated with enum member."); } - public struct EnumPair + private struct EnumPair { public int Value { get; } public ulong Id { get; } diff --git a/src/Flecs.NET/Core/Filter.cs b/src/Flecs.NET/Core/Filter.cs index 5ef43bff..f9d57b8e 100644 --- a/src/Flecs.NET/Core/Filter.cs +++ b/src/Flecs.NET/Core/Filter.cs @@ -5,14 +5,32 @@ namespace Flecs.NET.Core { + /// + /// A filter allows for uncached, adhoc iteration over ECS data. + /// public unsafe struct Filter : IDisposable { private ecs_world_t* _world; private ecs_filter_t _filter; + /// + /// A reference to the world. + /// public ref ecs_world_t* World => ref _world; + + /// + /// A pointer to the filter. + /// public ecs_filter_t* FilterPtr => (ecs_filter_t*)Unsafe.AsPointer(ref _filter); + + /// + /// Creates a filter. + /// + /// + /// + /// + /// public Filter(ecs_world_t* world, string name = "", FilterBuilder filterBuilder = default) { Assert.True(world == filterBuilder.World, "Worlds are different"); @@ -46,6 +64,11 @@ public Filter(ecs_world_t* world, string name = "", FilterBuilder filterBuilder filterBuilder.Dispose(); } + /// + /// Creates a filter with the specified world and filter pointer. + /// + /// + /// public Filter(ecs_world_t* world, ecs_filter_t* filter) { _world = world; @@ -55,6 +78,9 @@ public Filter(ecs_world_t* world, ecs_filter_t* filter) } } + /// + /// Cleans up resources. + /// public void Dispose() { fixed (ecs_filter_t* mFilter = &_filter) @@ -64,21 +90,37 @@ public void Dispose() } } + /// + /// Returns the entity associated with the filter. + /// + /// public Entity Entity() { return new Entity(World, ecs_get_entity(FilterPtr)); } + /// + /// Returns the field count of the filter. + /// + /// public int FieldCount() { return _filter.field_count; } + /// + /// Returns the string representation of the filter query. + /// + /// public string Str() { return NativeString.GetStringAndFree(ecs_filter_str(World, FilterPtr)); } + /// + /// Iterates the filter using the provided callback. + /// + /// public void Iter(Ecs.IterCallback func) { ecs_iter_t iter = ecs_filter_iter(World, FilterPtr); diff --git a/src/Flecs.NET/Core/FilterBuilder.cs b/src/Flecs.NET/Core/FilterBuilder.cs index 39fa38c8..8c6aa2f3 100644 --- a/src/Flecs.NET/Core/FilterBuilder.cs +++ b/src/Flecs.NET/Core/FilterBuilder.cs @@ -10,20 +10,10 @@ namespace Flecs.NET.Core public unsafe struct FilterBuilder : IDisposable { private ecs_world_t* _world; - - public ref ecs_world_t* World => ref _world; - public ref ecs_filter_desc_t Desc => ref FilterDesc; - - internal ecs_filter_desc_t FilterDesc; - internal UnsafeList Terms; - internal UnsafeList Strings; - private TermIdType _termIdType; private int _termIndex; private int _exprCount; - private readonly ref ecs_term_t CurrentTerm => ref Terms[_termIndex - 1]; - private readonly ref ecs_term_id_t CurrentTermId { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -43,6 +33,24 @@ private readonly ref ecs_term_id_t CurrentTermId } } + internal ecs_filter_desc_t FilterDesc; + internal UnsafeList Terms; + internal UnsafeList Strings; + + /// + /// A reference to the world. + /// + public ref ecs_world_t* World => ref _world; + + /// + /// A reference to the filter description. + /// + public ref ecs_filter_desc_t Desc => ref FilterDesc; + + /// + /// Creates a filter builder for the provided world. + /// + /// public FilterBuilder(ecs_world_t* world) { FilterDesc = default; @@ -55,6 +63,9 @@ public FilterBuilder(ecs_world_t* world) _termIndex = default; } + /// + /// Cleans up resources. + /// public void Dispose() { for (int i = 0; i < Strings.Count; i++) @@ -76,6 +87,10 @@ private readonly void AssertTerm() Assert.True(!Unsafe.IsNullRef(ref CurrentTermId), "No active term (call .term() first)"); } + /// + /// The self flags indicates the term identifier itself is used. + /// + /// public ref FilterBuilder Self() { AssertTermId(); @@ -83,6 +98,13 @@ public ref FilterBuilder Self() return ref this; } + /// + /// The up flag indicates that the term identifier may be substituted by + /// traversing a relationship upwards. For example: substitute the identifier + /// with its parent by traversing the ChildOf relationship. + /// + /// + /// public ref FilterBuilder Up(ulong traverse = 0) { AssertTermId(); @@ -94,11 +116,24 @@ public ref FilterBuilder Up(ulong traverse = 0) return ref this; } + /// + /// The up flag indicates that the term identifier may be substituted by + /// traversing a relationship upwards. For example: substitute the identifier + /// with its parent by traversing the ChildOf relationship. + /// + /// + /// public ref FilterBuilder Up() { return ref Up(Type.Id(World)); } + /// + /// The cascade flag is like up, but returns results in breadth-first order. + /// Only supported for Query. + /// + /// + /// public ref FilterBuilder Cascade(ulong traverse = 0) { AssertTermId(); @@ -110,11 +145,21 @@ public ref FilterBuilder Cascade(ulong traverse = 0) return ref this; } + /// + /// The cascade flag is like up, but returns results in breadth-first order. + /// Only supported for Query. + /// + /// + /// public ref FilterBuilder Cascade() { return ref Cascade(Type.Id(World)); } + /// + /// The parent flag is short for Up(EcsChildOf). + /// + /// public ref FilterBuilder Parent() { AssertTermId(); @@ -122,6 +167,12 @@ public ref FilterBuilder Parent() return ref this; } + /// + /// Specify relationship to traverse, and flags to indicate direction. + /// + /// + /// + /// public ref FilterBuilder Trav(ulong traverse, uint flags = 0) { AssertTermId(); @@ -130,6 +181,11 @@ public ref FilterBuilder Trav(ulong traverse, uint flags = 0) return ref this; } + /// + /// Specify value of identifier by id. + /// + /// + /// public ref FilterBuilder Id(ulong id) { AssertTermId(); @@ -137,6 +193,14 @@ public ref FilterBuilder Id(ulong id) return ref this; } + /// + /// Specify value of identifier by id. Almost the same as id(entity), but this + /// operation explicitly sets the EcsIsEntity flag. This forces the id to + /// be interpreted as entity, whereas not setting the flag would implicitly + /// convert ids for builtin variables such as EcsThis to a variable. + /// + /// + /// public ref FilterBuilder Entity(ulong entity) { AssertTermId(); @@ -145,6 +209,11 @@ public ref FilterBuilder Entity(ulong entity) return ref this; } + /// + /// Specify value of identifier by name. + /// + /// + /// public ref FilterBuilder Name(string name) { AssertTermId(); @@ -158,6 +227,11 @@ public ref FilterBuilder Name(string name) return ref this; } + /// + /// Specify identifier is a variable (resolved at query evaluation time). + /// + /// + /// public ref FilterBuilder Var(string name) { AssertTermId(); @@ -170,6 +244,11 @@ public ref FilterBuilder Var(string name) return ref this; } + /// + /// Override term id flags. + /// + /// + /// public ref FilterBuilder Flags(uint flags) { AssertTermId(); @@ -177,6 +256,10 @@ public ref FilterBuilder Flags(uint flags) return ref this; } + /// + /// Call prior to setting values for src identifier. + /// + /// public ref FilterBuilder Src() { AssertTerm(); @@ -184,6 +267,12 @@ public ref FilterBuilder Src() return ref this; } + /// + /// Call prior to setting values for first identifier. This is either the + /// component identifier, or first element of a pair (in case second is + /// populated as well). + /// + /// public ref FilterBuilder First() { AssertTerm(); @@ -191,6 +280,11 @@ public ref FilterBuilder First() return ref this; } + /// + /// Call prior to setting values for second identifier. This is the second + /// element of a pair. Requires that First() is populated as well. + /// + /// public ref FilterBuilder Second() { AssertTerm(); @@ -198,54 +292,107 @@ public ref FilterBuilder Second() return ref this; } + /// + /// Select src identifier, initialize it with entity id. + /// + /// + /// public ref FilterBuilder Src(ulong srcId) { return ref Src().Id(srcId); } + /// + /// Select src identifier, initialize it with id associated with type. + /// + /// + /// public ref FilterBuilder Src() { return ref Src(Type.Id(World)); } + /// + /// Select src identifier, initialize it with name. If name starts with a $ + /// the name is interpreted as a variable. + /// + /// + /// public ref FilterBuilder Src(string name) { Src(); return ref name[0] == '$' ? ref Var(name[1..]) : ref Name(name); } + /// + /// Select first identifier, initialize it with entity id. + /// + /// + /// public ref FilterBuilder First(ulong firstId) { return ref First().Id(firstId); } + /// + /// Select first identifier, initialize it with id associated with type. + /// + /// + /// public ref FilterBuilder First() { return ref First(Type.Id(World)); } + /// + /// Select first identifier, initialize it with name. If name starts with a $ + /// the name is interpreted as a variable. + /// + /// + /// public ref FilterBuilder First(string name) { First(); return ref (name[0] == '$' ? ref Var(name[1..]) : ref Name(name)); } + /// + /// Select second identifier, initialize it with entity id. + /// + /// + /// public ref FilterBuilder Second(ulong secondId) { return ref Second().Id(secondId); } + /// + /// Select second identifier, initialize it with id associated with type. + /// + /// + /// public ref FilterBuilder Second() { return ref Second(Type.Id(World)); } + /// + /// Select second identifier, initialize it with name. If name starts with a $ + /// the name is interpreted as a variable. + /// + /// + /// public ref FilterBuilder Second(string secondName) { Second(); return ref (secondName[0] == '$' ? ref Var(secondName[1..]) : ref Name(secondName)); } + /// + /// Set role of term. + /// + /// + /// public ref FilterBuilder Role(ulong role) { AssertTerm(); @@ -253,6 +400,11 @@ public ref FilterBuilder Role(ulong role) return ref this; } + /// + /// Set read/write access of term. + /// + /// + /// public ref FilterBuilder InOut(ecs_inout_kind_t inOut) { AssertTerm(); @@ -260,6 +412,17 @@ public ref FilterBuilder InOut(ecs_inout_kind_t inOut) return ref this; } + /// + /// Set read/write access for stage. Use this when a system reads or writes + /// components other than the ones provided by the query. This information + /// can be used by schedulers to insert sync/merge points between systems + /// where deferred operations are flushed. + /// + /// Setting this is optional. If not set, the value of the accessed component + /// may be out of sync for at most one frame. + /// + /// + /// public ref FilterBuilder InOutStage(ecs_inout_kind_t inOut) { AssertTerm(); @@ -270,41 +433,77 @@ public ref FilterBuilder InOutStage(ecs_inout_kind_t inOut) return ref this; } + /// + /// Short for InOutStage(EcsOut). + /// Use when system uses add, remove or set. + /// + /// public ref FilterBuilder Write() { return ref InOutStage(EcsOut); } + /// + /// Short for InOutStage(EcsIn). + /// Use when system uses get. + /// + /// public ref FilterBuilder Read() { return ref InOutStage(EcsIn); } + /// + /// Short for InOutStage(EcsInOut). + /// Use when system uses get_mut. + /// + /// public ref FilterBuilder ReadWrite() { return ref InOutStage(EcsInOut); } + /// + /// Short for InOut(EcsIn) + /// + /// public ref FilterBuilder In() { return ref InOut(EcsIn); } + /// + /// Short for InOut(EcsOut) + /// + /// public ref FilterBuilder Out() { return ref InOut(EcsOut); } + /// + /// Short for InOut(EcsInOut) + /// + /// public ref FilterBuilder InOut() { return ref InOut(EcsInOut); } + /// + /// Short for InOut(EcsInOutNone) + /// + /// public ref FilterBuilder InOutNone() { return ref InOut(EcsInOutNone); } + /// + /// Set operator of term. + /// + /// + /// public ref FilterBuilder Oper(ecs_oper_kind_t oper) { AssertTerm(); @@ -312,41 +511,74 @@ public ref FilterBuilder Oper(ecs_oper_kind_t oper) return ref this; } + /// + /// Short for Oper(EcsAnd). + /// + /// public ref FilterBuilder And() { return ref Oper(EcsAnd); } + /// + /// Short for Oper(EcsOr). + /// + /// public ref FilterBuilder Or() { return ref Oper(EcsOr); } + /// + /// Short for Oper(EcsNot). + /// + /// public ref FilterBuilder Not() { return ref Oper(EcsNot); } + /// + /// Short for Oper(EcsOptional). + /// + /// public ref FilterBuilder Optional() { return ref Oper(EcsOptional); } + + /// + /// Short for Oper(EcsAndFrom). + /// + /// public ref FilterBuilder AndFrom() { return ref Oper(EcsAndFrom); } + /// + /// Short for Oper(EcsOFrom). + /// + /// public ref FilterBuilder OrFrom() { return ref Oper(EcsOrFrom); } + /// + /// Short for Oper(EcsNotFrom). + /// + /// public ref FilterBuilder NotFrom() { return ref Oper(EcsNotFrom); } + /// + /// Match singleton. + /// + /// public ref FilterBuilder Singleton() { AssertTerm(); @@ -360,24 +592,46 @@ public ref FilterBuilder Singleton() return ref this; } + /// + /// Filter terms are not triggered on by observers. + /// + /// public ref FilterBuilder Filter() { CurrentTerm.src.flags |= EcsFilter; return ref this; } + /// + /// When true, terms returned by an iterator may either contain 1 or N + /// elements, where terms with N elements are owned, and terms with 1 element + /// are shared, for example from a parent or base entity. When false, the + /// iterator will at most return 1 element when the result contains both + /// owned and shared terms. + /// + /// public ref FilterBuilder Instanced() { Desc.instanced = Macros.True; return ref this; } + /// + /// Set flags for advanced usage + /// + /// + /// public ref FilterBuilder FilterFlags(uint flags) { Desc.flags |= flags; return ref this; } + /// + /// Filter expression. Should not be set at the same time as terms array. + /// + /// + /// public ref FilterBuilder Expr(string expr) { Assert.True(_exprCount == 0, "FilterBuilder.Expr() called more than once"); @@ -391,156 +645,339 @@ public ref FilterBuilder Expr(string expr) return ref this; } + /// + /// Alternative form of Term(). + /// + /// + /// public ref FilterBuilder With(ulong id) { return ref Term(id); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(ulong first, ulong second) { return ref Term(first, second); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(ulong first, string second) { return ref Term(first, second); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(string first, ulong second) { return ref Term(first, second); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(string first, string second) { return ref Term(first, second); } + /// + /// Alternative form of Term(). + /// + /// + /// public ref FilterBuilder With() { return ref Term(); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(TEnum enumMember) where TEnum : Enum { return ref Term(enumMember); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(ulong second) { return ref Term(second); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With(string second) { return ref Term(second); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// public ref FilterBuilder With() { return ref Term(); } + /// + /// Alternative form of Term(). + /// + /// + /// + /// + /// public ref FilterBuilder With(TSecondEnum secondEnum) where TSecondEnum : Enum { return ref Term(secondEnum); } + /// + /// Alternative form of TermSecond(). + /// + /// + /// + /// public ref FilterBuilder WithSecond(ulong first) { return ref TermSecond(first); } + /// + /// Alternative form of TermSecond(). + /// + /// + /// + /// public ref FilterBuilder WithSecond(string first) { return ref TermSecond(first); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// public ref FilterBuilder Without(ulong id) { return ref Term(id).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(ulong first, ulong second) { return ref Term(first, second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(ulong first, string second) { return ref Term(first, second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(string first, ulong second) { return ref Term(first, second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(string first, string second) { return ref Term(first, second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// public ref FilterBuilder Without() { return ref Term().Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(TEnum enumMember) where TEnum : Enum { return ref Term(enumMember).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(ulong second) { return ref Term(second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without(string second) { return ref Term(second).Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// public ref FilterBuilder Without() { return ref Term().Not(); } + /// + /// Alternative form of Term().Not(). + /// + /// + /// + /// + /// public ref FilterBuilder Without(TSecondEnum secondEnum) where TSecondEnum : Enum { return ref Term(secondEnum).Not(); } + /// + /// Alternative form of TermSecond().Not(). + /// + /// + /// + /// public ref FilterBuilder WithoutSecond(ulong first) { return ref TermSecond(first).Not(); } + /// + /// Alternative form of TermSecond().Not(). + /// + /// + /// + /// public ref FilterBuilder WithoutSecond(string first) { return ref TermSecond(first).Not(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// public ref FilterBuilder Write(ulong id) { return ref Term(id).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(ulong first, ulong second) { return ref Term(first, second).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(ulong first, string second) { return ref Term(first, second).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(string first, ulong second) { return ref Term(first, second).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(string first, string second) { return ref Term(first, second).Write(); @@ -551,116 +988,248 @@ public ref FilterBuilder Write() return ref Term().Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(TEnum enumMember) where TEnum : Enum { return ref Term(enumMember).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(ulong second) { return ref Term(second).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write(string second) { return ref Term(second).Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// public ref FilterBuilder Write() { return ref Term().Write(); } + /// + /// Alternative form of Term().Write(). + /// + /// + /// + /// + /// public ref FilterBuilder Write(TSecondEnum secondEnum) where TSecondEnum : Enum { return ref Term(secondEnum).Write(); } + /// + /// Alternative form of TermSecond().Write(). + /// + /// + /// + /// public ref FilterBuilder WriteSecond(ulong first) { return ref TermSecond(first).Write(); } + /// + /// Alternative form of TermSecond().Write(). + /// + /// + /// + /// public ref FilterBuilder WriteSecond(string first) { return ref TermSecond(first).Write(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// public ref FilterBuilder Read(ulong id) { return ref Term(id).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(ulong first, ulong second) { return ref Term(first, second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(ulong first, string second) { return ref Term(first, second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(string first, ulong second) { return ref Term(first, second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(string first, string second) { return ref Term(first, second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// public ref FilterBuilder Read() { return ref Term().Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(TEnum enumMember) where TEnum : Enum { return ref Term(enumMember).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(ulong second) { return ref Term(second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read(string second) { return ref Term(second).Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// public ref FilterBuilder Read() { return ref Term().Read(); } + /// + /// Alternative form of Term().Read(). + /// + /// + /// + /// + /// public ref FilterBuilder Read(TSecondEnum secondEnum) where TSecondEnum : Enum { return ref Term(secondEnum).Read(); } + /// + /// Alternative form of TermSecond().Read(). + /// + /// + /// + /// public ref FilterBuilder ReadSecond(ulong first) { return ref TermSecond(first).Read(); } + /// + /// Alternative form of TermSecond().Read(). + /// + /// + /// + /// public ref FilterBuilder ReadSecond(string first) { return ref TermSecond(first).Read(); } + /// + /// Alternative form of With(EcsScopeOpen).Entity(0) + /// + /// public ref FilterBuilder ScopeOpen() { return ref With(EcsScopeOpen).Entity(0); } + /// + /// Alternative form of With(EcsScopeClose).Entity(0) + /// + /// public ref FilterBuilder ScopeClose() { return ref With(EcsScopeClose).Entity(0); } + /// + /// Increments to the next term. + /// + /// public ref FilterBuilder IncrementTerm() { if (Terms.Count >= _termIndex) @@ -670,6 +1239,11 @@ public ref FilterBuilder IncrementTerm() return ref this; } + /// + /// Sets the current term to the one at the provided index. + /// + /// + /// public ref FilterBuilder TermAt(int termIndex) { Assert.True(termIndex > 0, nameof(ECS_INVALID_PARAMETER)); @@ -685,12 +1259,21 @@ public ref FilterBuilder TermAt(int termIndex) return ref this; } + /// + /// Alternative form for TermAt(). + /// + /// + /// public ref FilterBuilder Arg(int termIndex) { return ref TermAt(termIndex); } - + /// + /// Increments to the next term with the provided id. + /// + /// + /// public ref FilterBuilder Term(ulong id) { IncrementTerm(); @@ -698,6 +1281,11 @@ public ref FilterBuilder Term(ulong id) return ref this; } + /// + /// Increments to the next term with the provided name. + /// + /// + /// public ref FilterBuilder Term(string name) { IncrementTerm(); @@ -706,6 +1294,12 @@ public ref FilterBuilder Term(string name) return ref this; } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(ulong first, ulong second) { IncrementTerm(); @@ -713,6 +1307,12 @@ public ref FilterBuilder Term(ulong first, ulong second) return ref this; } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(ulong first, string second) { IncrementTerm(); @@ -721,6 +1321,12 @@ public ref FilterBuilder Term(ulong first, string second) return ref this; } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(string first, ulong second) { IncrementTerm(); @@ -730,6 +1336,12 @@ public ref FilterBuilder Term(string first, ulong second) return ref this; } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(string first, string second) { IncrementTerm(); @@ -739,6 +1351,11 @@ public ref FilterBuilder Term(string first, string second) return ref this; } + /// + /// Increments to the next term with the provided type. + /// + /// + /// public ref FilterBuilder Term() { IncrementTerm(); @@ -747,6 +1364,12 @@ public ref FilterBuilder Term() return ref this; } + /// + /// Increments to the next term with the provided enum. + /// + /// + /// + /// public ref FilterBuilder Term(TEnum enumMember) where TEnum : Enum { ulong enumId = EnumType.Id(enumMember, World); @@ -754,33 +1377,70 @@ public ref FilterBuilder Term(TEnum enumMember) where TEnum : Enum return ref Term(pair); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(ulong second) { return ref Term(Type.Id(World), second); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term(string second) { return ref Term(Type.Id(World)).Second(second); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder Term() { return ref Term(Type.Id(World)); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// + /// public ref FilterBuilder Term(TSecondEnum secondEnum) where TSecondEnum : Enum { ulong enumId = EnumType.Id(secondEnum, World); return ref Term(enumId); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder TermSecond(ulong first) { ulong pair = Macros.PairSecond(first, World); return ref Term(pair); } + /// + /// Increments to the next term with the provided pair. + /// + /// + /// + /// public ref FilterBuilder TermSecond(string first) { return ref Term(first, Type.Id(World)); diff --git a/src/Flecs.NET/Core/FlecsInternal.cs b/src/Flecs.NET/Core/FlecsInternal.cs index a369242a..22d3c41b 100644 --- a/src/Flecs.NET/Core/FlecsInternal.cs +++ b/src/Flecs.NET/Core/FlecsInternal.cs @@ -2,11 +2,17 @@ namespace Flecs.NET.Core { public class FlecsInternal { + /// + /// Current reset count. + /// public static int ResetCount { get; private set; } + /// + /// Resets all type ids. + /// public static void Reset() { ResetCount++; } } -} \ No newline at end of file +} diff --git a/src/Flecs.NET/Core/IFlecsModule.cs b/src/Flecs.NET/Core/IFlecsModule.cs index 511eafbd..84cde9d2 100644 --- a/src/Flecs.NET/Core/IFlecsModule.cs +++ b/src/Flecs.NET/Core/IFlecsModule.cs @@ -1,7 +1,14 @@ namespace Flecs.NET.Core { + /// + /// Interface for flecs modules. + /// public interface IFlecsModule { + /// + /// Register entities, components, and systems. + /// + /// public void InitModule(ref World world); } } diff --git a/src/Flecs.NET/Core/Id.cs b/src/Flecs.NET/Core/Id.cs index 320033f7..45cbc36e 100644 --- a/src/Flecs.NET/Core/Id.cs +++ b/src/Flecs.NET/Core/Id.cs @@ -4,65 +4,121 @@ namespace Flecs.NET.Core { + /// + /// Class that wraps around a flecs::id_t. + /// public unsafe struct Id : IEquatable { private ecs_world_t* _world; private ulong _value; + /// + /// A reference to the world. + /// public ref ecs_world_t* World => ref _world; + + /// + /// A reference to the id value. + /// public ref ulong Value => ref _value; + /// + /// Creates an id with the provided id value. + /// + /// public Id(ulong id) { _world = null; _value = id; } + /// + /// Creates an id with the provided pair. + /// + /// + /// public Id(ulong first, ulong second) { _world = null; _value = Macros.Pair(first, second); } + /// + /// Creates an id with the provided world and pair. + /// + /// + /// + /// public Id(ecs_world_t* world, ulong first, ulong second) { _world = world; _value = Macros.Pair(first, second); } + /// + /// Creates an id with the provided world and id value. + /// + /// + /// public Id(ecs_world_t* world, ulong id = 0) { _world = world; _value = id; } + /// + /// Creates an id with the provided pair. + /// + /// + /// public Id(Id first, Id second) { _world = first.World; _value = Macros.Pair(first.Value, second.Value); } + /// + /// Creates an id with the provided pair. + /// + /// + /// public Id(Entity first, Entity second) { _world = first.World; _value = Macros.Pair(first, second); } + /// + /// Test if id is pair. (has first, second) + /// + /// public bool IsPair() { return (Value & ECS_ID_FLAGS_MASK) == ECS_PAIR; } + /// + /// Test if id is a wildcard. + /// + /// public bool IsWildCard() { return ecs_id_is_wildcard(Value) == 1; } + /// + /// Test if id is an entity. + /// + /// public bool IsEntity() { return (Value & ECS_ID_FLAGS_MASK) == 0; } + /// + /// Return id as entity. (only allowed when id is valid entity) + /// + /// public Entity Entity() { Assert.True(!IsPair()); @@ -70,52 +126,99 @@ public Entity Entity() return new Entity(World, Value); } + /// + /// Return id with role added + /// + /// + /// public Entity AddFlags(ulong flags) { return new Entity(World, Value | flags); } + /// + /// Return id with role removed. + /// + /// + /// public Entity RemoveFlags(ulong flags) { Assert.True((Value & ECS_ID_FLAGS_MASK) == flags); return new Entity(World, Value & ECS_COMPONENT_MASK); } + /// + /// Return id without role. + /// + /// public Entity RemoveFlags() { return new Entity(World, Value & ECS_COMPONENT_MASK); } + /// + /// Return id without generation. + /// + /// public Entity RemoveGeneration() { return new Entity(World, (uint)Value); } + /// + /// Return component type of id. + /// + /// public Entity TypeId() { return new Entity(World, ecs_get_typeid(World, Value)); } + /// + /// Test if id has specified flags. + /// + /// + /// public bool HasFlags(ulong flags) { return (Value & flags) == flags; } + /// + /// Test if id has flags. + /// + /// public bool HasFlags() { return (Value & ECS_ID_FLAGS_MASK) != 0; } + /// + /// Return id flags set on id. + /// + /// public Entity Flags() { return new Entity(World, Value & ECS_ID_FLAGS_MASK); } + /// + /// Test if id has specified first. + /// + /// + /// public bool HasRelation(ulong first) { return IsPair() && Macros.PairFirst(Value) == first; } + /// + /// Get first element from a pair. + /// If the id is not a pair, this operation will fail. When the id has a + /// world, the operation will ensure that the returned id has the correct + /// generation count. + /// + /// public Entity First() { Assert.True(IsPair()); @@ -123,67 +226,132 @@ public Entity First() return World == null ? new Entity(entity) : new Entity(World, ecs_get_alive(World, entity)); } + /// + /// Get second element from a pair. + /// If the id is not a pair, this operation will fail. When the id has a + /// world, the operation will ensure that the returned id has the correct + /// generation count. + /// + /// public Entity Second() { ulong entity = Macros.PairSecond(Value); return World == null ? new Entity(entity) : new Entity(World, ecs_get_alive(World, entity)); } + /// + /// Convert id to string. + /// + /// public string Str() { return NativeString.GetStringAndFree(ecs_id_str(World, Value)); } + /// + /// Convert role of id to string. + /// + /// public string FlagsStr() { return NativeString.GetStringAndFree(ecs_id_flag_str(Value & ECS_ID_FLAGS_MASK)); } + /// + /// Returns the C# world. + /// + /// public World CsWorld() { return new World(World); } + /// + /// + /// + /// + /// public static implicit operator ulong(Id id) { return id.Value; } + /// + /// + /// + /// + /// public static implicit operator Id(ulong id) { return new Id(id); } + /// + /// + /// + /// + /// + /// public static bool operator ==(Id left, Id right) { return left.Equals(right); } + /// + /// + /// + /// + /// + /// public static bool operator !=(Id left, Id right) { return !(left == right); } + /// + /// + /// + /// + /// public static Id FromUInt64(ulong id) { return new Id(id); } + /// + /// + /// + /// + /// public static ulong ToUInt64(Id id) { return id.Value; } + /// + /// + /// + /// + /// public bool Equals(Id other) { return Value == other.Value; } + /// + /// + /// + /// + /// public override bool Equals(object? obj) { return obj is Id id && Equals(id); } + /// + /// Returns a hash code. + /// + /// public override int GetHashCode() { return Value.GetHashCode(); diff --git a/src/Flecs.NET/Core/Invoker.cs b/src/Flecs.NET/Core/Invoker.cs index 01bd6bc5..9943b413 100644 --- a/src/Flecs.NET/Core/Invoker.cs +++ b/src/Flecs.NET/Core/Invoker.cs @@ -6,6 +6,13 @@ namespace Flecs.NET.Core { public static unsafe class Invoker { + /// + /// + /// + /// + /// + /// + /// public static void Iter(Ecs.IterCallback iterCallback, Ecs.IterNext iterNext, ecs_iter_t* iter) { if (iterCallback == null) @@ -22,6 +29,13 @@ public static void Iter(Ecs.IterCallback iterCallback, Ecs.IterNext iterNext, ec } } + /// + /// + /// + /// + /// + /// + /// public static void EachEntity(Ecs.EachEntityCallback eachCallback, Ecs.IterNext iterNext, ecs_iter_t* iter) { if (eachCallback == null) diff --git a/src/Flecs.NET/Core/Iter.cs b/src/Flecs.NET/Core/Iter.cs index a1be6e3e..bd849fb1 100644 --- a/src/Flecs.NET/Core/Iter.cs +++ b/src/Flecs.NET/Core/Iter.cs @@ -4,12 +4,30 @@ namespace Flecs.NET.Core { + /// + /// Class for iterating over query results. + /// public readonly unsafe struct Iter : IEnumerable { + /// + /// Reference to handle. + /// public ecs_iter_t* Handle { get; } + + /// + /// + /// public int Begin { get; } + + /// + /// + /// public int End { get; } + /// + /// Creates an iter wrapper using the provided handle. + /// + /// public Iter(ecs_iter_t* iter) { Handle = iter; @@ -17,132 +35,231 @@ public Iter(ecs_iter_t* iter) End = iter->count; } + /// + /// Returns entity id of system. + /// + /// public Entity System() { return new Entity(Handle->world, Handle->system); } + /// + /// Returns entity id of event. + /// + /// public Entity Event() { return new Entity(Handle->world, Handle->@event); } + /// + /// Returns the entity id of the event id. + /// + /// public Entity EventId() { return new Entity(Handle->world, Handle->event_id); } + /// + /// Returns staged C# world. + /// + /// public World World() { return new World(Handle->world, false); } - public ecs_iter_t* CPtr() - { - return Handle; - } - + /// + /// Returns count of iter. + /// + /// public int Count() { return (Handle->count); } + /// + /// Returns the delta time. + /// + /// public float DeltaTime() { return Handle->delta_time; } + /// + /// Returns the delta system time. + /// + /// public float DeltaSystemTime() { return Handle->delta_system_time; } + /// + /// Returns the type of the iterated table. + /// + /// public Types Types() { return new Types(Handle->world, ecs_table_get_type(Handle->table)); } + /// + /// Returns table of the iter. + /// + /// public Table Table() { return new Table(Handle->world, Handle->table); } - // public bool HasModule() - // { - // return ecs_table_has_module(Handle->table) == 1; - // } - + /// + /// Returns context pointer. + /// + /// public void* CtxPtr() { return Handle->ctx; } + /// + /// Returns context pointer. + /// + /// + /// public T* CtxPtr() where T : unmanaged { return (T*)Handle->ctx; } + /// + /// Returns context ref. + /// + /// + /// public ref T Ctx() where T : unmanaged { return ref *CtxPtr(); } + /// + /// Returns param pointer. + /// + /// public void* ParamPtr() { return Handle->param; } + /// + /// Returns param pointer. + /// + /// + /// public T* ParamPtr() where T : unmanaged { return (T*)Handle->param; } + /// + /// Returns param ref. + /// + /// + /// public ref T Param() where T : unmanaged { return ref *ParamPtr(); } + /// + /// Obtain mutable handle to entity being iterated over. + /// + /// + /// public Entity Entity(int row) { Assert.True(row < Handle->count, nameof(ECS_COLUMN_INDEX_OUT_OF_RANGE)); return new Entity(Handle->world, Handle->entities[row]); } + /// + /// Returns whether field is matched on self. + /// + /// + /// public bool IsSelf(int index) { return ecs_field_is_self(Handle, index) == 1; } + /// + /// Returns whether field is set. + /// + /// + /// public bool IsSet(int index) { return ecs_field_is_set(Handle, index) == 1; } + /// + /// Returns whether field is readonly. + /// + /// + /// public bool IsReadonly(int index) { return ecs_field_is_readonly(Handle, index) == 1; } + /// + /// Number of fields in iterator. + /// + /// public int FieldCount() { return Handle->field_count; } + /// + /// Size of field data type. + /// + /// + /// public ulong Size(int index) { return ecs_field_size(Handle, index); } + /// + /// Obtain field source (0 if This). + /// + /// + /// public Entity Src(int index) { return new Entity(Handle->world, ecs_field_src(Handle, index)); } + /// + /// Obtain id matched for field. + /// + /// + /// public Entity Id(int index) { return new Entity(Handle->world, ecs_field_id(Handle, index)); } + /// + /// Obtain pair id matched for field. + /// This operation will fail if the id is not a pair. + /// + /// + /// public Entity Pair(int index) { ulong id = ecs_field_id(Handle, index); @@ -150,42 +267,100 @@ public Entity Pair(int index) return new Entity(Handle->world, id); } + /// + /// Obtain column index for field. + /// + /// + /// + public int ColumnIndex(int index) + { + return ecs_field_column_index(Handle, index); + } + + /// + /// Convert current iterator result to string. + /// + /// public string Str() { return NativeString.GetStringAndFree(ecs_iter_str(Handle)); } + /// + /// Get readonly access to field data. + /// + /// + /// + /// public Column Field(int index) { return GetField(index); } + /// + /// Get access to entity ids. + /// + /// + public Column Entities() + { + return new Column(Handle->entities, Handle->count); + } + + /// + /// Obtain the total number of tables the iterator will iterate over. + /// + /// public int TableCount() { return Handle->table_count; } + /// + /// Check if the current table has changed since the last iteration. + /// Can only be used when iterating queries and/or systems. + /// + /// public bool Changed() { return ecs_query_changed(null, Handle) == 1; } + /// + /// Skip current table. + /// This indicates to the query that the data in the current table is not + /// modified. By default, iterating a table with a query will mark the + /// iterated components as dirty if they are annotated with InOut or Out. + /// public void Skip() { ecs_query_skip(Handle); } + /// + /// Return group id for current table (grouped queries only) + /// + /// public ulong GroupId() { return Handle->group_id; } + /// + /// Get value of variable by id. + /// + /// + /// public Entity GetVar(int varId) { Assert.True(varId != -1, nameof(ECS_INVALID_PARAMETER)); return new Entity(Handle->world, ecs_iter_get_var(Handle, varId)); } + /// + /// Get value of variable by name. + /// + /// + /// public Entity GetVar(string name) { ecs_rule_iter_t* ruleIter = &Handle->priv.iter.rule; @@ -217,6 +392,10 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } + /// + /// Gets an enumerator for iter. + /// + /// public IterEnumerator GetEnumerator() { return new IterEnumerator(Handle->count); @@ -247,4 +426,4 @@ public void Reset() Current = -1; } } -} \ No newline at end of file +} diff --git a/src/Flecs.NET/Core/Module.cs b/src/Flecs.NET/Core/Module.cs index b7ac699a..71a0f2e0 100644 --- a/src/Flecs.NET/Core/Module.cs +++ b/src/Flecs.NET/Core/Module.cs @@ -3,8 +3,17 @@ namespace Flecs.NET.Core { + /// + /// Static class for importng modules. + /// public static unsafe class Module { + /// + /// Imports a module. + /// + /// + /// + /// public static Entity Import(World world) where T : IFlecsModule, new() { string symbol = Type.GetSymbolName(); @@ -27,6 +36,13 @@ public static unsafe class Module return new Entity(world, module); } + /// + /// Imports a module. + /// + /// + /// + /// + /// public static ulong DoImport(World world, string symbol) where T : IFlecsModule, new() { ulong scope = ecs_set_scope(world, 0); @@ -50,4 +66,4 @@ public static unsafe class Module return moduleEntity; } } -} \ No newline at end of file +}