using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using gaemstone.Utility; using gaemstone.Utility.IL; using static flecs_hub.flecs; namespace gaemstone.ECS; public unsafe sealed class Filter : IDisposable { public Universe Universe { get; } public ecs_filter_t* Handle { get; } public Filter(Universe universe, FilterDesc desc) { using var alloc = TempAllocator.Use(); var flecsDesc = desc.ToFlecs(alloc); Universe = universe; Handle = ecs_filter_init(universe, &flecsDesc); } public static void RunOnce(Universe universe, Delegate action) { var gen = IterActionGenerator.GetOrBuild(universe, action.Method); var desc = new FilterDesc(gen.Terms.ToArray()); using var filter = new Filter(universe, desc); foreach (var iter in filter.Iter()) gen.RunWithTryCatch(action.Target, iter); } public void Dispose() => ecs_filter_fini(Handle); public Iterator Iter() => new(Universe, IteratorType.Filter, ecs_filter_iter(Universe, this)); public override string ToString() => ecs_filter_str(Universe, Handle).FlecsToStringAndFree()!; public static implicit operator ecs_filter_t*(Filter q) => q.Handle; } public class FilterDesc { public IReadOnlyList Terms { get; } public string? Expression { get; } /// /// 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 bool Instanced { get; set; } /// /// Entity associated with query (optional). /// public Entity Entity { get; set; } public FilterDesc(params Term[] terms) => Terms = terms; public FilterDesc(string expression) : this() => Expression = expression; public unsafe ecs_filter_desc_t ToFlecs(IAllocator allocator) { var desc = new ecs_filter_desc_t { expr = allocator.AllocateCString(Expression), instanced = Instanced, entity = Entity, }; var span = desc.terms; if (Terms.Count > span.Length) { span = allocator.Allocate(Terms.Count); desc.terms_buffer = (ecs_term_t*)Unsafe.AsPointer(ref span[0]); desc.terms_buffer_count = Terms.Count; } for (var i = 0; i < Terms.Count; i++) span[i] = Terms[i].ToFlecs(allocator); return desc; } }