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.
124 lines
3.0 KiB
124 lines
3.0 KiB
1 year ago
|
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 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 Iterator SetThis(Entity entity)
|
||
|
{
|
||
|
fixed (ecs_iter_t* ptr = &Value)
|
||
|
ecs_iter_set_var(ptr, 0, entity);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
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 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(World, new(Value.entities[index]));
|
||
|
|
||
|
public IdentifierRef FieldId(int index)
|
||
|
{
|
||
|
fixed (ecs_iter_t* ptr = &Value)
|
||
|
return new(World, new(ecs_field_id(ptr, 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> FieldOrEmpty<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 = World.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,
|
||
|
}
|