133 lines
No EOL
4.7 KiB
C#
133 lines
No EOL
4.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Svelto.DataStructures;
|
|
using Svelto.ECS;
|
|
using Svelto.ECS.Hybrid;
|
|
using Svelto.ECS.Internal;
|
|
using TechbloxModdingAPI.Common.Engines;
|
|
using TechbloxModdingAPI.Common.Utils;
|
|
using TechbloxModdingAPI.Utility;
|
|
|
|
namespace TechbloxModdingAPI.Common;
|
|
|
|
public abstract class EcsObjectBase
|
|
{
|
|
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; }
|
|
|
|
/// <summary>
|
|
/// Whether the entity reference is still valid. Returns false if this object no longer exists.
|
|
/// </summary>
|
|
public bool Exists => Id != default; // TODO: Might need extra code to support IDs during init
|
|
|
|
private static readonly Dictionary<Type, WeakDictionary<EntityReference, EcsObjectBase>> _instances = new();
|
|
private static readonly EcsObjectBaseEngine _engine = new();
|
|
|
|
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(_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) : this(_engine.GetEntityReference(id), entityDescriptorType)
|
|
{
|
|
}
|
|
|
|
protected EcsObjectBase(EntityReference reference, Type entityDescriptorType)
|
|
{
|
|
if (!_instances.TryGetValue(GetType(), out var dict))
|
|
{
|
|
dict = new();
|
|
_instances.Add(GetType(), dict);
|
|
}
|
|
if (!dict.ContainsKey(reference)) // Multiple instances may be created
|
|
dict.Add(reference, this);
|
|
Reference = reference;
|
|
}
|
|
|
|
|
|
|
|
protected internal OptionalRef<T> GetComponentOptional<T>() where T : unmanaged, IEntityComponent
|
|
{
|
|
return _engine.GetComponentOptional<T>(this);
|
|
}
|
|
|
|
protected internal ref T GetComponent<T>() where T : unmanaged, IEntityComponent
|
|
{
|
|
return ref _engine.GetComponent<T>(this);
|
|
}
|
|
|
|
protected internal ref T GetViewComponent<T>() where T : struct, IEntityViewComponent
|
|
{
|
|
return ref _engine.GetViewComponent<T>(this);
|
|
}
|
|
|
|
protected internal object GetComponent(Type type, string name)
|
|
{
|
|
return _engine.GetComponent(this, type, name);
|
|
}
|
|
|
|
protected internal void SetComponent(Type type, string name, object value)
|
|
{
|
|
_engine.SetComponent(this, type, name, value);
|
|
}
|
|
|
|
protected bool RemoveEntity()
|
|
{
|
|
// TODO: _entityFunctions.Remove...()
|
|
}
|
|
|
|
#region ECS initializer stuff
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
|
|
#endregion
|
|
|
|
public static void Init()
|
|
{
|
|
EngineManager.AddEngine(_engine, ApiEngineType.Build, ApiEngineType.Menu, ApiEngineType.PlayClient, ApiEngineType.PlayServer);
|
|
}
|
|
} |