using System.Collections.Concurrent; namespace ICRebuild.Core; // Every resource is registered before activation, including during partial startup. public sealed class ResourceScope : IDisposable { private readonly Stack resources = new(); private bool disposed; public T Own(T resource) where T : IDisposable { ObjectDisposedException.ThrowIf(disposed, this); resources.Push(resource); return resource; } public void Defer(Action action) => Own(new ActionDisposable(action)); public void Dispose() { if (disposed) return; disposed = true; List errors = []; while (resources.TryPop(out var resource)) try { resource.Dispose(); } catch (Exception ex) { errors.Add(ex); } if (errors.Count > 0) throw new AggregateException("资源释放失败", errors); } } public sealed class ActionDisposable(Action action) : IDisposable { private Action? release = action; public void Dispose() => Interlocked.Exchange(ref release, null)?.Invoke(); } public sealed class ModuleManager : IDisposable { private readonly Dictionary> factories = []; private readonly ConcurrentDictionary active = []; private readonly Dictionary dependencies = []; private readonly Dictionary unavailable = []; private readonly HashSet cleanupFailed = []; private bool disposed; public Dictionary Status { get; } = Features.All.ToDictionary(f => f.Id, f => new ModuleStatus(f)); public bool IsEnabled(int id) => active.ContainsKey(id); public void Register(int id, Action start, params int[] required) { factories.Add(id, start); dependencies[id] = required; Status[id].HasImplementation=true; Status[id].Detail = "已关闭"; } public void Unavailable(int id, string reason) { unavailable[id] = reason; if (Status.TryGetValue(id, out var state)) state.Detail = reason; } public void ClearUnavailable(int id) => unavailable.Remove(id); public bool Enable(int id) { ObjectDisposedException.ThrowIf(disposed, this); if (!Status.TryGetValue(id, out var state)) return false; if (cleanupFailed.Contains(id)) { state.Detail="上次资源释放失败,重载插件前禁止重复挂接"; return false; } if (unavailable.TryGetValue(id, out var blockedReason)) { state.State = ModuleState.Failed; state.Detail = blockedReason; return false; } state.Requested = true; if (active.ContainsKey(id)) return true; if (!factories.TryGetValue(id, out var start)) return false; var missing = dependencies[id].Where(x => !IsEnabled(x)).ToArray(); if (missing.Length > 0) { state.State = ModuleState.Failed; state.Detail = $"先启用依赖:{string.Join(", ", missing)}"; return false; } var scope = new ResourceScope(); try { start(scope); if (!active.TryAdd(id, scope)) throw new InvalidOperationException("模块已经启用"); state.State = ModuleState.Enabled; if(state.Adaptation!=Adaptation.Verified) state.Adaptation = Adaptation.Testable; state.Detail = "已开启"; return true; } catch (Exception ex) { try { scope.Dispose(); } catch (Exception cleanup) { cleanupFailed.Add(id); ex = new AggregateException(ex, cleanup); } state.State = ModuleState.Failed; state.Detail = ex.Message; return false; } } public void Disable(int id) { if (!Status.TryGetValue(id, out var state)) return; foreach (var dependent in dependencies.Where(x => x.Value.Contains(id) && IsEnabled(x.Key)).Select(x => x.Key).ToArray()) Disable(dependent); state.Requested = false; if (!active.TryRemove(id, out var scope)) return; try { scope.Dispose(); state.State = ModuleState.Disabled; state.Detail = "已关闭"; } catch (Exception ex) { cleanupFailed.Add(id); state.State = ModuleState.Failed; state.Detail = $"卸载失败:{ex.Message}"; } } public void DisableAll() { foreach (var id in active.Keys.Reverse().ToArray()) Disable(id); } public void Dispose() { if (disposed) return; DisableAll(); disposed = true; } }