2023-03-29 16:43:06 +08:00
using System ;
using System.Collections.Generic ;
2023-06-10 02:12:22 +08:00
using System.Linq ;
2023-03-29 16:43:06 +08:00
using System.Reflection ;
namespace DCFApixels.DragonECS
{
internal class AutoInjectionMap
{
2023-03-30 05:33:55 +08:00
private readonly EcsPipeline _source ;
2023-06-10 02:12:22 +08:00
private Dictionary < Type , List < InjectedPropertyRecord > > _systemProoperties ;
2023-04-05 09:14:42 +08:00
private HashSet < Type > _notInjected ;
private bool _isDummyInjected = false ;
2023-03-29 16:43:06 +08:00
2023-06-16 16:13:44 +08:00
private bool _isPreInitInjectionComplete = false ;
2023-03-30 05:33:55 +08:00
public AutoInjectionMap ( EcsPipeline source )
2023-03-29 16:43:06 +08:00
{
_source = source ;
var allsystems = _source . AllSystems ;
2023-06-10 02:12:22 +08:00
_systemProoperties = new Dictionary < Type , List < InjectedPropertyRecord > > ( ) ;
2023-04-05 09:14:42 +08:00
_notInjected = new HashSet < Type > ( ) ;
2023-03-29 16:43:06 +08:00
foreach ( var system in allsystems )
{
Type systemType = system . GetType ( ) ;
2023-06-10 02:12:22 +08:00
foreach ( var property in GetAllPropertiesFor ( systemType ) )
2023-03-29 16:43:06 +08:00
{
2023-06-10 02:12:22 +08:00
Type fieldType = property . PropertyType ;
List < InjectedPropertyRecord > list ;
if ( ! _systemProoperties . TryGetValue ( fieldType , out list ) )
{
list = new List < InjectedPropertyRecord > ( ) ;
_systemProoperties . Add ( fieldType , list ) ;
2023-03-29 16:43:06 +08:00
}
2023-06-10 02:12:22 +08:00
list . Add ( new InjectedPropertyRecord ( system , property ) ) ;
2023-03-29 16:43:06 +08:00
}
}
2023-06-10 02:12:22 +08:00
foreach ( var item in _systemProoperties . Keys )
2023-04-05 09:14:42 +08:00
_notInjected . Add ( item ) ;
2023-03-29 16:43:06 +08:00
}
2023-06-12 22:12:30 +08:00
private static void Do ( Type type , List < IInjectedProperty > result )
2023-05-26 23:32:29 +08:00
{
2023-06-10 02:12:22 +08:00
const BindingFlags bindingFlags = BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ;
2023-06-12 22:12:30 +08:00
result . AddRange ( type . GetFields ( bindingFlags )
. Where ( o = > o . GetCustomAttribute < EcsInjectAttribute > ( ) ! = null )
. Select ( o = > new InjectedField ( o ) ) ) ;
result . AddRange ( type . GetProperties ( bindingFlags )
. Where ( o = > {
if ( o . GetCustomAttribute < EcsInjectAttribute > ( ) = = null )
return false ;
2023-06-10 02:12:22 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2023-06-12 22:12:30 +08:00
if ( o . CanWrite = = false )
throw new EcsAutoInjectionException ( $"{o.Name} property is cant write" ) ;
2023-06-10 02:12:22 +08:00
#endif
2023-06-12 22:12:30 +08:00
return true ;
} )
. Select ( o = > new InjectedProperty ( o ) ) ) ;
result . AddRange ( type . GetMethods ( bindingFlags )
. Where ( o = > {
if ( o . GetCustomAttribute < EcsInjectAttribute > ( ) = = null )
return false ;
2023-06-10 02:12:22 +08:00
#if ( DEBUG & & ! DISABLE_DEBUG ) | | ENABLE_DRAGONECS_ASSERT_CHEKS
2023-06-12 22:12:30 +08:00
if ( o . IsGenericMethod )
throw new EcsAutoInjectionException ( $"{o.Name} method is Generic" ) ;
if ( o . GetParameters ( ) . Length ! = 1 )
throw new EcsAutoInjectionException ( $"{o.Name} method Arguments != 1" ) ;
2023-06-10 02:12:22 +08:00
#endif
2023-06-12 22:12:30 +08:00
return true ;
} )
. Select ( o = > new InjectedMethod ( o ) ) ) ;
if ( type . BaseType ! = null )
Do ( type . BaseType , result ) ;
}
private static List < IInjectedProperty > GetAllPropertiesFor ( Type type )
{
List < IInjectedProperty > result = new List < IInjectedProperty > ( ) ;
Do ( type , result ) ;
2023-05-26 23:32:29 +08:00
return result ;
}
2023-05-30 02:17:09 +08:00
public void Inject ( Type fieldType , object obj )
2023-03-29 16:43:06 +08:00
{
2023-06-16 16:13:44 +08:00
if ( ! _isPreInitInjectionComplete )
_notInjected . Remove ( fieldType ) ;
2023-05-30 02:17:09 +08:00
Type baseType = fieldType . BaseType ;
if ( baseType ! = null )
Inject ( baseType , obj ) ;
2023-06-10 02:12:22 +08:00
if ( _systemProoperties . TryGetValue ( fieldType , out List < InjectedPropertyRecord > list ) )
2023-03-29 16:43:06 +08:00
{
foreach ( var item in list )
2023-06-10 02:12:22 +08:00
item . property . Inject ( item . target , obj ) ;
2023-03-29 16:43:06 +08:00
}
}
2023-04-05 09:14:42 +08:00
public void InjectDummy ( )
{
if ( _isDummyInjected )
return ;
_isDummyInjected = true ;
foreach ( var notInjectedItem in _notInjected )
{
2023-06-10 02:12:22 +08:00
foreach ( var systemRecord in _systemProoperties [ notInjectedItem ] )
2023-04-05 09:14:42 +08:00
{
2023-06-10 02:12:22 +08:00
if ( systemRecord . Attribute . notNullDummyType = = null )
2023-04-05 09:14:42 +08:00
continue ;
2023-06-10 02:12:22 +08:00
if ( systemRecord . property . IsInjected )
2023-04-05 09:14:42 +08:00
continue ;
2023-06-10 02:12:22 +08:00
if ( systemRecord . property . PropertyType . IsAssignableFrom ( systemRecord . Attribute . notNullDummyType ) = = false )
2023-04-05 09:14:42 +08:00
{
2023-06-10 02:12:22 +08:00
EcsDebug . Print ( EcsConsts . DEBUG_ERROR_TAG , $"The {systemRecord.Attribute.notNullDummyType} dummy cannot be assigned to the {systemRecord.property.PropertyType.Name} field" ) ;
2023-04-05 09:14:42 +08:00
continue ;
}
2023-06-16 16:13:44 +08:00
systemRecord . property . Inject ( systemRecord . target , DummyInstance . GetInstance ( systemRecord . Attribute . notNullDummyType ) ) ;
2023-04-05 09:14:42 +08:00
}
}
2023-05-27 15:59:57 +08:00
WarningMissedInjections ( ) ;
2023-04-05 09:14:42 +08:00
_notInjected . Clear ( ) ;
2023-06-12 22:12:30 +08:00
_notInjected = null ;
2023-04-05 09:14:42 +08:00
}
2023-05-27 15:59:57 +08:00
private void WarningMissedInjections ( )
{
2023-05-28 05:53:37 +08:00
#if DEBUG
2023-05-27 15:59:57 +08:00
foreach ( var item in _notInjected )
{
2023-06-10 02:12:22 +08:00
foreach ( var systemRecord in _systemProoperties [ item ] )
2023-05-27 15:59:57 +08:00
EcsDebug . PrintWarning ( $"in system {EcsDebugUtility.GetGenericTypeFullName(systemRecord.target.GetType(), 1)} is missing an injection of {EcsDebugUtility.GetGenericTypeFullName(item, 1)}." ) ;
}
2023-05-28 05:53:37 +08:00
#endif
2023-05-27 15:59:57 +08:00
}
2023-06-16 16:13:44 +08:00
public void OnPreInitInjectionComplete ( )
{
_isPreInitInjectionComplete = true ;
}
2023-06-10 02:12:22 +08:00
private readonly struct InjectedPropertyRecord
2023-03-29 16:43:06 +08:00
{
2023-05-30 18:30:41 +08:00
public readonly IEcsProcess target ;
2023-06-10 02:12:22 +08:00
public readonly IInjectedProperty property ;
public EcsInjectAttribute Attribute = > property . GetAutoInjectAttribute ( ) ;
public InjectedPropertyRecord ( IEcsProcess target , IInjectedProperty property )
2023-03-29 16:43:06 +08:00
{
this . target = target ;
2023-06-10 02:12:22 +08:00
this . property = property ;
2023-03-29 16:43:06 +08:00
}
}
}
[DebugHide, DebugColor(DebugColor.Gray)]
2023-06-16 16:13:44 +08:00
public class AutoInjectSystem : IEcsPreInject , IEcsInject < EcsPipeline > , IEcsPreInitInjectProcess
2023-03-29 16:43:06 +08:00
{
2023-03-30 05:33:55 +08:00
private EcsPipeline _pipeline ;
2023-06-16 16:13:44 +08:00
private List < object > _delayedInjects = new List < object > ( ) ;
2023-03-29 16:43:06 +08:00
private AutoInjectionMap _autoInjectionMap ;
2023-06-16 16:13:44 +08:00
public void Inject ( EcsPipeline obj ) = > _pipeline = obj ;
2023-03-29 16:43:06 +08:00
public void PreInject ( object obj )
{
2023-06-16 16:13:44 +08:00
_delayedInjects . Add ( obj ) ;
2023-03-29 16:43:06 +08:00
}
2023-04-05 09:14:42 +08:00
public void OnPreInitInjectionBefore ( ) { }
public void OnPreInitInjectionAfter ( )
{
2023-06-16 16:13:44 +08:00
_autoInjectionMap = new AutoInjectionMap ( _pipeline ) ;
2023-06-10 02:12:22 +08:00
2023-06-16 16:13:44 +08:00
foreach ( var obj in _delayedInjects )
_autoInjectionMap . Inject ( obj . GetType ( ) , obj ) ;
_autoInjectionMap . InjectDummy ( ) ;
_autoInjectionMap . OnPreInitInjectionComplete ( ) ;
_delayedInjects . Clear ( ) ;
_delayedInjects = null ;
GC . Collect ( 0 ) ;
2023-06-10 02:12:22 +08:00
}
}
#region Utils
internal interface IInjectedProperty
{
2023-06-12 22:12:30 +08:00
bool IsInjected { get ; }
Type PropertyType { get ; }
2023-06-10 02:12:22 +08:00
EcsInjectAttribute GetAutoInjectAttribute ( ) ;
void Inject ( object target , object value ) ;
}
internal class InjectedField : IInjectedProperty
{
private FieldInfo _member ;
private EcsInjectAttribute _injectAttribute ;
public bool IsInjected { get ; private set ; }
public Type PropertyType = > _member . FieldType ;
public InjectedField ( FieldInfo member )
{
_member = member ;
_injectAttribute = member . GetCustomAttribute < EcsInjectAttribute > ( ) ;
}
public EcsInjectAttribute GetAutoInjectAttribute ( ) = > _injectAttribute ;
public void Inject ( object target , object value )
{
_member . SetValue ( target , value ) ;
IsInjected = true ;
}
}
internal class InjectedProperty : IInjectedProperty
{
private PropertyInfo _member ;
private EcsInjectAttribute _injectAttribute ;
public bool IsInjected { get ; private set ; }
public Type PropertyType = > _member . PropertyType ;
public InjectedProperty ( PropertyInfo member )
{
_member = member ;
_injectAttribute = member . GetCustomAttribute < EcsInjectAttribute > ( ) ;
}
public EcsInjectAttribute GetAutoInjectAttribute ( ) = > _injectAttribute ;
public void Inject ( object target , object value )
{
_member . SetValue ( target , value ) ;
IsInjected = true ;
}
}
internal class InjectedMethod : IInjectedProperty
{
private MethodInfo _member ;
private EcsInjectAttribute _injectAttribute ;
private Type propertyType ;
public bool IsInjected { get ; private set ; }
public Type PropertyType = > propertyType ;
public InjectedMethod ( MethodInfo member )
{
_member = member ;
_injectAttribute = member . GetCustomAttribute < EcsInjectAttribute > ( ) ;
propertyType = _member . GetParameters ( ) [ 0 ] . ParameterType ;
}
public EcsInjectAttribute GetAutoInjectAttribute ( ) = > _injectAttribute ;
public void Inject ( object target , object value )
{
_member . Invoke ( target , new object [ ] { value } ) ;
IsInjected = true ;
2023-04-05 09:14:42 +08:00
}
2023-03-29 16:43:06 +08:00
}
2023-06-10 02:12:22 +08:00
#endregion
2023-03-29 16:43:06 +08:00
}