|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using gaemstone.Utility;
|
|
|
|
using static flecs_hub.flecs;
|
|
|
|
|
|
|
|
namespace gaemstone.ECS;
|
|
|
|
|
|
|
|
public unsafe sealed class EntityRef
|
|
|
|
: EntityBase<EntityRef>
|
|
|
|
, IEquatable<EntityRef>
|
|
|
|
{
|
|
|
|
public override Universe Universe { get; }
|
|
|
|
public Entity Entity { get; }
|
|
|
|
public uint ID => Entity.ID;
|
|
|
|
|
|
|
|
public bool IsAlive => ecs_is_alive(Universe, this);
|
|
|
|
public EntityType Type => new(Universe, ecs_get_type(Universe, this));
|
|
|
|
|
|
|
|
public string? Name {
|
|
|
|
get => ecs_get_name(Universe, this).FlecsToString()!;
|
|
|
|
set { using var alloc = TempAllocator.Use();
|
|
|
|
ecs_set_name(Universe, this, alloc.AllocateCString(value)); }
|
|
|
|
}
|
|
|
|
public string? Symbol {
|
|
|
|
get => ecs_get_symbol(Universe, this).FlecsToString()!;
|
|
|
|
set { using var alloc = TempAllocator.Use();
|
|
|
|
ecs_set_symbol(Universe, this, alloc.AllocateCString(value)); }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private EntityRef(Universe universe, Entity entity, bool throwOnInvalid)
|
|
|
|
{
|
|
|
|
if (throwOnInvalid && !ecs_is_valid(universe, entity))
|
|
|
|
throw new InvalidOperationException($"The entity {entity} is not valid");
|
|
|
|
Universe = universe;
|
|
|
|
Entity = entity;
|
|
|
|
}
|
|
|
|
|
|
|
|
public EntityRef(Universe universe, Entity entity)
|
|
|
|
: this(universe, entity, true) { }
|
|
|
|
|
|
|
|
public static EntityRef? CreateOrNull(Universe universe, Entity entity)
|
|
|
|
=> ecs_is_valid(universe, entity) ? new(universe, entity) : null;
|
|
|
|
|
|
|
|
|
|
|
|
public void Delete()
|
|
|
|
=> ecs_delete(Universe, this);
|
|
|
|
|
|
|
|
public EntityBuilder NewChild(EntityPath? path = null)
|
|
|
|
=> Universe.New(EnsureRelativePath(path)).ChildOf(this);
|
|
|
|
public EntityRef? Lookup(EntityPath path)
|
|
|
|
=> Universe.Lookup(this, EnsureRelativePath(path)!);
|
|
|
|
public EntityRef LookupOrThrow(EntityPath path)
|
|
|
|
=> Universe.LookupOrThrow(this, EnsureRelativePath(path)!);
|
|
|
|
|
|
|
|
private static EntityPath? EnsureRelativePath(EntityPath? path)
|
|
|
|
{ if (path?.IsAbsolute == true) throw new ArgumentException("path must not be absolute", nameof(path)); return path; }
|
|
|
|
|
|
|
|
|
|
|
|
public EntityRef? Parent
|
|
|
|
=> GetTarget(Universe.ChildOf);
|
|
|
|
|
|
|
|
public IEnumerable<EntityRef> GetChildren()
|
|
|
|
{
|
|
|
|
foreach (var iter in Iterator.FromTerm(Universe, new(Universe.ChildOf, this)))
|
|
|
|
for (var i = 0; i < iter.Count; i++)
|
|
|
|
yield return iter.Entity(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override EntityRef Add(Identifier id) { ecs_add_id(Universe, this, id); return this; }
|
|
|
|
public override EntityRef Remove(Identifier id) { ecs_remove_id(Universe, this, id); return this; }
|
|
|
|
public override bool Has(Identifier id) => ecs_has_id(Universe, this, id);
|
|
|
|
|
|
|
|
public override T Get<T>()
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var ptr = ecs_get_id(Universe, this, comp);
|
|
|
|
if (ptr == null) throw new Exception($"Component {typeof(T)} not found on {this}");
|
|
|
|
return typeof(T).IsValueType ? Unsafe.Read<T>(ptr)
|
|
|
|
: (T)((GCHandle)Unsafe.Read<nint>(ptr)).Target!;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override T? MaybeGet<T>()
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var ptr = ecs_get_id(Universe, this, comp);
|
|
|
|
return (ptr != null) ? Unsafe.Read<T>(ptr) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override T? MaybeGet<T>(T _ = null!)
|
|
|
|
where T : class
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var ptr = ecs_get_id(Universe, this, comp);
|
|
|
|
return (ptr != null) ? (T)((GCHandle)Unsafe.Read<nint>(ptr)).Target! : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override ref T GetRefOrNull<T>()
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var @ref = ecs_ref_init_id(Universe, this, comp);
|
|
|
|
var ptr = ecs_ref_get_id(Universe, &@ref, comp);
|
|
|
|
return ref (ptr != null) ? ref Unsafe.AsRef<T>(ptr) : ref Unsafe.NullRef<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override ref T GetRefOrThrow<T>()
|
|
|
|
{
|
|
|
|
ref var ptr = ref GetRefOrNull<T>();
|
|
|
|
if (Unsafe.IsNullRef(ref ptr)) throw new Exception(
|
|
|
|
$"Component {typeof(T)} not found on {this}");
|
|
|
|
return ref ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override ref T GetMut<T>()
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var ptr = ecs_get_mut_id(Universe, this, comp);
|
|
|
|
// NOTE: Value is added if it doesn't exist on the entity.
|
|
|
|
return ref Unsafe.AsRef<T>(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void Modified<T>()
|
|
|
|
=> ecs_modified_id(Universe, this, Universe.LookupOrThrow<T>());
|
|
|
|
|
|
|
|
public override EntityRef Set<T>(in T value)
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var size = (ulong)Unsafe.SizeOf<T>();
|
|
|
|
fixed (T* ptr = &value)
|
|
|
|
if (ecs_set_id(Universe, this, comp, size, ptr).Data == 0)
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override EntityRef Set<T>(T obj) where T : class
|
|
|
|
{
|
|
|
|
var comp = Universe.LookupOrThrow<T>();
|
|
|
|
var handle = (nint)GCHandle.Alloc(obj);
|
|
|
|
// FIXME: Previous handle needs to be freed.
|
|
|
|
if (ecs_set_id(Universe, this, comp, (ulong)sizeof(nint), &handle).Data == 0)
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
// FIXME: Handle needs to be freed when component is removed!
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public EntityRef? GetTarget(Entity relation, int index = 0)
|
|
|
|
=> CreateOrNull(Universe, new(ecs_get_target(Universe, this, relation, index)));
|
|
|
|
public EntityRef? GetTarget(string symbol, int index = 0)
|
|
|
|
=> GetTarget(Universe.LookupSymbolOrThrow(symbol), index);
|
|
|
|
public EntityRef? GetTarget<T>(int index = 0)
|
|
|
|
=> GetTarget(Universe.LookupOrThrow(typeof(T)), index);
|
|
|
|
|
|
|
|
public bool Equals(EntityRef? other) => (other is not null) && (Universe == other.Universe) && (Entity == other.Entity);
|
|
|
|
public override bool Equals(object? obj) => Equals(obj as EntityRef);
|
|
|
|
public override int GetHashCode() => HashCode.Combine(Universe, Entity);
|
|
|
|
public override string? ToString() => ecs_entity_str(Universe, this).FlecsToStringAndFree()!;
|
|
|
|
|
|
|
|
public static bool operator ==(EntityRef? left, EntityRef? right) => ReferenceEquals(left, right) || (left?.Equals(right) ?? false);
|
|
|
|
public static bool operator !=(EntityRef? left, EntityRef? right) => !(left == right);
|
|
|
|
|
|
|
|
public static implicit operator Entity(EntityRef? e) => e?.Entity ?? default;
|
|
|
|
public static implicit operator ecs_entity_t(EntityRef? e) => e?.Entity.Value ?? default;
|
|
|
|
|
|
|
|
public static implicit operator Identifier(EntityRef? e) => new(e?.Entity.Value.Data ?? default);
|
|
|
|
public static implicit operator ecs_id_t(EntityRef? e) => e?.Entity.Value.Data ?? default;
|
|
|
|
}
|