NorbiPeti
d27bcee8d5
- Fixed compilation errors - Fixed patching errors and added missing anti-cheat patch - Added check to verify that the init data has been removed from blocks once they are placed in game - Removed block place event deduplication as it seems to be not needed anymore - Fixed async tests not properly running - Added Player.State - Attempted to fix seat entering/leaving (we can only send inputs in client code) - Fixed the weak dictionary ContainsKey returning true even if the item is no longer there
128 lines
No EOL
5.1 KiB
C#
128 lines
No EOL
5.1 KiB
C#
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 EGID Id { get; }
|
|
|
|
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances =
|
|
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>();
|
|
|
|
private static readonly WeakDictionary<EGID, EcsObjectBase> _noInstance =
|
|
new WeakDictionary<EGID, EcsObjectBase>();
|
|
|
|
internal static WeakDictionary<EGID, 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;
|
|
}
|
|
|
|
protected EcsObjectBase(EGID id)
|
|
{
|
|
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;
|
|
}
|
|
|
|
protected EcsObjectBase(Func<EcsObjectBase, EGID> initializer)
|
|
{
|
|
if (!_instances.TryGetValue(GetType(), out var dict))
|
|
{
|
|
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
|
_instances.Add(GetType(), dict);
|
|
}
|
|
|
|
var id = initializer(this);
|
|
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
|
dict.Add(id, this);
|
|
else
|
|
{
|
|
Logging.MetaDebugLog($"An object of this type and ID is already stored: {GetType()} - {id}");
|
|
Logging.MetaDebugLog(this);
|
|
Logging.MetaDebugLog(dict[id]);
|
|
}
|
|
|
|
Id = id;
|
|
}
|
|
|
|
#region ECS initializer stuff
|
|
|
|
protected internal EcsInitData InitData;
|
|
|
|
/// <summary>
|
|
/// Holds information needed to construct a component initializer
|
|
/// </summary>
|
|
protected internal struct EcsInitData
|
|
{
|
|
private FasterDictionary<RefWrapperType, ITypeSafeDictionary> 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<RefWrapperType, ITypeSafeDictionary> GetInitGroupFunc(
|
|
EntityInitializer initializer);
|
|
|
|
/// <summary>
|
|
/// Accesses the group field of the initializer
|
|
/// </summary>
|
|
private static GetInitGroupFunc GetInitGroup = CreateAccessor<GetInitGroupFunc>("_group");
|
|
|
|
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
|
|
private static TDelegate CreateAccessor<TDelegate>(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<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam });
|
|
return lambda.Compile();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |