@ -1,6 +1,5 @@
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using gaemstone.ECS ;
using gaemstone.Utility ;
@ -21,16 +20,13 @@ public class ModuleManager
= > _ modules . GetValueOrDefault ( entity ) ;
public EntityRef Register < T > ( )
where T : class , new ( )
where T : class , IModule , new ( )
{
var moduleType = typeof ( T ) ;
if ( moduleType . IsGenericType ) throw new Exception (
$"Module {moduleType} must be a non-generic class" ) ;
if ( ! moduleType . Has < ModuleAttribute > ( ) ) throw new Exception (
$"Module {moduleType} must be marked with ModuleAttribute" ) ;
var path = GetModulePath ( moduleType ) ;
var module = new ModuleInfo ( Universe , moduleType , path ) ;
var module = new ModuleInfo < T > ( Universe ) ;
_ modules . Add ( module . Entity , module ) ;
TryEnableModule ( module ) ;
return module . Entity ;
@ -40,7 +36,7 @@ public class ModuleManager
{
if ( module . UnmetDependencies . Count > 0 ) return ;
Console . WriteLine ( $"Enabling module {module.Path} ..." ) ;
Console . WriteLine ( $"Enabling module {module.Entity.GetFull Path() } ..." ) ;
module . Enable ( ) ;
// Find other modules that might be missing this module as a dependency.
@ -56,102 +52,66 @@ public class ModuleManager
}
}
public static EntityPath GetModulePath ( Type type )
internal abstract class ModuleInfo
{
var attr = type . Get < ModuleAttribute > ( ) ;
if ( attr = = null ) throw new ArgumentException (
$"Module {type} must be marked with ModuleAttribute" , nameof ( type ) ) ;
public abstract EntityRef Entity { get ; }
public abstract bool IsActive { get ; }
var path = EntityPath . Parse (
( type . Get < PathAttribute > ( ) is PathAttribute pathAttr )
? pathAttr . Value : type . Name ) ;
// If specified path is absolute, return it now.
if ( path . IsAbsolute ) return path ;
// Otherwise, create it based on the type's assembly, namespace and name.
var assemblyName = type . Assembly . GetName ( ) . Name ! ;
if ( ! type . FullName ! . StartsWith ( assemblyName + '.' ) ) throw new InvalidOperationException (
$"Module {type} must be defined under namespace {assemblyName}" ) ;
var fullNameWithoutAssembly = type . FullName ! [ ( assemblyName . Length + 1 ) . . ] ;
public HashSet < ModuleInfo > MetDependencies { get ; } = new ( ) ;
public HashSet < Entity > UnmetDependencies { get ; } = new ( ) ;
var parts = fullNameWithoutAssembly . Split ( '.' ) [ . . ^ 1 ] ;
return new ( true , parts . Prepend ( assemblyName ) . Concat ( path . GetParts ( ) ) . ToArray ( ) ) ;
public abstract void Enable ( ) ;
}
internal class ModuleInfo
internal class ModuleInfo < T > : ModuleInfo
where T : IModule , new ( )
{
public Universe Universe { get ; }
public Type Type { get ; }
public EntityPath Path { get ; }
public override EntityRef Entity { get ; }
public override bool IsActive = > ( Instance ! = null ) ;
public T ? Instance { get ; internal set ; }
public EntityRef Entity { get ; }
public object? Instance { get ; internal set ; }
public bool IsActive = > Instance ! = null ;
public ModuleInfo ( Universe universe )
{
var builder = universe . New ( T . ModulePath ) . Add < Module > ( ) ;
public HashSet < ModuleInfo > MetDependencies { get ; } = new ( ) ;
public HashSet < Entity > UnmetDependencies { get ; } = new ( ) ;
foreach ( var dependsPath in T . Dependencies ) {
var dependency = universe . LookupByPath ( dependsPath ) ? ?
universe . New ( dependsPath ) . Add < Module > ( ) . Disable ( ) . Build ( ) ;
public ModuleInfo ( Universe universe , Type type , EntityPath path )
{
Universe = universe ;
Type = type ;
Path = path ;
if ( Type . IsAbstract | | Type . IsSealed ) throw new Exception (
$"Module {Type} must not be abstract, sealed or static" ) ;
if ( Type . GetConstructor ( Type . EmptyTypes ) = = null ) throw new Exception (
$"Module {Type} must define public parameterless constructor" ) ;
var module = Universe . New ( Path ) . Add < Module > ( ) ;
// Add module dependencies from [DependsOn<>] attributes.
foreach ( var attr in Type . GetCustomAttributes ( ) ) {
// TODO: Do this without reflection.
var attrType = attr . GetType ( ) ;
if ( ! attrType . IsGenericType ) continue ;
if ( attrType . GetGenericTypeDefinition ( ) ! = typeof ( DependsOnAttribute < > ) ) continue ;
var dependsTarget = attrType . GenericTypeArguments [ 0 ] ;
var dependsPath = GetModulePath ( dependsTarget ) ;
var dependency = Universe . LookupByPath ( dependsPath ) ? ?
Universe . New ( dependsPath ) . Add < Module > ( ) . Disable ( ) . Build ( ) ;
var depModule = Universe . Modules . Lookup ( dependency ) ;
var depModule = universe . Modules . Lookup ( dependency ) ;
if ( depModule ? . IsActive = = true ) MetDependencies . Add ( depModule ) ;
else { UnmetDependencies . Add ( dependency ) ; module . Disable ( ) ; }
else { UnmetDependencies . Add ( dependency ) ; builder . Disable ( ) ; }
module . Add < DependsOn > ( dependency ) ;
builder . Add < DependsOn > ( dependency ) ;
}
Entity = module . Build ( ) . CreateLookup ( Type ) ;
Entity = builder . Build ( ) . CreateLookup < T > ( ) ;
// Ensure all parent entities have Module set.
for ( var p = Entity . Parent ; p ! = null ; p = p . Parent )
p . Add < Module > ( ) ;
}
public void Enable ( )
public override void Enable ( )
{
Entity . Enable ( ) ;
Instance = Activator . CreateInstance ( Type ) ! ; // TODO: Replace with generic new() somehow.
if ( Instance is IModuleAutoRegisterComponents generatedComponents )
generatedComponents . RegisterComponents ( Entity ) ;
Instance = new T ( ) ;
( Instance as IModuleAutoRegisterComponents ) ? . RegisterComponents ( Entity ) ;
( Instance as IModuleInitializer ) ? . Initialize ( Entity ) ;
RegisterMethods ( Instance ) ;
}
private void RegisterMethods ( object? instance )
{
foreach ( var method in Type . GetMethods (
var world = Entity . World ;
foreach ( var method in typeof ( T ) . GetMethods (
BindingFlags . Public | BindingFlags . NonPublic |
BindingFlags . Static | BindingFlags . Instance
) ) {
if ( method . Has < SystemAttribute > ( ) )
Universe . InitSystem ( instance , method ) . ChildOf ( Entity ) ;
world . InitSystem ( instance , method ) . ChildOf ( Entity ) ;
if ( method . Has < ObserverAttribute > ( ) )
Universe . InitObserver ( instance , method ) . ChildOf ( Entity ) ;
world . InitObserver ( instance , method ) . ChildOf ( Entity ) ;
}
}
}