|
|
@ -1,18 +1,17 @@ |
|
|
|
using System; |
|
|
|
using System; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
using static flecs_hub.flecs; |
|
|
|
using static flecs_hub.flecs; |
|
|
|
|
|
|
|
|
|
|
|
namespace gaemstone.ECS; |
|
|
|
namespace gaemstone.ECS; |
|
|
|
|
|
|
|
|
|
|
|
public static unsafe class ComponentExtensions |
|
|
|
public static class ComponentExtensions |
|
|
|
{ |
|
|
|
{ |
|
|
|
public static EntityRef InitComponent(this EntityRef entity, Type type) |
|
|
|
public unsafe static EntityRef InitComponent(this EntityRef entity, Type type) |
|
|
|
=> (EntityRef)typeof(ComponentExtensions) |
|
|
|
=> (EntityRef)typeof(ComponentExtensions) |
|
|
|
.GetMethod(nameof(InitComponent), new[] { typeof(EntityRef) })! |
|
|
|
.GetMethod(nameof(InitComponent), new[] { typeof(EntityRef) })! |
|
|
|
.MakeGenericMethod(type).Invoke(null, new[]{ entity })!; |
|
|
|
.MakeGenericMethod(type).Invoke(null, new[]{ entity })!; |
|
|
|
|
|
|
|
|
|
|
|
public static EntityRef InitComponent<T>(this EntityRef entity) |
|
|
|
public unsafe static EntityRef InitComponent<T>(this EntityRef entity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (typeof(T).IsPrimitive) throw new ArgumentException( |
|
|
|
if (typeof(T).IsPrimitive) throw new ArgumentException( |
|
|
|
"Must not be primitive"); |
|
|
|
"Must not be primitive"); |
|
|
@ -20,91 +19,12 @@ public static unsafe class ComponentExtensions |
|
|
|
"Struct component must satisfy the unmanaged constraint. " + |
|
|
|
"Struct component must satisfy the unmanaged constraint. " + |
|
|
|
"Consider making it a class if you need to store references."); |
|
|
|
"Consider making it a class if you need to store references."); |
|
|
|
|
|
|
|
|
|
|
|
var size = typeof(T).IsValueType ? Unsafe.SizeOf<T>() : sizeof(ReferenceHandle); |
|
|
|
var size = Unsafe.SizeOf<T>(); |
|
|
|
var typeInfo = new ecs_type_info_t { size = size, alignment = size }; |
|
|
|
var typeInfo = new ecs_type_info_t |
|
|
|
var componentDesc = new ecs_component_desc_t { entity = entity, type = typeInfo }; |
|
|
|
{ size = size, alignment = size }; |
|
|
|
|
|
|
|
var componentDesc = new ecs_component_desc_t |
|
|
|
|
|
|
|
{ entity = entity, type = typeInfo }; |
|
|
|
ecs_component_init(entity.World, &componentDesc); |
|
|
|
ecs_component_init(entity.World, &componentDesc); |
|
|
|
|
|
|
|
|
|
|
|
if (!typeof(T).IsValueType) { |
|
|
|
|
|
|
|
// Set up component hooks for proper freeing of GCHandles. |
|
|
|
|
|
|
|
// Without them, managed classes would never be garbage collected. |
|
|
|
|
|
|
|
var typeHooks = new ecs_type_hooks_t { |
|
|
|
|
|
|
|
ctor = new() { Data = new() { Pointer = &ReferenceHandle.Construct } }, |
|
|
|
|
|
|
|
dtor = new() { Data = new() { Pointer = &ReferenceHandle.Destruct } }, |
|
|
|
|
|
|
|
move = new() { Data = new() { Pointer = &ReferenceHandle.Move } }, |
|
|
|
|
|
|
|
copy = new() { Data = new() { Pointer = &ReferenceHandle.Copy } }, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
ecs_set_hooks_id(entity.World, entity, &typeHooks); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return entity.CreateLookup(typeof(T)); |
|
|
|
return entity.CreateLookup(typeof(T)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public unsafe readonly struct ReferenceHandle |
|
|
|
|
|
|
|
: IDisposable |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
public static int NumActiveHandles { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private readonly nint _value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public object? Target => |
|
|
|
|
|
|
|
(_value != default) |
|
|
|
|
|
|
|
? ((GCHandle)_value).Target |
|
|
|
|
|
|
|
: null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ReferenceHandle(nint value) |
|
|
|
|
|
|
|
=> _value = value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static ReferenceHandle Alloc(object? target) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (target == null) return default; |
|
|
|
|
|
|
|
NumActiveHandles++; |
|
|
|
|
|
|
|
return new((nint)GCHandle.Alloc(target)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ReferenceHandle Clone() |
|
|
|
|
|
|
|
=> Alloc(Target); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (_value == default) return; |
|
|
|
|
|
|
|
NumActiveHandles--; |
|
|
|
|
|
|
|
((GCHandle)_value).Free(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[UnmanagedCallersOnly] |
|
|
|
|
|
|
|
internal static void Construct(void* ptr, int count, ecs_type_info_t* _) |
|
|
|
|
|
|
|
=> new Span<ReferenceHandle>(ptr, count).Clear(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[UnmanagedCallersOnly] |
|
|
|
|
|
|
|
internal static void Destruct(void* ptr, int count, ecs_type_info_t* _) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var span = new Span<ReferenceHandle>(ptr, count); |
|
|
|
|
|
|
|
foreach (var handle in span) handle.Dispose(); |
|
|
|
|
|
|
|
span.Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[UnmanagedCallersOnly] |
|
|
|
|
|
|
|
internal static void Move(void* dstPtr, void* srcPtr, int count, ecs_type_info_t* _) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var dst = new Span<ReferenceHandle>(dstPtr, count); |
|
|
|
|
|
|
|
var src = new Span<ReferenceHandle>(srcPtr, count); |
|
|
|
|
|
|
|
foreach (var handle in dst) handle.Dispose(); |
|
|
|
|
|
|
|
src.CopyTo(dst); |
|
|
|
|
|
|
|
src.Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[UnmanagedCallersOnly] |
|
|
|
|
|
|
|
internal static void Copy(void* dstPtr, void* srcPtr, int count, ecs_type_info_t* _) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var dst = new Span<ReferenceHandle>(dstPtr, count); |
|
|
|
|
|
|
|
var src = new Span<ReferenceHandle>(srcPtr, count); |
|
|
|
|
|
|
|
for (var i = 0; i < count; i++) { |
|
|
|
|
|
|
|
dst[i].Dispose(); |
|
|
|
|
|
|
|
dst[i] = src[i].Clone(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|