You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.6 KiB
135 lines
3.6 KiB
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Collections.Immutable; |
|
using System.Linq; |
|
using gaemstone.ECS; |
|
using static gaemstone.Flecs.Core; |
|
using Module = gaemstone.Flecs.Core.Module; |
|
|
|
namespace gaemstone; |
|
|
|
public class ModuleManager<TContext> |
|
: IEnumerable<ModuleManager<TContext>.IModuleInfo> |
|
{ |
|
private readonly Dictionary<Entity<TContext>, IModuleInfo> _modules = new(); |
|
|
|
public Universe<TContext> Universe { get; } |
|
public ModuleManager(Universe<TContext> universe) |
|
=> Universe = universe; |
|
|
|
internal IModuleInfo? Lookup(Entity<TContext> entity) |
|
=> _modules.GetValueOrDefault(entity); |
|
|
|
public Entity<TContext> Register<T>() |
|
where T : IModule |
|
{ |
|
// if (!typeof(T).IsAssignableTo(typeof(IModule))) throw new ArgumentException( |
|
// $"The specified type {typeof(T)} does not implement IModule", nameof(T)); |
|
|
|
var module = new ModuleInfo<T>(Universe); |
|
_modules.Add(module.Entity, module); |
|
TryEnableModule(module); |
|
return module.Entity; |
|
} |
|
|
|
private void TryEnableModule(IModuleInfo module) |
|
{ |
|
if (!module.Dependencies.All(dep => dep.IsDependencyMet)) return; |
|
|
|
Console.WriteLine($"Enabling module {module.Entity.Path}"); |
|
|
|
module.Enable(); |
|
|
|
// Find other modules that might be missing this module as a dependency. |
|
foreach (var other in _modules.Values) { |
|
if (other.IsInitialized) continue; |
|
var dependency = other.Dependencies.FirstOrDefault(dep => dep.Entity == module.Entity); |
|
if (dependency == null) continue; |
|
|
|
dependency.Info = module; |
|
dependency.IsDependencyMet = true; |
|
|
|
TryEnableModule(other); |
|
} |
|
} |
|
|
|
public interface IModuleInfo |
|
{ |
|
Entity<TContext> Entity { get; } |
|
IReadOnlyCollection<ModuleDependency> Dependencies { get; } |
|
bool IsInitialized { get; } |
|
void Enable(); |
|
} |
|
|
|
// IEnumerable implementation |
|
public IEnumerator<IModuleInfo> GetEnumerator() => _modules.Values.GetEnumerator(); |
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|
|
|
|
|
public class ModuleDependency |
|
{ |
|
public Entity<TContext> Entity { get; } |
|
public IModuleInfo? Info { get; internal set; } |
|
public bool IsDependencyMet { get; internal set; } |
|
|
|
public ModuleDependency(Entity<TContext> entity, |
|
IModuleInfo? info = null, bool isDependencyMet = false) |
|
{ |
|
Entity = entity; |
|
Info = info; |
|
IsDependencyMet = isDependencyMet; |
|
} |
|
} |
|
|
|
internal class ModuleInfo<T> : IModuleInfo |
|
where T : IModule |
|
{ |
|
public Entity<TContext> Entity { get; } |
|
public IReadOnlyCollection<ModuleDependency> Dependencies { get; } |
|
public bool IsInitialized { get; private set; } |
|
|
|
public ModuleInfo(Universe<TContext> universe) |
|
{ |
|
var world = universe.World; |
|
|
|
if (T.IsBuiltIn) |
|
{ |
|
Entity = world.LookupPathOrThrow(T.Path); |
|
Dependencies = Array.Empty<ModuleDependency>(); |
|
} |
|
else |
|
{ |
|
var builder = world.New(T.Path); |
|
var deps = new List<ModuleDependency>(); |
|
|
|
builder.Add<Module>(); |
|
foreach (var dependsPath in T.Dependencies) { |
|
var dependency = world.LookupPathOrNull(dependsPath) ?? |
|
world.New(dependsPath).Add<Module>().Add<Disabled>().Build(); |
|
|
|
var depModule = universe.Modules.Lookup(dependency); |
|
var isDepInit = (depModule?.IsInitialized == true); |
|
|
|
deps.Add(new(dependency, depModule, isDepInit)); |
|
if (!isDepInit) builder.Add<Disabled>(); |
|
builder.Add<DependsOn>(dependency); |
|
} |
|
|
|
Entity = builder.Build().CreateLookup<T>(); |
|
Dependencies = deps.AsReadOnly(); |
|
|
|
// Ensure all parent entities have the Module tag set. |
|
for (var p = Entity.Parent; p is Entity<TContext> parent; p = parent.Parent) |
|
parent.Add<Module>(); |
|
} |
|
} |
|
|
|
public void Enable() |
|
{ |
|
Entity.Enable(); |
|
T.Initialize(Entity); |
|
IsInitialized = true; |
|
} |
|
} |
|
}
|
|
|