DragonECS/src/EcsRunner.cs

249 lines
9.1 KiB
C#
Raw Normal View History

using System;
2023-03-26 11:19:03 +08:00
using System.Collections;
using System.Collections.Generic;
2023-03-26 11:19:03 +08:00
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
2023-03-12 01:33:48 +08:00
sealed class EcsRunnerFilterAttribute : Attribute
{
public readonly Type interfaceType;
public readonly object filter;
2023-03-12 01:33:48 +08:00
public EcsRunnerFilterAttribute(Type interfaceType, object filter)
{
this.interfaceType = interfaceType;
this.filter = filter;
}
2023-03-30 01:57:10 +08:00
public EcsRunnerFilterAttribute(object filter) : this(null, filter) { }
}
2023-03-12 20:45:18 +08:00
public interface IEcsSystem { }
2023-03-26 11:19:03 +08:00
public interface IEcsRunner
{
public EcsSystems Source { get; }
2023-03-26 11:19:03 +08:00
public IList Targets { get; }
public object Filter { get; }
public bool IsHasFilter { get; }
}
2023-03-16 01:49:14 +08:00
2023-03-12 01:33:48 +08:00
internal static class EcsRunnerActivator
{
2023-03-26 11:19:03 +08:00
private static Dictionary<Guid, Type> _runnerHandlerTypes; //interface guid/Runner handler type pairs;
2023-03-12 02:02:39 +08:00
static EcsRunnerActivator()
{
2023-03-30 01:57:10 +08:00
List<Exception> delayedExceptions = new List<Exception>();
2023-03-12 02:02:39 +08:00
Type runnerBaseType = typeof(EcsRunner<>);
2023-03-26 11:19:03 +08:00
List<Type> runnerHandlerTypes = new List<Type>();
2023-03-29 19:40:18 +08:00
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
2023-03-30 01:57:10 +08:00
runnerHandlerTypes.AddRange(assembly.GetTypes()
2023-03-29 19:40:18 +08:00
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
}
2023-03-12 02:02:39 +08:00
2023-03-26 11:19:03 +08:00
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
for (int i = 0; i < runnerHandlerTypes.Count; i++)
2023-03-12 02:02:39 +08:00
{
2023-03-26 11:19:03 +08:00
var e = CheckRunnerValide(runnerHandlerTypes[i]);
2023-03-12 02:02:39 +08:00
if (e != null)
{
2023-03-26 11:19:03 +08:00
runnerHandlerTypes.RemoveAt(i--);
2023-03-30 01:57:10 +08:00
delayedExceptions.Add(e);
2023-03-12 02:02:39 +08:00
}
}
#endif
2023-03-26 11:19:03 +08:00
_runnerHandlerTypes = new Dictionary<Guid, Type>();
foreach (var item in runnerHandlerTypes)
2023-03-12 02:02:39 +08:00
{
Type interfaceType = item.BaseType.GenericTypeArguments[0];
2023-03-26 11:19:03 +08:00
_runnerHandlerTypes.Add(interfaceType.GUID, item);
2023-03-12 02:02:39 +08:00
}
2023-03-30 01:57:10 +08:00
if (delayedExceptions.Count > 0)
2023-03-12 02:02:39 +08:00
{
2023-03-30 01:57:10 +08:00
foreach (var item in delayedExceptions) EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, item.Message);
throw delayedExceptions[0];
2023-03-12 02:02:39 +08:00
}
}
private static Exception CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера
{
Type baseType = type.BaseType;
Type baseTypeArgument = baseType.GenericTypeArguments[0];
2023-03-12 02:02:39 +08:00
if (type.ReflectedType != null)
{
2023-03-30 01:57:10 +08:00
return new EcsRunnerImplementationException($"{type.FullName}.ReflectedType must be Null, but equal to {type.ReflectedType.FullName}.");
}
if (!baseTypeArgument.IsInterface)
{
2023-03-30 01:57:10 +08:00
return new EcsRunnerImplementationException($"Argument T of class EcsRunner<T>, can only be an inetrface.The {baseTypeArgument.FullName} type is not an interface.");
}
var interfaces = type.GetInterfaces();
2023-03-12 02:02:39 +08:00
if (!interfaces.Any(o => o == baseTypeArgument))
{
2023-03-30 01:57:10 +08:00
return new EcsRunnerImplementationException($"Runner {type.FullName} does not implement interface {baseTypeArgument.FullName}.");
}
2023-03-12 02:02:39 +08:00
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-03-16 01:49:14 +08:00
internal static void InitFor<TInterface>() where TInterface : IEcsSystem
{
2023-03-12 02:02:39 +08:00
Type interfaceType = typeof(TInterface);
2023-03-12 02:48:51 +08:00
Type nonGenericInterfaceType = interfaceType;
if (nonGenericInterfaceType.IsGenericType)
{
nonGenericInterfaceType = nonGenericInterfaceType.GetGenericTypeDefinition();
}
Guid interfaceGuid = nonGenericInterfaceType.GUID;
2023-03-12 02:02:39 +08:00
2023-03-26 11:19:03 +08:00
if (!_runnerHandlerTypes.TryGetValue(interfaceGuid, out Type runnerType))
2023-03-12 02:02:39 +08:00
{
throw new Exception();
}
if (interfaceType.IsGenericType)
{
2023-03-12 02:02:39 +08:00
Type[] genericTypes = interfaceType.GetGenericArguments();
runnerType = runnerType.MakeGenericType(genericTypes);
}
2023-03-26 11:19:03 +08:00
EcsRunner<TInterface>.Register(runnerType);
}
}
2023-03-12 20:45:18 +08:00
public abstract class EcsRunner<TInterface> : IEcsSystem, IEcsRunner
where TInterface : IEcsSystem
{
2023-03-26 11:19:03 +08:00
#region Register
private static Type _subclass;
internal static void Register(Type subclass)
{
2023-03-16 01:49:14 +08:00
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
if (_subclass != null)
{
2023-03-30 01:57:10 +08:00
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");
}
Type interfaceType = typeof(TInterface);
var interfaces = interfaceType.GetInterfaces();
if (interfaceType.IsInterface == false)
{
throw new ArgumentException($"{typeof(TInterface).FullName} is not interface");
}
2023-03-12 20:45:18 +08:00
if (interfaces.Length != 1 || interfaces[0] != typeof(IEcsSystem))
{
2023-03-12 20:45:18 +08:00
throw new ArgumentException($"{typeof(TInterface).FullName} does not directly inherit the {nameof(IEcsSystem)} interface");
}
#endif
_subclass = subclass;
}
2023-03-26 11:19:03 +08:00
#endregion
2023-03-26 11:19:03 +08:00
#region FilterSystems
private static TInterface[] FilterSystems(IEnumerable<IEcsSystem> targets)
{
return targets.Where(o => o is TInterface).Select(o => (TInterface)o).ToArray();
}
private static TInterface[] FilterSystems(IEnumerable<IEcsSystem> targets, object filter)
{
Type interfaceType = typeof(TInterface);
2023-03-12 20:45:18 +08:00
IEnumerable<IEcsSystem> newTargets;
if (filter != null)
{
2023-03-12 02:02:39 +08:00
newTargets = targets.Where(o =>
{
if (o is TInterface == false) return false;
2023-03-12 01:33:48 +08:00
var atr = o.GetType().GetCustomAttribute<EcsRunnerFilterAttribute>();
return atr != null && atr.interfaceType == interfaceType && atr.filter.Equals(filter);
});
}
else
{
2023-03-12 02:02:39 +08:00
newTargets = targets.Where(o =>
{
if (o is TInterface == false) return false;
2023-03-12 01:33:48 +08:00
var atr = o.GetType().GetCustomAttribute<EcsRunnerFilterAttribute>();
return atr == null || atr.interfaceType == interfaceType && atr.filter == null;
});
}
2023-03-26 11:19:03 +08:00
return newTargets.Select(o => (TInterface)o).ToArray();
}
2023-03-26 11:19:03 +08:00
#endregion
#region Instantiate
2023-03-29 15:09:00 +08:00
private static TInterface Instantiate(EcsSystems source, TInterface[] targets, bool isHasFilter, object filter)
{
2023-03-12 02:02:39 +08:00
if (_subclass == null)
EcsRunnerActivator.InitFor<TInterface>();
2023-03-12 01:33:48 +08:00
var instance = (EcsRunner<TInterface>)Activator.CreateInstance(_subclass);
2023-03-29 15:09:00 +08:00
return (TInterface)(IEcsSystem)instance.Set(source, targets, isHasFilter, filter);
}
2023-03-29 15:09:00 +08:00
public static TInterface Instantiate(EcsSystems source)
2023-03-26 11:19:03 +08:00
{
2023-03-29 15:09:00 +08:00
return Instantiate(source, FilterSystems(source.AllSystems), false, null);
2023-03-26 11:19:03 +08:00
}
2023-03-29 15:09:00 +08:00
public static TInterface Instantiate(EcsSystems source, object filter)
2023-03-26 11:19:03 +08:00
{
2023-03-29 15:09:00 +08:00
return Instantiate(source, FilterSystems(source.AllSystems, filter), true, filter);
2023-03-26 11:19:03 +08:00
}
#endregion
2023-03-12 02:02:39 +08:00
2023-03-29 15:09:00 +08:00
private EcsSystems _source;
protected TInterface[] targets;
2023-03-26 11:19:03 +08:00
private ReadOnlyCollection<TInterface> _targetsSealed;
private object _filter;
private bool _isHasFilter;
2023-03-30 01:57:10 +08:00
#region Properties
2023-03-29 15:09:00 +08:00
public EcsSystems Source => _source;
2023-03-26 11:19:03 +08:00
public IList Targets => _targetsSealed;
public object Filter => _filter;
public bool IsHasFilter => _isHasFilter;
2023-03-30 01:57:10 +08:00
#endregion
2023-03-27 17:34:12 +08:00
2023-03-29 15:09:00 +08:00
private EcsRunner<TInterface> Set(EcsSystems source, TInterface[] targets, bool isHasFilter, object filter)
{
2023-03-29 15:09:00 +08:00
_source = source;
this.targets = targets;
2023-03-26 11:19:03 +08:00
_targetsSealed = new ReadOnlyCollection<TInterface>(targets);
_filter = filter;
_isHasFilter = isHasFilter;
2023-03-27 17:34:12 +08:00
OnSetup();
return this;
}
2023-03-26 11:19:03 +08:00
2023-03-27 17:34:12 +08:00
protected virtual void OnSetup() { }
2023-03-29 15:09:00 +08:00
internal void Rebuild()
2023-03-26 11:19:03 +08:00
{
if(_isHasFilter)
2023-03-29 15:09:00 +08:00
Set(_source, FilterSystems(_source.AllSystems), _isHasFilter, _filter);
2023-03-26 11:19:03 +08:00
else
2023-03-29 15:09:00 +08:00
Set(_source, FilterSystems(_source.AllSystems, _filter), _isHasFilter, _filter);
2023-03-26 11:19:03 +08:00
}
}
2023-03-30 01:57:10 +08:00
#region Extensions
public static class IEcsSystemExtensions
{
public static bool IsRunner(this IEcsSystem self)
{
return self is IEcsRunner;
}
}
#endregion
}