Skip to content

Commit

Permalink
Add ModInitializationBatches
Browse files Browse the repository at this point in the history
  • Loading branch information
Popax21 committed Nov 7, 2023
1 parent 8969c68 commit 6e8a1e0
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 20 deletions.
15 changes: 3 additions & 12 deletions Celeste.Mod.mm/Mod/Everest/Everest.Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,8 @@ internal static void ProcessAssembly(EverestModuleMetadata meta, Assembly asm, T
}
// We should run the map data processors again if new berry types are registered, so that CoreMapDataProcessor assigns them checkpoint IDs and orders.
if (newStrawberriesRegistered && _Initialized) {
Logger.Log(LogLevel.Verbose, "core", $"Assembly {asm.FullName} for module {meta} has custom strawberries: reloading maps.");
AssetReloadHelper.ReloadAllMaps();
Logger.Log(LogLevel.Verbose, "core", $"Assembly {asm.FullName} for module {meta} has custom strawberries: triggering map reload.");
Everest.TriggerModInitMapReload();
}
}

Expand Down Expand Up @@ -814,10 +814,7 @@ void VisitMod(EverestModuleMetadata node) {

// Load modules in the reverse order determined before (dependencies before dependents)
// Delay initialization until all mods have been loaded
Trace.Assert(_DelayedModuleInitializationQueue == null);
try {
_DelayedModuleInitializationQueue = new Queue<EverestModule>();

using (new ModInitializationBatch()) {
foreach (EverestModuleMetadata loadMod in reloadMods.Reverse<EverestModuleMetadata>()) {
if (loadMod.Dependencies.Any(dep => !DependencyLoaded(dep))) {
Logger.Log(LogLevel.Warn, "loader", $"-> skipping reload of mod '{loadMod.Name}' as dependency failed to load");
Expand All @@ -828,12 +825,6 @@ void VisitMod(EverestModuleMetadata node) {
if (!LoadMod(loadMod))
Logger.Log(LogLevel.Warn, "loader", $"-> failed to reload mod '{loadMod.Name}'!");
}
} finally {
Queue<EverestModule> moduleInitQueue = _DelayedModuleInitializationQueue;
_DelayedModuleInitializationQueue = null;

if (moduleInitQueue.Count > 0)
Everest.LateInitializeMods(moduleInitQueue);
}
}, static () => AssetReloadHelper.ReloadLevel(true));
});
Expand Down
81 changes: 73 additions & 8 deletions Celeste.Mod.mm/Mod/Everest/Everest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Net;
Expand Down Expand Up @@ -544,10 +545,10 @@ public static void Register(this EverestModule module) {
module.LoadContent(true);
}
if (_Initialized) {
if (_DelayedModuleInitializationQueue != null)
_DelayedModuleInitializationQueue.Enqueue(module);
if (_ModInitBatch != null)
_ModInitBatch.LateInitializeModule(module);
else
LateInitializeMods(Enumerable.Repeat(module, 1));
LateInitializeModules(Enumerable.Repeat(module, 1));
}

if (Engine.Instance != null && Engine.Scene is Overworld overworld) {
Expand Down Expand Up @@ -581,10 +582,7 @@ public static void Register(this EverestModule module) {
CheckDependenciesOfDelayedMods();
}

[ThreadStatic]
internal static Queue<EverestModule> _DelayedModuleInitializationQueue = null;

internal static void LateInitializeMods(IEnumerable<EverestModule> modules) {
internal static void LateInitializeModules(IEnumerable<EverestModule> modules) {
// Re-initialize the tracker
Tracker.Initialize();

Expand Down Expand Up @@ -627,12 +625,79 @@ internal static void LateInitializeMods(IEnumerable<EverestModule> modules) {
foreach (EverestModule module in modules) {
if (module.GetType().GetMethod("PrepareMapDataProcessors", new Type[] { typeof(MapDataFixup) })?.DeclaringType == module.GetType()) {
Logger.Log(LogLevel.Verbose, "core", $"Module {module.Metadata} has map data processors: reloading maps.");
AssetReloadHelper.ReloadAllMaps();
TriggerModInitMapReload();
break;
}
}
}

[ThreadStatic]
private static ModInitializationBatch _ModInitBatch;

internal class ModInitializationBatch : IDisposable {
public bool IsActive { get; private set; }

private bool shouldReloadMaps = false;
private Queue<EverestModule> lateModuleInitQueue = null;

public ModInitializationBatch() {
if (_ModInitBatch == null)
return;

IsActive = true;
}

public void Dispose() {
if (!IsActive)
return;
Trace.Assert(_ModInitBatch == this);

// Flush the batch
Flush();

// Reset the mod init batch
_ModInitBatch = null;
IsActive = false;
}

public void ReloadMaps() {
if (!IsActive)
throw new InvalidOperationException("Can't add tasks to an inactive mod initialization batch");
shouldReloadMaps = true;
}

public void LateInitializeModule(EverestModule module) {
if (!IsActive)
throw new InvalidOperationException("Can't add tasks to an inactive mod initialization batch");
lateModuleInitQueue.Enqueue(module);
}

public void Flush() {
if (!IsActive)
return;

// Flush the module late-initialization queue
// This might set shouldReloadMaps
if (lateModuleInitQueue.Count > 0) {
LateInitializeModules(lateModuleInitQueue);
lateModuleInitQueue.Clear();
}

// Reload maps if we should
if (shouldReloadMaps) {
AssetReloadHelper.ReloadAllMaps();
shouldReloadMaps = false;
}
}
}

internal static void TriggerModInitMapReload() {
if (_ModInitBatch != null)
_ModInitBatch.ReloadMaps();
else
AssetReloadHelper.ReloadAllMaps();
}

internal static void CheckDependenciesOfDelayedMods() {
// Attempt to load mods after their dependencies have been loaded.
// Only load and lock the delayed list if we're not already loading delayed mods.
Expand Down

0 comments on commit 6e8a1e0

Please sign in to comment.