-
Notifications
You must be signed in to change notification settings - Fork 22
Hide modding-related types from TypeHelper.FindType #67
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using BaseX; | ||
using HarmonyLib; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
|
||
namespace NeosModLoader | ||
{ | ||
internal static class AssemblyHider | ||
{ | ||
private static HashSet<Assembly>? neosAssemblies; | ||
private static HashSet<Assembly>? modAssemblies; | ||
|
||
/// <summary> | ||
/// Patch Neos's type lookup code to not see mod-related types. This is needed, because users can pass | ||
/// arbitrary strings to TypeHelper.FindType(), which can be used to detect if someone is running mods. | ||
/// </summary> | ||
/// <param name="harmony">Our NML harmony instance</param> | ||
/// <param name="initialAssemblies">Assemblies that were loaded when NML first started</param> | ||
internal static void PatchNeos(Harmony harmony, HashSet<Assembly> initialAssemblies) | ||
{ | ||
if (ModLoaderConfiguration.Get().HideModTypes) | ||
{ | ||
neosAssemblies = GetNeosAssemblies(initialAssemblies); | ||
modAssemblies = GetModAssemblies(); | ||
MethodInfo target = AccessTools.DeclaredMethod(typeof(TypeHelper), nameof(TypeHelper.FindType)); | ||
MethodInfo patch = AccessTools.DeclaredMethod(typeof(AssemblyHider), nameof(FindTypePostfix)); | ||
harmony.Patch(target, postfix: new HarmonyMethod(patch)); | ||
} | ||
} | ||
|
||
private static HashSet<Assembly> GetNeosAssemblies(HashSet<Assembly> initialAssemblies) | ||
{ | ||
// Remove NML itself, as its types should be hidden but it's guaranteed to be loaded. | ||
initialAssemblies.Remove(Assembly.GetExecutingAssembly()); | ||
|
||
// Remove Harmony, as users who aren't using nml_libs will already have it loaded. | ||
initialAssemblies.Remove(typeof(Harmony).Assembly); | ||
|
||
return initialAssemblies; | ||
} | ||
|
||
private static HashSet<Assembly> GetModAssemblies() | ||
{ | ||
// start with ALL assemblies | ||
HashSet<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().ToHashSet(); | ||
|
||
// remove assemblies that already existed before NML loaded | ||
assemblies.ExceptWith(neosAssemblies); | ||
|
||
return assemblies; | ||
} | ||
|
||
private static void FindTypePostfix(ref Type? __result) | ||
{ | ||
if (__result != null && !neosAssemblies!.Contains(__result.Assembly)) | ||
{ | ||
if (!modAssemblies!.Contains(__result.Assembly)) | ||
{ | ||
// an assembly was in neither neosAssemblies nor modAssemblies | ||
// this implies someone late-loaded an assembly after NML, and it was later used in-game | ||
// this is super weird, and probably shouldn't ever happen... but if it does, I want to know about it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wont any plugin loaded after nml is loaded cause this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, plugins are loaded before NML starts executing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we could add another config option to allow late loaded types thru our filter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's doable. I'm just not sure who'd be late loading types at all, hence the warning log. |
||
Logger.WarnInternal($"The \"{__result}\" type does not appear to part of Neos or a mod. It is unclear whether it should be hidden or not."); | ||
} | ||
else | ||
{ | ||
Type type = __result; | ||
Logger.DebugFuncInternal(() => $"Hid type \"{type}\" from Neos"); | ||
} | ||
|
||
// Pretend the type doesn't exist | ||
__result = null; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using HarmonyLib; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
|
||
namespace NeosModLoader | ||
{ | ||
// this class does all the harmony-related NML work. | ||
// this is needed to avoid importing harmony in ExecutionHook, where it may not be loaded yet. | ||
internal class HarmonyWorker | ||
{ | ||
internal static void LoadModsAndHideModAssemblies(HashSet<Assembly> initialAssemblies) | ||
{ | ||
Harmony harmony = new("com.neosmodloader"); | ||
ModLoader.LoadMods(harmony); | ||
AssemblyHider.PatchNeos(harmony, initialAssemblies); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably add a comment here clarifying why a specific case is needed.
afak in theory, if nml is the only thing a user is using that loads harmony, and the user followed the install instructions of placing harmony in nml_libs, initialAssemblies shouldn't contain harmony.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment added in f9c6f22. The install instructions weren't always to use nml_libs, and I don't want to punish users who followed the old steps.