You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
4.0 KiB
158 lines
4.0 KiB
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Runtime.CompilerServices; |
|
using gaemstone.ECS.Utility; |
|
using static flecs_hub.flecs; |
|
|
|
namespace gaemstone.ECS; |
|
|
|
public unsafe class Iterator |
|
: IEnumerable<Iterator> |
|
, IDisposable |
|
{ |
|
public World World { get; } |
|
public IteratorType? Type { get; } |
|
public ecs_iter_t Value; |
|
|
|
public bool Completed { get; private set; } |
|
public int Count => Value.count; |
|
public TimeSpan DeltaTime => TimeSpan.FromSeconds(Value.delta_time); |
|
public TimeSpan DeltaSystemTime => TimeSpan.FromSeconds(Value.delta_system_time); |
|
|
|
public Iterator(World world, IteratorType? type, ecs_iter_t value) |
|
{ World = world; Type = type; Value = value; } |
|
|
|
public static Iterator FromTerm(World world, Term term) |
|
{ |
|
using var alloc = TempAllocator.Use(); |
|
var flecsTerm = term.ToFlecs(alloc); |
|
var flecsIter = ecs_term_iter(world, &flecsTerm); |
|
return new(world, IteratorType.Term, flecsIter); |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
// When an iterator is iterated until completion, |
|
// ecs_iter_fini will be called automatically. |
|
if (!Completed) |
|
fixed (ecs_iter_t* ptr = &Value) |
|
ecs_iter_fini(ptr); |
|
} |
|
|
|
|
|
public EntityRef GetVar(Variable var) |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
return new(World, new(ecs_iter_get_var(ptr, var.Index))); |
|
} |
|
|
|
public Iterator SetVar(Variable var, Entity entity) |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
ecs_iter_set_var(ptr, var.Index, entity); |
|
return this; |
|
} |
|
|
|
|
|
public bool Next() |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) { |
|
var result = Type switch { |
|
IteratorType.Term => ecs_term_next(ptr), |
|
IteratorType.Filter => ecs_filter_next(ptr), |
|
IteratorType.Query => ecs_query_next(ptr), |
|
IteratorType.Rule => ecs_rule_next(ptr), |
|
_ => ecs_iter_next(ptr), |
|
}; |
|
Completed = !result; |
|
return result; |
|
} |
|
} |
|
|
|
|
|
public bool FieldIsSet(int index) |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
return ecs_field_is_set(ptr, index); |
|
} |
|
|
|
public bool FieldIs(int index, Id id) |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
return ecs_field_id(ptr, index) == id.Value; |
|
} |
|
|
|
// TODO: Potentially misleading, doesn't check the field's backing data type. |
|
// The id might be "(Identifier, Name)", but its data type "Identifier". |
|
public bool FieldIs<T>(int index) |
|
=> FieldIs(index, World.LookupByType<T>()); |
|
|
|
public IdRef FieldId(int index) |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
return new(World, new(ecs_field_id(ptr, index))); |
|
} |
|
|
|
|
|
public EntityRef Entity(int index) |
|
=> new(World, new(Value.entities[index])); |
|
|
|
public Span<T> Field<T>(int index) |
|
where T : unmanaged |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) { |
|
var size = (ulong)Unsafe.SizeOf<T>(); |
|
var isSelf = ecs_field_is_self(ptr, index); |
|
var pointer = ecs_field_w_size(ptr, size, index); |
|
return new(pointer, isSelf ? Count : 1); |
|
} |
|
} |
|
|
|
public SpanToRef<T> Field<T>(int index, T _ = null!) where T : class |
|
=> new(Field<ReferenceHandle>(index)); |
|
|
|
public Span<T> FieldOrEmpty<T>(int index) where T : unmanaged |
|
=> FieldIsSet(index) ? Field<T>(index) : Span<T>.Empty; |
|
|
|
public SpanToRef<T> FieldOrEmpty<T>(int index, T _ = null!) where T : class |
|
=> FieldIsSet(index) ? Field(index, _) : SpanToRef<T>.Empty; |
|
|
|
|
|
public override string ToString() |
|
{ |
|
fixed (ecs_iter_t* ptr = &Value) |
|
return ecs_iter_str(ptr).FlecsToStringAndFree()!; |
|
} |
|
|
|
// IEnumerable implementation |
|
public IEnumerator<Iterator> GetEnumerator() { while (Next()) yield return this; } |
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|
|
|
|
|
public class Variable |
|
{ |
|
public int Index { get; } |
|
public string Name { get; } |
|
public Variable(int index, string name) |
|
{ Index = index; Name = name; } |
|
} |
|
|
|
public readonly ref struct SpanToRef<T> |
|
where T : class |
|
{ |
|
public static SpanToRef<T> Empty => default; |
|
private readonly Span<ReferenceHandle> _span; |
|
public int Length => _span.Length; |
|
public T? this[int index] => (T?)_span[index].Target; |
|
internal SpanToRef(Span<ReferenceHandle> span) => _span = span; |
|
} |
|
} |
|
|
|
public enum IteratorType |
|
{ |
|
Term, |
|
Filter, |
|
Query, |
|
Rule, |
|
}
|
|
|