|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using gaemstone.Utility;
|
|
|
|
using static flecs_hub.flecs;
|
|
|
|
|
|
|
|
namespace gaemstone.ECS;
|
|
|
|
|
|
|
|
public unsafe class Iterator
|
|
|
|
: IEnumerable<Iterator>
|
|
|
|
, IDisposable
|
|
|
|
{
|
|
|
|
public Universe Universe { 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(Universe universe, IteratorType? type, ecs_iter_t value)
|
|
|
|
{ Universe = universe; Type = type; Value = value; }
|
|
|
|
|
|
|
|
public static Iterator FromTerm(Universe universe, Term term)
|
|
|
|
{
|
|
|
|
using var alloc = TempAllocator.Use();
|
|
|
|
var flecsTerm = term.ToFlecs(alloc);
|
|
|
|
var flecsIter = ecs_term_iter(universe, &flecsTerm);
|
|
|
|
return new(universe, IteratorType.Term, flecsIter);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
// NOTE: When an iterator is iterated until completion, resources are automatically freed.
|
|
|
|
if (!Completed)
|
|
|
|
fixed (ecs_iter_t* ptr = &Value)
|
|
|
|
ecs_iter_fini(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SetThis(Entity entity)
|
|
|
|
{
|
|
|
|
fixed (ecs_iter_t* ptr = &Value)
|
|
|
|
ecs_iter_set_var(ptr, 0, entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 EntityRef Entity(int index)
|
|
|
|
=> new(Universe, 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 Span<T> MaybeField<T>(int index)
|
|
|
|
where T : unmanaged => FieldIsSet(index) ? Field<T>(index) : default;
|
|
|
|
|
|
|
|
public SpanToRef<T> FieldRef<T>(int index)
|
|
|
|
where T : class => new(Field<nint>(index));
|
|
|
|
|
|
|
|
public bool FieldIsSet(int index)
|
|
|
|
{
|
|
|
|
fixed (ecs_iter_t* ptr = &Value)
|
|
|
|
return ecs_field_is_set(ptr, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool FieldIs<T>(int index)
|
|
|
|
{
|
|
|
|
fixed (ecs_iter_t* ptr = &Value) {
|
|
|
|
var id = ecs_field_id(ptr, index);
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
return id == comp.Entity.Value.Data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 enum IteratorType
|
|
|
|
{
|
|
|
|
Term,
|
|
|
|
Filter,
|
|
|
|
Query,
|
|
|
|
Rule,
|
|
|
|
}
|