@ -1,17 +1,18 @@
using System ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
using static flecs_hub . flecs ;
namespace gaemstone.ECS ;
public static class ComponentExtensions
public static unsafe class ComponentExtensions
{
public unsafe static EntityRef InitComponent ( this EntityRef entity , Type type )
public static EntityRef InitComponent ( this EntityRef entity , Type type )
= > ( EntityRef ) typeof ( ComponentExtensions )
. GetMethod ( nameof ( InitComponent ) , new [ ] { typeof ( EntityRef ) } ) !
. MakeGenericMethod ( type ) . Invoke ( null , new [ ] { entity } ) ! ;
public unsafe static EntityRef InitComponent < T > ( this EntityRef entity )
public static EntityRef InitComponent < T > ( this EntityRef entity )
{
if ( typeof ( T ) . IsPrimitive ) throw new ArgumentException (
"Must not be primitive" ) ;
@ -19,12 +20,91 @@ public static class ComponentExtensions
"Struct component must satisfy the unmanaged constraint. " +
"Consider making it a class if you need to store references." ) ;
var size = Unsafe . SizeOf < T > ( ) ;
var typeInfo = new ecs_type_info_t
{ size = size , alignment = size } ;
var componentDesc = new ecs_component_desc_t
{ entity = entity , type = typeInfo } ;
var size = typeof ( T ) . IsValueType ? Unsafe . SizeOf < T > ( ) : sizeof ( ReferenceHandle ) ;
var typeInfo = new ecs_type_info_t { size = size , alignment = size } ;
var componentDesc = new ecs_component_desc_t { entity = entity , type = typeInfo } ;
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 ) ) ;
}
}
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 ( ) ;
}
}
}