Skip to content

Commit

Permalink
#183 Add delayed mod loader (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
polycone authored Jul 29, 2023
1 parent e63e348 commit 316a148
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 36 deletions.
64 changes: 64 additions & 0 deletions MultiplayerMod/Core/Loader/DelayedModLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using KMod;
using MultiplayerMod.Core.Extensions;
using MultiplayerMod.Core.Logging;
using MultiplayerMod.Core.Patch;

namespace MultiplayerMod.Core.Loader;

public class DelayedModLoader {

private readonly Logging.Logger log = LoggerFactory.GetLogger<DelayedModLoader>();

private readonly Harmony harmony;
private readonly Assembly assembly;
private readonly IReadOnlyList<Mod> mods;

public DelayedModLoader(Harmony harmony, Assembly assembly, IReadOnlyList<Mod> mods) {
this.harmony = harmony;
this.assembly = assembly;
this.mods = mods;
}

public void OnLoad() {
PrioritizedPatch();
assembly.GetTypes()
.Where(type => typeof(IModComponentLoader).IsAssignableFrom(type) && type.IsClass)
.OrderBy(type => type.GetCustomAttribute<ModComponentOrder>()?.Order ?? ModComponentOrder.Default)
.ForEach(
type => {
var instance = (IModComponentLoader) Activator.CreateInstance(type);
log.Debug($"Running mod component loader {type.FullName}");
instance.OnLoad(harmony);
}
);
}

private void PrioritizedPatch() => AccessTools.GetTypesFromAssembly(assembly)
.Where(it => it != typeof(LaunchInitializerPatch))
.Select(it => TryCreateClassProcessor(harmony, it))
.NotNull()
.Where(it => it.containerAttributes != null)
.OrderByDescending(it => it.containerAttributes.priority)
.ForEach(it => it.Patch());

private PatchClassProcessor? TryCreateClassProcessor(Harmony harmony, Type type) {
var optional = type.GetCustomAttribute<HarmonyOptionalAttribute>() != null;
try {
return harmony.CreateClassProcessor(type);
} catch (Exception exception) {
if (optional) {
log.Trace(() => $"Unable to create class processor for patch {type.FullName}\n{exception}");
log.Info($"Optional patch {type.FullName} is omitted");
} else {
throw;
}
}
return null;
}

}
17 changes: 17 additions & 0 deletions MultiplayerMod/Core/Loader/LaunchInitializerPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using HarmonyLib;

namespace MultiplayerMod.Core.Loader;

[HarmonyPatch(typeof(LaunchInitializer))]
public static class LaunchInitializerPatch {

public static DelayedModLoader Loader { get; set; } = null!;

// ReSharper disable once UnusedMember.Local
[HarmonyPrefix]
[HarmonyPatch(nameof(LaunchInitializer.DeleteLingeringFiles))]
private static void LaunchInitializerBeforeDeleteLingeringFiles() {
Loader.OnLoad();
}

}
42 changes: 6 additions & 36 deletions MultiplayerMod/Core/Loader/ModLoader.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using KMod;
using MultiplayerMod.Core.Extensions;
using MultiplayerMod.Core.Logging;
using MultiplayerMod.Core.Patch;

namespace MultiplayerMod.Core.Loader;

Expand All @@ -16,40 +13,13 @@ public class ModLoader : UserMod2 {

public override void OnLoad(Harmony harmony) {
var version = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
log.Info($"Version: {version}");
PrioritizedPatch(harmony);
assembly.GetTypes()
.Where(type => typeof(IModComponentLoader).IsAssignableFrom(type) && type.IsClass)
.OrderBy(type => type.GetCustomAttribute<ModComponentOrder>()?.Order ?? ModComponentOrder.Default)
.ForEach(
type => {
var instance = (IModComponentLoader) Activator.CreateInstance(type);
log.Debug($"Running mod component loader {type.FullName}");
instance.OnLoad(harmony);
}
);
log.Info($"Multiplayer mod version: {version}");
harmony.CreateClassProcessor(typeof(LaunchInitializerPatch)).Patch();
}

private void PrioritizedPatch(Harmony harmony) => AccessTools.GetTypesFromAssembly(assembly)
.Select(it => TryCreateClassProcessor(harmony, it))
.NotNull()
.Where(it => it.containerAttributes != null)
.OrderByDescending(it => it.containerAttributes.priority)
.ForEach(it => it.Patch());

private PatchClassProcessor? TryCreateClassProcessor(Harmony harmony, Type type) {
var optional = type.GetCustomAttribute<HarmonyOptionalAttribute>() != null;
try {
return harmony.CreateClassProcessor(type);
} catch (Exception exception) {
if (optional) {
log.Trace(() => $"Unable to create class processor for patch {type.FullName}\n{exception}");
log.Info($"Optional patch {type.FullName} is omitted");
} else {
throw;
}
}
return null;
public override void OnAllModsLoaded(Harmony harmony, IReadOnlyList<Mod> mods) {
LaunchInitializerPatch.Loader = new DelayedModLoader(harmony, assembly, mods);
log.Info("Delayed loader initialized");
}

}

0 comments on commit 316a148

Please sign in to comment.