2023-12-20 23:16:57 +08:00
using DCFApixels.DragonECS.RunnersCore ;
2023-05-27 23:02:32 +08:00
using System ;
2023-03-11 17:11:40 +08:00
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 ;
2023-03-11 17:11:40 +08:00
namespace DCFApixels.DragonECS
{
2023-12-06 20:34:33 +08:00
#if UNITY_2020_3_OR_NEWER
2024-02-22 15:39:37 +08:00
[UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve]
2023-12-06 20:34:33 +08:00
#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:34:33 +08:00
{
2023-12-06 20:38:00 +08:00
private static readonly Type _baseType = typeof ( EcsRunner < > ) ;
2023-12-06 20:34:33 +08:00
public readonly Type runnerType ;
2023-12-06 20:38:00 +08:00
public BindWithEcsRunnerAttribute ( Type runnerType )
2023-12-06 20:34:33 +08:00
{
if ( runnerType = = null )
2024-02-22 15:39:37 +08:00
{
2023-12-06 20:34:33 +08:00
throw new ArgumentNullException ( ) ;
2024-02-22 15:39:37 +08:00
}
2023-12-06 21:15:35 +08:00
if ( ! CheckSubclass ( runnerType ) )
2024-02-22 15:39:37 +08:00
{
2023-12-06 20:34:33 +08:00
throw new ArgumentException ( ) ;
2024-02-22 15:39:37 +08:00
}
2023-12-06 20:34:33 +08:00
this . runnerType = runnerType ;
}
2023-12-06 21:15:35 +08:00
private bool CheckSubclass ( Type type )
2023-12-06 20:34:33 +08:00
{
2023-12-06 20:38:00 +08:00
if ( type . IsGenericType & & type . GetGenericTypeDefinition ( ) = = _baseType )
2024-02-22 15:39:37 +08:00
{
2023-12-06 20:34:33 +08:00
return true ;
2024-02-22 15:39:37 +08:00
}
2023-12-06 20:34:33 +08:00
if ( type . BaseType ! = null )
2024-02-22 15:39:37 +08:00
{
2023-12-06 21:15:35 +08:00
return CheckSubclass ( type . BaseType ) ;
2024-02-22 15:39:37 +08:00
}
2023-12-06 20:34:33 +08:00
return false ;
}
}
2023-03-11 17:11:40 +08:00
2024-02-22 22:16:03 +08:00
public interface IEcsProcess { }
2023-03-16 01:49:14 +08:00
2023-04-23 17:55:01 +08:00
namespace RunnersCore
2023-03-11 17:11:40 +08:00
{
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 ; }
2024-02-22 15:39:37 +08:00
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
2024-02-22 22:16:03 +08:00
public abstract class EcsRunner < TProcess > : IEcsProcess , IEcsRunner
where TProcess : IEcsProcess
2023-03-11 17:11:40 +08:00
{
2023-04-23 17:55:01 +08:00
#region Register
2024-02-22 15:39:37 +08:00
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
2024-02-22 15:39:37 +08:00
if ( _runnerImplementationType ! = null )
2023-05-26 04:25:09 +08:00
{
2024-02-22 15:39:37 +08:00
throw new EcsRunnerImplementationException ( $"The Runner<{typeof(TProcess).FullName}> can have only one implementing subclass" ) ;
2023-05-26 04:25:09 +08:00
}
2023-03-11 17:11:40 +08:00
2024-02-22 15:39:37 +08:00
Type interfaceType = typeof ( TProcess ) ;
2023-03-11 17:11:40 +08:00
2023-05-26 04:25:09 +08:00
var interfaces = interfaceType . GetInterfaces ( ) ;
if ( interfaceType . IsInterface = = false )
{
2024-02-22 15:39:37 +08:00
throw new ArgumentException ( $"{typeof(TProcess).FullName} is not interface" ) ;
2023-05-26 04:25:09 +08:00
}
2024-02-22 22:16:03 +08:00
if ( interfaces . Length ! = 1 | | interfaces [ 0 ] ! = typeof ( IEcsProcess ) )
2023-05-26 04:25:09 +08:00
{
2024-02-22 22:16:03 +08:00
throw new ArgumentException ( $"{typeof(TProcess).FullName} does not directly inherit the {nameof(IEcsProcess)} interface" ) ;
2023-05-26 04:25:09 +08:00
}
2023-03-11 17:11:40 +08:00
#endif
2024-02-22 15:39:37 +08:00
_runnerImplementationType = subclass ;
2023-03-11 17:11:40 +08:00
}
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
2023-12-06 21:15:35 +08:00
private static void CheckRunnerValide ( Type type ) //TODO доработать проверку валидности реалиазации ранера
{
2024-02-22 15:39:37 +08:00
Type targetInterface = typeof ( TProcess ) ;
2023-12-06 21:15:35 +08:00
if ( type . IsAbstract )
{
throw new Exception ( ) ;
}
2024-01-08 13:39:38 +08:00
Type GetRunnerBaseType ( Type inType )
2023-12-06 21:15:35 +08:00
{
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 ) ;
2023-12-06 21:15:35 +08:00
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)}." ) ;
}
}
2024-02-22 15:39:37 +08:00
public static TProcess Instantiate ( EcsPipeline source )
2023-04-23 17:55:01 +08:00
{
2024-02-22 15:39:37 +08:00
EcsProcess < TProcess > process = source . GetProcess < TProcess > ( ) ;
if ( _runnerImplementationType = = null )
2023-12-06 20:34:33 +08:00
{
2024-02-22 15:39:37 +08:00
Type interfaceType = typeof ( TProcess ) ;
2023-12-20 23:16:57 +08:00
if ( interfaceType . TryGetCustomAttribute ( out BindWithEcsRunnerAttribute atr ) )
2023-12-06 20:34:33 +08:00
{
2024-02-22 15:39:37 +08:00
Type runnerImplementationType = atr . runnerType ;
2023-12-06 20:34:33 +08:00
if ( interfaceType . IsGenericType )
{
Type [ ] genericTypes = interfaceType . GetGenericArguments ( ) ;
2024-02-22 15:39:37 +08:00
runnerImplementationType = runnerImplementationType . MakeGenericType ( genericTypes ) ;
2023-12-06 20:34:33 +08:00
}
2024-02-22 15:39:37 +08:00
CheckRunnerValide ( runnerImplementationType ) ;
_runnerImplementationType = runnerImplementationType ;
2023-12-06 20:34:33 +08:00
}
else
{
2023-12-06 21:15:35 +08:00
throw new EcsFrameworkException ( "Процесс не связан с раннером, используйте атрибуут BindWithEcsRunner(Type runnerType)" ) ;
2023-12-06 20:34:33 +08:00
}
}
2024-02-22 15:39:37 +08:00
var instance = ( EcsRunner < TProcess > ) Activator . CreateInstance ( _runnerImplementationType ) ;
2024-02-22 22:16:03 +08:00
return ( TProcess ) ( IEcsProcess ) 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 ;
2024-02-22 15:39:37 +08:00
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
2024-02-22 15:39:37 +08:00
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
2024-02-22 15:39:37 +08:00
private EcsRunner < TProcess > Set ( EcsPipeline source , EcsProcess < TProcess > process )
2023-04-23 17:55:01 +08:00
{
_source = source ;
2024-02-22 15:39:37 +08:00
this . _process = process ;
2023-04-23 17:55:01 +08:00
OnSetup ( ) ;
return this ;
}
internal void Rebuild ( )
{
2024-02-22 15:39:37 +08:00
Set ( _source , _source . GetProcess < TProcess > ( ) ) ;
2023-04-23 17:55:01 +08:00
}
public void Destroy ( )
{
_isDestroyed = true ;
2024-02-22 15:39:37 +08:00
_source . OnRunnerDestroy_Internal ( this ) ;
2023-04-23 17:55:01 +08:00
_source = null ;
2024-02-22 15:39:37 +08:00
_process = EcsProcess < TProcess > . Empty ;
2023-04-23 17:55:01 +08:00
OnDestroy ( ) ;
}
2024-02-22 15:39:37 +08:00
protected virtual void OnSetup ( ) { } //TODO rename to OnInitialize
2023-04-23 17:55:01 +08:00
protected virtual void OnDestroy ( ) { }
}
2023-03-11 17:11:40 +08:00
}
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
{
2024-02-22 22:16:03 +08:00
public static void Destroy ( IEcsProcess runner ) = > ( ( IEcsRunner ) runner ) . Destroy ( ) ;
2023-04-23 17:55:01 +08:00
}
2023-03-30 01:57:10 +08:00
public static class IEcsSystemExtensions
{
2024-02-22 22:16:03 +08:00
public static bool IsRunner ( this IEcsProcess 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 ( )
{
2024-02-22 22:16:03 +08:00
Type processBasicInterface = typeof ( IEcsProcess ) ;
2023-11-08 15:15:10 +08:00
foreach ( var assembly in AppDomain . CurrentDomain . GetAssemblies ( ) )
{
var types = assembly . GetTypes ( ) ;
foreach ( var type in types )
{
2024-02-22 22:16:03 +08:00
if ( type . GetInterface ( nameof ( IEcsProcess ) ) ! = 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
}
2023-03-11 17:11:40 +08:00
}