DragonECS/src/EcsRunner.cs

299 lines
11 KiB
C#
Raw Normal View History

using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections.Generic;
using System.Linq;
2023-11-08 15:15:10 +08:00
using System.Text.RegularExpressions;
2023-05-26 04:25:09 +08:00
using static DCFApixels.DragonECS.EcsDebugUtility;
namespace DCFApixels.DragonECS
{
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve]
#endif
[AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
2023-12-06 20:38:00 +08:00
public sealed class BindWithEcsRunnerAttribute : Attribute
{
2023-12-06 20:38:00 +08:00
private static readonly Type _baseType = typeof(EcsRunner<>);
public readonly Type runnerType;
2023-12-06 20:38:00 +08:00
public BindWithEcsRunnerAttribute(Type runnerType)
{
if (runnerType == null)
{
throw new ArgumentNullException();
}
if (!CheckSubclass(runnerType))
{
throw new ArgumentException();
}
this.runnerType = runnerType;
}
private bool CheckSubclass(Type type)
{
2023-12-06 20:38:00 +08:00
if (type.IsGenericType && type.GetGenericTypeDefinition() == _baseType)
{
return true;
}
if (type.BaseType != null)
{
return CheckSubclass(type.BaseType);
}
return false;
}
}
public interface IEcsSystem { }
2023-03-16 01:49:14 +08:00
2023-04-23 17:55:01 +08:00
namespace RunnersCore
{
2023-04-23 17:55:01 +08:00
public interface IEcsRunner
{
2024-01-26 02:26:17 +08:00
EcsPipeline Pipeline { get; }
2023-05-26 04:25:09 +08:00
Type Interface { get; }
EcsProcessRaw ProcessRaw { get; }
2023-05-26 04:25:09 +08:00
bool IsDestroyed { get; }
bool IsEmpty { get; }
void Destroy();
2023-04-23 17:55:01 +08:00
}
2023-03-12 02:02:39 +08:00
2023-05-28 05:53:08 +08:00
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve]
#endif
public abstract class EcsRunner<TProcess> : IEcsSystem, IEcsRunner
where TProcess : IEcsSystem
{
2023-04-23 17:55:01 +08:00
#region Register
private static Type _runnerImplementationType;
2023-04-23 17:55:01 +08:00
internal static void Register(Type subclass)
{
2023-06-02 01:20:46 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_runnerImplementationType != null)
2023-05-26 04:25:09 +08:00
{
throw new EcsRunnerImplementationException($"The Runner<{typeof(TProcess).FullName}> can have only one implementing subclass");
2023-05-26 04:25:09 +08:00
}
Type interfaceType = typeof(TProcess);
2023-05-26 04:25:09 +08:00
var interfaces = interfaceType.GetInterfaces();
if (interfaceType.IsInterface == false)
{
throw new ArgumentException($"{typeof(TProcess).FullName} is not interface");
2023-05-26 04:25:09 +08:00
}
if (interfaces.Length != 1 || interfaces[0] != typeof(IEcsSystem))
2023-05-26 04:25:09 +08:00
{
throw new ArgumentException($"{typeof(TProcess).FullName} does not directly inherit the {nameof(IEcsSystem)} interface");
2023-05-26 04:25:09 +08:00
}
#endif
_runnerImplementationType = subclass;
}
2023-04-23 17:55:01 +08:00
#endregion
2023-03-26 11:19:03 +08:00
2023-04-23 17:55:01 +08:00
#region Instantiate
private static void CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера
{
Type targetInterface = typeof(TProcess);
if (type.IsAbstract)
{
throw new Exception();
}
2024-01-08 13:39:38 +08:00
Type GetRunnerBaseType(Type inType)
{
2024-01-08 13:39:38 +08:00
if (inType.IsGenericType && inType.GetGenericTypeDefinition() == typeof(EcsRunner<>))
return inType;
if (inType.BaseType != null)
return GetRunnerBaseType(inType.BaseType);
return null;
}
Type baseType = GetRunnerBaseType(type);
Type baseTypeArgument = baseType.GenericTypeArguments[0];
if (baseTypeArgument != targetInterface)
{
throw new Exception();
}
if (!type.GetInterfaces().Any(o => o == targetInterface))
{
throw new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}.");
}
}
public static TProcess Instantiate(EcsPipeline source)
2023-04-23 17:55:01 +08:00
{
EcsProcess<TProcess> process = source.GetProcess<TProcess>();
if (_runnerImplementationType == null)
{
Type interfaceType = typeof(TProcess);
if (interfaceType.TryGetCustomAttribute(out BindWithEcsRunnerAttribute atr))
{
Type runnerImplementationType = atr.runnerType;
if (interfaceType.IsGenericType)
{
Type[] genericTypes = interfaceType.GetGenericArguments();
runnerImplementationType = runnerImplementationType.MakeGenericType(genericTypes);
}
CheckRunnerValide(runnerImplementationType);
_runnerImplementationType = runnerImplementationType;
}
else
{
throw new EcsFrameworkException("Процесс не связан с раннером, используйте атрибуут BindWithEcsRunner(Type runnerType)");
}
}
var instance = (EcsRunner<TProcess>)Activator.CreateInstance(_runnerImplementationType);
return (TProcess)(IEcsSystem)instance.Set(source, process);
2023-04-23 17:55:01 +08:00
}
#endregion
2023-12-20 23:21:10 +08:00
2023-04-23 17:55:01 +08:00
private EcsPipeline _source;
private EcsProcess<TProcess> _process;
2023-04-23 17:55:01 +08:00
private bool _isDestroyed;
2023-03-26 11:19:03 +08:00
2023-04-23 17:55:01 +08:00
#region Properties
public EcsPipeline Pipeline
{
get { return _source; }
}
public Type Interface
{
get { return typeof(TProcess); }
}
public EcsProcessRaw ProcessRaw
{
get { return _process; }
}
public EcsProcess<TProcess> Process
{
get { return _process; }
}
public bool IsDestroyed
{
get { return _isDestroyed; }
}
public bool IsEmpty
{
get { return _process.IsNullOrEmpty;}
}
2023-04-23 17:55:01 +08:00
#endregion
2023-03-27 17:34:12 +08:00
private EcsRunner<TProcess> Set(EcsPipeline source, EcsProcess<TProcess> process)
2023-04-23 17:55:01 +08:00
{
_source = source;
this._process = process;
2023-04-23 17:55:01 +08:00
OnSetup();
return this;
}
internal void Rebuild()
{
Set(_source, _source.GetProcess<TProcess>());
2023-04-23 17:55:01 +08:00
}
public void Destroy()
{
_isDestroyed = true;
_source.OnRunnerDestroy_Internal(this);
2023-04-23 17:55:01 +08:00
_source = null;
_process = EcsProcess<TProcess>.Empty;
2023-04-23 17:55:01 +08:00
OnDestroy();
}
protected virtual void OnSetup() { } //TODO rename to OnInitialize
2023-04-23 17:55:01 +08:00
protected virtual void OnDestroy() { }
}
}
2023-05-26 04:25:09 +08:00
2023-03-30 01:57:10 +08:00
#region Extensions
2023-04-23 17:55:01 +08:00
public static class EcsRunner
{
public static void Destroy(IEcsSystem runner) => ((IEcsRunner)runner).Destroy();
2023-04-23 17:55:01 +08:00
}
2023-03-30 01:57:10 +08:00
public static class IEcsSystemExtensions
{
public static bool IsRunner(this IEcsSystem self)
2023-03-30 01:57:10 +08:00
{
return self is IEcsRunner;
}
}
#endregion
2023-11-08 15:15:10 +08:00
public static class EcsProcessUtility
{
private struct ProcessInterface
{
public Type interfaceType;
public string processName;
public ProcessInterface(Type interfaceType, string processName)
{
this.interfaceType = interfaceType;
this.processName = processName;
}
}
private static Dictionary<Type, ProcessInterface> _processes = new Dictionary<Type, ProcessInterface>();
private static HashSet<Type> _systems = new HashSet<Type>();
static EcsProcessUtility()
{
Type processBasicInterface = typeof(IEcsSystem);
2023-11-08 15:15:10 +08:00
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.GetInterface(nameof(IEcsSystem)) != null || type == processBasicInterface)
2023-11-08 15:15:10 +08:00
{
if (type.IsInterface)
{
string name = type.Name;
if (name[0] == 'I' && name.Length > 1 && char.IsUpper(name[1]))
name = name.Substring(1);
name = Regex.Replace(name, @"\bEcs|Process\b", "");
if (Regex.IsMatch(name, "`\\w{1,}$"))
{
2024-01-08 13:39:38 +08:00
var s = name.Split('`');
2023-11-08 15:15:10 +08:00
name = s[0] + $"<{s[1]}>";
}
_processes.Add(type, new ProcessInterface(type, name));
}
else
{
_systems.Add(type);
}
}
}
}
}
#region Systems
public static bool IsSystem(Type type) => _systems.Contains(type);
public static bool IsEcsSystem(this Type type) => _systems.Contains(type);
#endregion
#region Process
public static bool IsProcessInterface(Type type)
{
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
return _processes.ContainsKey(type);
}
public static bool IsEcsProcessInterface(this Type type) => IsProcessInterface(type);
public static string GetProcessInterfaceName(Type type)
{
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
return _processes[type].processName;
}
public static bool TryGetProcessInterfaceName(Type type, out string name)
{
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
bool result = _processes.TryGetValue(type, out ProcessInterface data);
name = data.processName;
return result;
}
public static IEnumerable<Type> GetEcsProcessInterfaces(this Type self)
{
2023-12-20 23:21:10 +08:00
return self.GetInterfaces().Where(o => o.IsEcsProcessInterface());
2023-11-08 15:15:10 +08:00
}
#endregion
}
}