using System; using System.Collections.Generic; using System.Linq.Expressions; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Internal; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI { public abstract class EcsObjectBase { public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor private static readonly Dictionary> _instances = new Dictionary>(); private static readonly WeakDictionary _noInstance = new WeakDictionary(); internal static WeakDictionary GetInstances(Type type) { return _instances.TryGetValue(type, out var dict) ? dict : null; } protected EcsObjectBase() { if (!_instances.TryGetValue(GetType(), out var dict)) { dict = new WeakDictionary(); _instances.Add(GetType(), dict); } // ReSharper disable once VirtualMemberCallInConstructor // The ID should not depend on the constructor dict.Add(Id, this); } #region ECS initializer stuff protected internal EcsInitData InitData; /// /// Holds information needed to construct a component initializer /// protected internal struct EcsInitData { private FasterDictionary group; private EntityReference reference; public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData { group = GetInitGroup(initializer), reference = initializer.reference }; public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference); public bool Valid => group != null; } private delegate FasterDictionary GetInitGroupFunc( EntityInitializer initializer); /// /// Accesses the group field of the initializer /// private static GetInitGroupFunc GetInitGroup = CreateAccessor("_group"); //https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection private static TDelegate CreateAccessor(string memberName) where TDelegate : Delegate { var invokeMethod = typeof(TDelegate).GetMethod("Invoke"); if (invokeMethod == null) throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined."); var delegateParameters = invokeMethod.GetParameters(); if (delegateParameters.Length != 1) throw new InvalidOperationException("Delegate must have a single parameter."); var paramType = delegateParameters[0].ParameterType; var objParam = Expression.Parameter(paramType, "obj"); var memberExpr = Expression.PropertyOrField(objParam, memberName); Expression returnExpr = memberExpr; if (invokeMethod.ReturnType != memberExpr.Type) returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType); var lambda = Expression.Lambda(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam }); return lambda.Compile(); } #endregion } }