|
|
@@ -1,81 +1,95 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq.Expressions; |
|
|
|
using Svelto.DataStructures; |
|
|
|
using Svelto.ECS; |
|
|
|
using Svelto.ECS.Internal; |
|
|
|
using TechbloxModdingAPI.Common.Engines; |
|
|
|
using TechbloxModdingAPI.Common.Utils; |
|
|
|
using TechbloxModdingAPI.Utility; |
|
|
|
|
|
|
|
namespace TechbloxModdingAPI |
|
|
|
namespace TechbloxModdingAPI.Common; |
|
|
|
|
|
|
|
public abstract class EcsObjectBase |
|
|
|
{ |
|
|
|
public abstract class EcsObjectBase |
|
|
|
{ |
|
|
|
public EGID Id { get; } |
|
|
|
public EGID Id => _engine.GetEgid(Reference); |
|
|
|
/// <summary> |
|
|
|
/// A reference to a specific entity that persists through group swaps and such. |
|
|
|
/// May be an invalid reference, in that case operations do not have any effect. |
|
|
|
/// </summary> |
|
|
|
public EntityReference Reference { get; } |
|
|
|
|
|
|
|
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances = new(); |
|
|
|
private static readonly Dictionary<Type, WeakDictionary<EntityReference, EcsObjectBase>> _instances = new(); |
|
|
|
private static readonly EcsObjectBaseEngine _engine = new(); |
|
|
|
|
|
|
|
internal static WeakDictionary<EGID, EcsObjectBase> GetInstances(Type type) |
|
|
|
{ |
|
|
|
return _instances.TryGetValue(type, out var dict) ? dict : null; |
|
|
|
} |
|
|
|
private static WeakDictionary<EntityReference, EcsObjectBase> GetInstances(Type type) |
|
|
|
{ |
|
|
|
return _instances.TryGetValue(type, out var dict) ? dict : null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// Returns a cached instance if there's an actively used instance of the object already. |
|
|
|
/// Objects still get garbage collected and then they will be removed from the cache. |
|
|
|
/// </summary> |
|
|
|
/// <param name="egid">The EGID of the entity</param> |
|
|
|
/// <param name="constructor">The constructor to construct the object</param> |
|
|
|
/// <typeparam name="T">The object type</typeparam> |
|
|
|
/// <returns></returns> |
|
|
|
internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase |
|
|
|
{ |
|
|
|
var instances = GetInstances(type ?? typeof(T)); |
|
|
|
if (instances == null || !instances.TryGetValue(egid, out var instance)) |
|
|
|
return constructor(egid); // It will be added by the constructor |
|
|
|
return (T)instance; |
|
|
|
} |
|
|
|
/// <summary> |
|
|
|
/// Returns a cached instance if there's an actively used instance of the object already. |
|
|
|
/// Objects still get garbage collected and then they will be removed from the cache. |
|
|
|
/// </summary> |
|
|
|
/// <param name="egid">The EGID of the entity</param> |
|
|
|
/// <param name="constructor">The constructor to construct the object</param> |
|
|
|
/// <typeparam name="T">The object type</typeparam> |
|
|
|
/// <returns></returns> |
|
|
|
internal static T GetInstance<T>(EGID egid, Func<EGID, T> constructor, Type type = null) where T : EcsObjectBase |
|
|
|
{ |
|
|
|
var instances = GetInstances(type ?? typeof(T)); |
|
|
|
if (instances == null || !instances.TryGetValue(_engine.GetEntityReference(egid), out var instance)) |
|
|
|
return constructor(egid); // It will be added by the constructor |
|
|
|
return (T)instance; |
|
|
|
} |
|
|
|
|
|
|
|
protected EcsObjectBase(EGID id, Type entityDescriptorType) |
|
|
|
protected EcsObjectBase(EGID id, Type entityDescriptorType) : this(_engine.GetEntityReference(id), entityDescriptorType) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
protected EcsObjectBase(EntityReference reference, Type entityDescriptorType) |
|
|
|
{ |
|
|
|
if (!_instances.TryGetValue(GetType(), out var dict)) |
|
|
|
{ |
|
|
|
if (!_instances.TryGetValue(GetType(), out var dict)) |
|
|
|
{ |
|
|
|
dict = new WeakDictionary<EGID, EcsObjectBase>(); |
|
|
|
_instances.Add(GetType(), dict); |
|
|
|
} |
|
|
|
if (!dict.ContainsKey(id)) // Multiple instances may be created |
|
|
|
dict.Add(id, this); |
|
|
|
Id = id; |
|
|
|
dict = new(); |
|
|
|
_instances.Add(GetType(), dict); |
|
|
|
} |
|
|
|
if (!dict.ContainsKey(reference)) // Multiple instances may be created |
|
|
|
dict.Add(reference, this); |
|
|
|
Reference = reference; |
|
|
|
} |
|
|
|
|
|
|
|
#region ECS initializer stuff |
|
|
|
#region ECS initializer stuff |
|
|
|
|
|
|
|
protected internal EcsInitData InitData; |
|
|
|
protected internal EcsInitData InitData; |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// Holds information needed to construct a component initializer. |
|
|
|
/// Necessary because the initializer is a ref struct which cannot be assigned to a field. |
|
|
|
/// </summary> |
|
|
|
protected internal struct EcsInitData |
|
|
|
{ |
|
|
|
private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group; |
|
|
|
private EntityReference reference; |
|
|
|
/// <summary> |
|
|
|
/// Holds information needed to construct a component initializer. |
|
|
|
/// Necessary because the initializer is a ref struct which cannot be assigned to a field. |
|
|
|
/// </summary> |
|
|
|
protected internal struct EcsInitData |
|
|
|
{ |
|
|
|
private FasterDictionary<RefWrapperType, ITypeSafeDictionary> group; |
|
|
|
private EntityReference reference; |
|
|
|
|
|
|
|
public static implicit operator EcsInitData(EntityInitializer initializer) => new() |
|
|
|
{ group = GetInitGroup(initializer), reference = initializer.reference }; |
|
|
|
public static implicit operator EcsInitData(EntityInitializer initializer) => new() |
|
|
|
{ group = GetInitGroup(initializer), reference = initializer.reference }; |
|
|
|
|
|
|
|
public EntityInitializer Initializer(EGID id) => new(id, group, reference); |
|
|
|
public bool Valid => group != null; |
|
|
|
} |
|
|
|
public EntityInitializer Initializer(EGID id) => new(id, group, reference); |
|
|
|
public bool Valid => group != null; |
|
|
|
} |
|
|
|
|
|
|
|
private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc( |
|
|
|
EntityInitializer initializer); |
|
|
|
private delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc( |
|
|
|
EntityInitializer initializer); |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// Accesses the group field of the initializer |
|
|
|
/// </summary> |
|
|
|
private static readonly GetInitGroupFunc GetInitGroup = Reflections.CreateAccessor<GetInitGroupFunc>("_group"); |
|
|
|
/// <summary> |
|
|
|
/// Accesses the group field of the initializer |
|
|
|
/// </summary> |
|
|
|
private static readonly GetInitGroupFunc GetInitGroup = Reflections.CreateAccessor<GetInitGroupFunc>("_group"); |
|
|
|
|
|
|
|
#endregion |
|
|
|
#endregion |
|
|
|
|
|
|
|
public static void Init() |
|
|
|
{ |
|
|
|
EngineManager.AddEngine(_engine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer); |
|
|
|
} |
|
|
|
} |