using WeaveLoader.API; namespace WeaveLoader.Core; /// /// Manages the lifecycle of all loaded mods. /// Catches exceptions from individual mods to prevent one broken mod from crashing the game. /// internal class ModManager { private readonly List _mods = new(); internal int ModCount => _mods.Count; internal void AddMods(IEnumerable mods) { _mods.AddRange(mods); } internal void PreInit() { Logger.Info("--- PreInit phase ---"); foreach (var mod in _mods) SafeCall(mod, "OnPreInit", () => mod.Instance.OnPreInit()); } internal void Init() { Logger.Info("--- Initialize phase ---"); foreach (var mod in _mods) SafeCall(mod, "OnInitialize", () => mod.Instance.OnInitialize()); } internal void PostInit() { Logger.Info("--- PostInitialize phase ---"); foreach (var mod in _mods) SafeCall(mod, "OnPostInitialize", () => mod.Instance.OnPostInitialize()); } internal void Tick() { foreach (var mod in _mods) { try { WithContext(mod, () => mod.Instance.OnTick()); } catch (Exception ex) { Logger.Error($"[{mod.Metadata.Id}] OnTick error: {ex.Message}"); } } } internal void Shutdown() { Logger.Info("--- Shutdown phase ---"); foreach (var mod in _mods) SafeCall(mod, "OnShutdown", () => mod.Instance.OnShutdown()); } private static void SafeCall(ModDiscovery.DiscoveredMod mod, string phase, Action action) { try { WithContext(mod, action); } catch (Exception ex) { Logger.Error($"[{mod.Metadata.Id}] {phase} failed: {ex.Message}"); Logger.Debug(ex.StackTrace ?? ""); } } private static void WithContext(ModDiscovery.DiscoveredMod mod, Action action) { ModContext.ModId = mod.Metadata.Id; ModContext.ModFolder = mod.Folder; try { action(); } finally { ModContext.ModId = null; ModContext.ModFolder = null; } } }