2023-03-29 16:43:06 +08:00
using System ;
using System.Collections.Generic ;
using System.Reflection ;
namespace DCFApixels.DragonECS
{
2023-04-05 09:14:42 +08:00
internal static class DummyInstance < T >
{
public static T intsance = ( T ) Activator . CreateInstance ( typeof ( T ) ) ;
}
2023-03-29 16:43:06 +08:00
internal class AutoInjectionMap
{
2023-03-30 05:33:55 +08:00
private readonly EcsPipeline _source ;
2023-05-30 02:17:09 +08:00
private Dictionary < Type , List < FieldRecord > > _systems ;
2023-04-05 09:14:42 +08:00
private HashSet < Type > _notInjected ;
2023-05-27 15:59:57 +08:00
private Type _dummyInstance = typeof ( DummyInstance < > ) ;
2023-04-05 09:14:42 +08:00
private bool _isDummyInjected = false ;
2023-03-29 16:43:06 +08:00
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-05-30 02:17:09 +08:00
_systems = new Dictionary < Type , List < FieldRecord > > ( ) ;
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-05-26 23:32:29 +08:00
foreach ( var field in GetAllFieldsFor ( systemType ) )
2023-03-29 16:43:06 +08:00
{
2023-05-07 00:50:23 +08:00
EcsInjectAttribute autoInjectAttribute = field . GetCustomAttribute < EcsInjectAttribute > ( ) ;
2023-04-05 09:14:42 +08:00
if ( autoInjectAttribute ! = null )
2023-03-29 16:43:06 +08:00
{
Type fieldType = field . FieldType ;
2023-05-30 02:17:09 +08:00
List < FieldRecord > list ;
2023-03-29 16:43:06 +08:00
if ( ! _systems . TryGetValue ( fieldType , out list ) )
{
2023-05-30 02:17:09 +08:00
list = new List < FieldRecord > ( ) ;
2023-03-29 16:43:06 +08:00
_systems . Add ( fieldType , list ) ;
}
2023-05-30 02:17:09 +08:00
list . Add ( new FieldRecord ( system , field , autoInjectAttribute ) ) ;
2023-03-29 16:43:06 +08:00
}
}
}
2023-04-05 09:14:42 +08:00
foreach ( var item in _systems . Keys )
_notInjected . Add ( item ) ;
2023-03-29 16:43:06 +08:00
}
2023-05-26 23:32:29 +08:00
private static List < FieldInfo > GetAllFieldsFor ( Type type )
{
List < FieldInfo > result = new List < FieldInfo > ( ) ;
Do ( type , result ) ;
2023-05-30 02:17:09 +08:00
static void Do ( Type type , List < FieldInfo > result )
2023-05-26 23:32:29 +08:00
{
result . AddRange ( type . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ) ;
if ( type . BaseType ! = null )
Do ( type . BaseType , result ) ;
}
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-04-05 09:14:42 +08:00
_notInjected . Remove ( obj . GetType ( ) ) ;
2023-05-30 02:17:09 +08:00
Type baseType = fieldType . BaseType ;
if ( baseType ! = null )
Inject ( baseType , obj ) ;
if ( _systems . TryGetValue ( fieldType , out List < FieldRecord > list ) )
2023-03-29 16:43:06 +08:00
{
foreach ( var item in list )
item . field . SetValue ( item . target , obj ) ;
}
}
2023-04-05 09:14:42 +08:00
public void InjectDummy ( )
{
if ( _isDummyInjected )
return ;
_isDummyInjected = true ;
foreach ( var notInjectedItem in _notInjected )
{
foreach ( var systemRecord in _systems [ notInjectedItem ] )
{
if ( systemRecord . attribute . notNullDummyType = = null )
continue ;
if ( systemRecord . field . GetValue ( systemRecord . target ) ! = null )
continue ;
if ( systemRecord . field . FieldType . IsAssignableFrom ( systemRecord . attribute . notNullDummyType ) = = false )
{
EcsDebug . Print ( EcsConsts . DEBUG_ERROR_TAG , $"The {systemRecord.attribute.notNullDummyType} dummy cannot be assigned to the {systemRecord.field.FieldType.Name} field" ) ;
continue ;
}
systemRecord . field . SetValue ( systemRecord . target ,
2023-05-27 15:59:57 +08:00
_dummyInstance . MakeGenericType ( systemRecord . attribute . notNullDummyType ) . GetField ( "intsance" , BindingFlags . Static | BindingFlags . Public ) . GetValue ( null ) ) ;
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 ( ) ;
_notInjected = null ;
}
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 )
{
foreach ( var systemRecord in _systems [ item ] )
{
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-05-30 02:17:09 +08:00
private readonly struct FieldRecord
2023-03-29 16:43:06 +08:00
{
public readonly IEcsSystem target ;
public readonly FieldInfo field ;
2023-05-07 00:50:23 +08:00
public readonly EcsInjectAttribute attribute ;
2023-05-30 02:17:09 +08:00
public FieldRecord ( IEcsSystem target , FieldInfo field , EcsInjectAttribute attribute )
2023-03-29 16:43:06 +08:00
{
this . target = target ;
this . field = field ;
2023-04-05 09:14:42 +08:00
this . attribute = attribute ;
2023-03-29 16:43:06 +08:00
}
}
}
[DebugHide, DebugColor(DebugColor.Gray)]
2023-05-07 00:50:23 +08:00
public class AutoInjectSystem : IEcsPreInitProcess , IEcsPreInject , IEcsPreInitInjectProcess
2023-03-29 16:43:06 +08:00
{
2023-03-30 05:33:55 +08:00
private EcsPipeline _pipeline ;
2023-03-29 16:43:06 +08:00
private List < object > _injectQueue = new List < object > ( ) ;
private AutoInjectionMap _autoInjectionMap ;
2023-04-05 09:14:42 +08:00
private bool _isPreInjectionComplete = false ;
2023-03-29 16:43:06 +08:00
public void PreInject ( object obj )
{
2023-03-30 05:33:55 +08:00
if ( _pipeline = = null )
2023-03-29 16:43:06 +08:00
{
_injectQueue . Add ( obj ) ;
return ;
}
AutoInject ( obj ) ;
}
2023-03-30 05:33:55 +08:00
public void PreInit ( EcsPipeline pipeline )
2023-03-29 16:43:06 +08:00
{
2023-03-30 05:33:55 +08:00
_pipeline = pipeline ;
_autoInjectionMap = new AutoInjectionMap ( _pipeline ) ;
2023-03-29 16:43:06 +08:00
foreach ( var obj in _injectQueue )
{
AutoInject ( obj ) ;
}
_injectQueue . Clear ( ) ;
_injectQueue = null ;
2023-04-05 09:14:42 +08:00
if ( _isPreInjectionComplete )
{
_autoInjectionMap . InjectDummy ( ) ;
}
2023-03-29 16:43:06 +08:00
}
private void AutoInject ( object obj )
{
2023-05-30 02:17:09 +08:00
_autoInjectionMap . Inject ( obj . GetType ( ) , obj ) ;
2023-03-29 16:43:06 +08:00
}
2023-04-05 09:14:42 +08:00
public void OnPreInitInjectionBefore ( ) { }
public void OnPreInitInjectionAfter ( )
{
_isPreInjectionComplete = true ;
if ( _autoInjectionMap ! = null )
{
_autoInjectionMap . InjectDummy ( ) ;
}
}
2023-03-29 16:43:06 +08:00
}
}