Alternative managed wrapper around flecs-cs bindings for using the ECS framework Flecs in modern .NET.
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.

117 lines
3.9 KiB

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using gaemstone.ECS.Utility;
using static flecs_hub.flecs;
namespace gaemstone.ECS.Internal;
public unsafe static class EntityAccess
{
public static bool IsValid(World world, Entity entity)
=> ecs_is_valid(world, entity);
public static bool IsAlive(World world, Entity entity)
=> ecs_is_alive(world, entity);
public static string? GetName(World world, Entity entity)
=> ecs_get_name(world, entity).FlecsToString()!;
public static void SetName(World world, Entity entity, string? value)
{ using var alloc = TempAllocator.Use(); ecs_set_name(world, entity, alloc.AllocateCString(value)); }
public static string? GetSymbol(World world, Entity entity)
=> ecs_get_symbol(world, entity).FlecsToString()!;
public static void SetSymbol(World world, Entity entity, string? value)
{ using var alloc = TempAllocator.Use(); ecs_set_symbol(world, entity, alloc.AllocateCString(value)); }
public static void Add(World world, Entity entity, Id id)
=> ecs_add_id(world, entity, id);
public static void Remove(World world, Entity entity, Id id)
=> ecs_remove_id(world, entity, id);
public static bool Has(World world, Entity entity, Id id)
=> ecs_has_id(world, entity, id);
public static IEnumerable<Entity> GetTargets(World world, Entity entity, Entity relation)
{
Entity GetTarget(int index)
=> new(ecs_get_target(world, entity, relation, index));
for (var i = 0; GetTarget(i) is { IsSome: true } target; i++)
yield return target;
}
public static T? GetOrNull<T>(World world, Entity entity, Id id)
where T : unmanaged
{
var ptr = ecs_get_id(world, entity, id);
return (ptr != null) ? Unsafe.Read<T>(ptr) : null;
}
public static T? GetOrNull<T>(World world, Entity entity, Id id, T _ = null!)
where T : class
{
var ptr = ecs_get_id(world, entity, id);
return (T?)Unsafe.Read<ReferenceHandle>(ptr).Target;
}
public static T GetOrThrow<T>(World world, Entity entity, Id id)
{
var ptr = ecs_get_id(world, entity, id);
if (ptr == null) throw new ComponentNotFoundException($"Component {id} not found on {entity}");
if (typeof(T).IsValueType) return Unsafe.Read<T>(ptr);
else return (T)Unsafe.Read<ReferenceHandle>(ptr).Target!;
}
public static ref T GetMut<T>(World world, Entity entity, Id id)
where T : unmanaged
{
var ptr = ecs_get_mut_id(world, entity, id);
// NOTE: Value is added if it doesn't exist on the entity.
return ref Unsafe.AsRef<T>(ptr);
}
public static ref T GetRefOrNull<T>(World world, Entity entity, Id id)
where T : unmanaged
{
var @ref = ecs_ref_init_id(world, entity, id);
var ptr = ecs_ref_get_id(world, &@ref, id);
return ref (ptr != null) ? ref Unsafe.AsRef<T>(ptr)
: ref Unsafe.NullRef<T>();
}
public static ref T GetRefOrThrow<T>(World world, Entity entity, Id id)
where T : unmanaged
{
ref var ptr = ref GetRefOrNull<T>(world, entity, id);
if (Unsafe.IsNullRef(ref ptr)) throw new Exception(
$"Component {typeof(T)} not found on {entity}");
return ref ptr;
}
public static void Modified(World world, Entity entity, Id id)
=> ecs_modified_id(world, entity, id);
public static Entity Set<T>(World world, Entity entity, Id id, in T value)
where T : unmanaged
{
var size = (ulong)Unsafe.SizeOf<T>();
fixed (T* ptr = &value)
if (ecs_set_id(world, entity, id, size, ptr).Data == 0)
throw new InvalidOperationException();
return entity;
}
public static Entity Set<T>(World world, Entity entity, Id id, T value)
where T : class
{
if (value == null) throw new ArgumentNullException(nameof(value));
var size = (ulong)sizeof(ReferenceHandle);
// Dispose entity handle afterwards, since Flecs clones it.
using var handle = ReferenceHandle.Alloc(value);
if (ecs_set_id(world, entity, id, size, &handle).Data == 0)
throw new InvalidOperationException();
return entity;
}
}