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.
 
 

118 lines
3.3 KiB

using System;
using System.Collections.Generic;
using System.Reflection;
using gaemstone.ECS;
using gaemstone.Utility;
using static gaemstone.Flecs.Core;
using Module = gaemstone.Flecs.Core.Module;
namespace gaemstone;
public class ModuleManager
{
private readonly Dictionary<Entity, ModuleInfo> _modules = new();
public Universe Universe { get; }
public ModuleManager(Universe universe)
=> Universe = universe;
internal ModuleInfo? Lookup(Entity entity)
=> _modules.GetValueOrDefault(entity);
public EntityRef Register<T>()
where T : class, IModule, new()
{
var moduleType = typeof(T);
if (moduleType.IsGenericType) throw new Exception(
$"Module {moduleType} must be a non-generic class");
var module = new ModuleInfo<T>(Universe);
_modules.Add(module.Entity, module);
TryEnableModule(module);
return module.Entity;
}
private void TryEnableModule(ModuleInfo module)
{
if (module.UnmetDependencies.Count > 0) return;
Console.WriteLine($"Enabling module {module.Entity.GetFullPath()} ...");
module.Enable();
// Find other modules that might be missing this module as a dependency.
foreach (var other in _modules.Values) {
if (other.IsActive) continue;
if (!other.UnmetDependencies.Contains(module.Entity)) continue;
// Move the just enabled module from unmet to met depedencies.
other.UnmetDependencies.Remove(module.Entity);
other.MetDependencies.Add(module);
TryEnableModule(other);
}
}
internal abstract class ModuleInfo
{
public abstract EntityRef Entity { get; }
public abstract bool IsActive { get; }
public HashSet<ModuleInfo> MetDependencies { get; } = new();
public HashSet<Entity> UnmetDependencies { get; } = new();
public abstract void Enable();
}
internal class ModuleInfo<T> : ModuleInfo
where T : IModule, new()
{
public override EntityRef Entity { get; }
public override bool IsActive => (Instance != null);
public T? Instance { get; internal set; }
public ModuleInfo(Universe universe)
{
var builder = universe.New(T.ModulePath).Add<Module>();
foreach (var dependsPath in T.Dependencies) {
var dependency = universe.LookupByPath(dependsPath) ??
universe.New(dependsPath).Add<Module>().Disable().Build();
var depModule = universe.Modules.Lookup(dependency);
if (depModule?.IsActive == true) MetDependencies.Add(depModule);
else { UnmetDependencies.Add(dependency); builder.Disable(); }
builder.Add<DependsOn>(dependency);
}
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 override void Enable()
{
Entity.Enable();
Instance = new T();
(Instance as IModuleAutoRegisterComponents)?.RegisterComponents(Entity);
(Instance as IModuleInitializer)?.Initialize(Entity);
RegisterMethods(Instance);
}
private void RegisterMethods(object? instance)
{
var world = Entity.World;
foreach (var method in typeof(T).GetMethods(
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance
)) {
if (method.Has<SystemAttribute>())
world.InitSystem(instance, method).ChildOf(Entity);
if (method.Has<ObserverAttribute>())
world.InitObserver(instance, method).ChildOf(Entity);
}
}
}
}