Skip to content
This repository was archived by the owner on Feb 28, 2024. It is now read-only.

Commit 0a7025c

Browse files
authored
Merge pull request #19 from ljoonal/libraries-loading
Allow loading libraries from `nml_libs` folder
2 parents 8160092 + 20fe6dc commit 0a7025c

File tree

9 files changed

+153
-80
lines changed

9 files changed

+153
-80
lines changed

NeosModLoader/ModAssembly.cs renamed to NeosModLoader/AssemblyFile.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace NeosModLoader
44
{
5-
internal class ModAssembly
5+
internal class AssemblyFile
66
{
77
internal string File { get; }
88
internal Assembly Assembly { get; set; }
9-
internal ModAssembly(string file)
9+
internal AssemblyFile(string file)
1010
{
1111
File = file;
1212
}

NeosModLoader/AssemblyLoader.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reflection;
5+
6+
namespace NeosModLoader
7+
{
8+
internal static class AssemblyLoader
9+
{
10+
private static AssemblyFile[] GetAssembliesFromDir(string dirName)
11+
{
12+
string assembliesDirectory = Path.Combine(Directory.GetCurrentDirectory(), dirName);
13+
14+
Logger.MsgInternal($"loading assemblies from {dirName}");
15+
16+
AssemblyFile[] assembliesToLoad = null;
17+
try
18+
{
19+
assembliesToLoad = Directory.GetFiles(assembliesDirectory, "*.dll")
20+
.Select(file => new AssemblyFile(file))
21+
.ToArray();
22+
23+
Array.Sort(assembliesToLoad, (a, b) => string.CompareOrdinal(a.File, b.File));
24+
}
25+
catch (Exception e)
26+
{
27+
if (e is DirectoryNotFoundException)
28+
{
29+
Logger.MsgInternal($"{dirName} directory not found, creating it now.");
30+
try
31+
{
32+
Directory.CreateDirectory(assembliesDirectory);
33+
}
34+
catch (Exception e2)
35+
{
36+
Logger.ErrorInternal($"Error creating ${dirName} directory:\n{e2}");
37+
}
38+
}
39+
else
40+
{
41+
Logger.ErrorInternal($"Error enumerating ${dirName} directory:\n{e}");
42+
}
43+
}
44+
return assembliesToLoad;
45+
}
46+
47+
private static void LoadAssembly(AssemblyFile assemblyFile)
48+
{
49+
string filename = Path.GetFileName(assemblyFile.File);
50+
SplashChanger.SetCustom($"Loading file: {filename}");
51+
Assembly assembly;
52+
try
53+
{
54+
Logger.DebugInternal($"load assembly {filename}");
55+
assembly = Assembly.LoadFile(assemblyFile.File);
56+
}
57+
catch (Exception e)
58+
{
59+
Logger.ErrorInternal($"error loading assembly from {assemblyFile.File}: {e}");
60+
return;
61+
}
62+
if (assembly == null)
63+
{
64+
Logger.ErrorInternal($"unexpected null loading assembly from {assemblyFile.File}");
65+
return;
66+
}
67+
assemblyFile.Assembly = assembly;
68+
}
69+
70+
internal static AssemblyFile[] LoadAssembliesFromDir(string dirName) {
71+
var assembliesOrNull = GetAssembliesFromDir(dirName);
72+
if (assembliesOrNull is AssemblyFile[] assembliesToLoad) {
73+
foreach (AssemblyFile assemblyFile in assembliesToLoad)
74+
{
75+
try
76+
{
77+
LoadAssembly(assemblyFile);
78+
}
79+
catch (Exception e)
80+
{
81+
Logger.ErrorInternal($"Unexpected exception loading assembly from {assemblyFile.File}:\n{e}");
82+
}
83+
}
84+
}
85+
86+
return assembliesOrNull;
87+
}
88+
}
89+
}

NeosModLoader/ExecutionHook.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ static ExecutionHook()
1616
{
1717
try
1818
{
19+
SplashChanger.SetCustom("Loading libraries");
20+
AssemblyLoader.LoadAssembliesFromDir("nml_libs");
1921
SplashChanger.SetCustom("Initializing");
2022
DebugInfo.Log();
2123
NeosVersionReset.Initialize();

NeosModLoader/LoadedNeosMod.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
{
33
internal class LoadedNeosMod
44
{
5-
internal LoadedNeosMod(NeosMod neosMod, ModAssembly modAssembly)
5+
internal LoadedNeosMod(NeosMod neosMod, AssemblyFile modAssembly)
66
{
77
NeosMod = neosMod;
88
ModAssembly = modAssembly;
99
}
1010

1111
internal NeosMod NeosMod { get; private set; }
12-
internal ModAssembly ModAssembly { get; private set; }
12+
internal AssemblyFile ModAssembly { get; private set; }
1313
internal ModConfiguration ModConfiguration { get; set; }
1414
internal bool AllowSavingConfiguration = true;
1515
internal bool FinishedLoading { get => NeosMod.FinishedLoading; set => NeosMod.FinishedLoading = value; }

NeosModLoader/ModLoader.cs

Lines changed: 6 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -39,58 +39,18 @@ internal static void LoadMods()
3939
}
4040
SplashChanger.SetCustom("Looking for mods");
4141

42-
string modDirectory = Path.Combine(Directory.GetCurrentDirectory(), "nml_mods");
43-
44-
Logger.MsgInternal($"loading mods from {modDirectory}");
45-
4642
// generate list of assemblies to load
47-
ModAssembly[] modsToLoad = null;
48-
try
49-
{
50-
modsToLoad = Directory.GetFiles(modDirectory, "*.dll")
51-
.Select(file => new ModAssembly(file))
52-
.ToArray();
53-
54-
Array.Sort(modsToLoad, (a, b) => string.CompareOrdinal(a.File, b.File));
55-
}
56-
catch (Exception e)
57-
{
58-
if (e is DirectoryNotFoundException)
59-
{
60-
Logger.MsgInternal("mod directory not found, creating it now.");
61-
try
62-
{
63-
Directory.CreateDirectory(modDirectory);
64-
}
65-
catch (Exception e2)
66-
{
67-
Logger.ErrorInternal($"Error creating mod directory:\n{e2}");
68-
}
69-
}
70-
else
71-
{
72-
Logger.ErrorInternal($"Error enumerating mod directory:\n{e}");
73-
}
43+
AssemblyFile[] modsToLoad;
44+
if (AssemblyLoader.LoadAssembliesFromDir("nml_mods") is AssemblyFile[] arr) {
45+
modsToLoad = arr;
46+
} else {
7447
return;
7548
}
7649

7750
ModConfiguration.EnsureDirectoryExists();
7851

79-
// mods assemblies are all loaded before hooking begins so mods can interconnect if needed
80-
foreach (ModAssembly mod in modsToLoad)
81-
{
82-
try
83-
{
84-
LoadAssembly(mod);
85-
}
86-
catch (Exception e)
87-
{
88-
Logger.ErrorInternal($"Unexpected exception loading mod assembly from {mod.File}:\n{e}");
89-
}
90-
}
91-
9252
// call Initialize() each mod
93-
foreach (ModAssembly mod in modsToLoad)
53+
foreach (AssemblyFile mod in modsToLoad)
9454
{
9555
try
9656
{
@@ -183,30 +143,8 @@ private static string TypesForOwner(Patches patches, string owner)
183143
return $"prefix={prefixCount}; postfix={postfixCount}; transpiler={transpilerCount}; finalizer={finalizerCount}";
184144
}
185145

186-
private static void LoadAssembly(ModAssembly mod)
187-
{
188-
SplashChanger.SetCustom($"Loading file: {mod.File}");
189-
Assembly assembly;
190-
try
191-
{
192-
Logger.DebugInternal($"Loading assembly [{Path.GetFileName(mod.File)}]");
193-
assembly = Assembly.LoadFile(mod.File);
194-
}
195-
catch (Exception e)
196-
{
197-
Logger.ErrorInternal($"error loading assembly from {mod.File}: {e}");
198-
return;
199-
}
200-
if (assembly == null)
201-
{
202-
Logger.ErrorInternal($"unexpected null loading assembly from {mod.File}");
203-
return;
204-
}
205-
mod.Assembly = assembly;
206-
}
207-
208146
// loads mod class and mod config
209-
private static LoadedNeosMod InitializeMod(ModAssembly mod)
147+
private static LoadedNeosMod InitializeMod(AssemblyFile mod)
210148
{
211149
if (mod.Assembly == null)
212150
{

NeosModLoader/ModLoaderConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ internal static ModLoaderConfiguration Get()
4747
{
4848
_configuration.NoMods = true;
4949
}
50+
else if ("nolibraries".Equals(key) && "true".Equals(value))
51+
{
52+
_configuration.NoLibraries = true;
53+
}
5054
else if ("advertiseversion".Equals(key) && "true".Equals(value))
5155
{
5256
_configuration.AdvertiseVersion = true;
@@ -89,6 +93,7 @@ private static string GetAssemblyDirectory()
8993
public bool Debug { get; private set; } = false;
9094
public bool HideVisuals { get; private set; } = false;
9195
public bool NoMods { get; private set; } = false;
96+
public bool NoLibraries { get; private set; } = false;
9297
public bool AdvertiseVersion { get; private set; } = false;
9398
public bool LogConflicts { get; private set; } = true;
9499
}

NeosModLoader/SplashChanger.cs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using FrooxEngine;
2-
using HarmonyLib;
32
using System;
3+
using System.Reflection;
44

55
namespace NeosModLoader
66
{
@@ -9,6 +9,44 @@ namespace NeosModLoader
99
internal static class SplashChanger
1010
{
1111
private static bool failed = false;
12+
13+
private static MethodInfo _updatePhase = null;
14+
private static MethodInfo UpdatePhase {
15+
get {
16+
if (_updatePhase is null) {
17+
try {
18+
_updatePhase = typeof(Engine)
19+
.GetMethod("UpdateInitPhase", BindingFlags.NonPublic | BindingFlags.Instance);
20+
} catch (Exception ex) {
21+
if (!failed)
22+
{
23+
Logger.WarnInternal("UpdatePhase not found: " + ex.ToString());
24+
}
25+
failed = true;
26+
}
27+
}
28+
return _updatePhase;
29+
}
30+
}
31+
private static MethodInfo _updateSubPhase = null;
32+
private static MethodInfo UpdateSubPhase {
33+
get {
34+
if (_updatePhase is null) {
35+
try {
36+
_updateSubPhase = typeof(Engine)
37+
.GetMethod("UpdateInitPhase", BindingFlags.NonPublic | BindingFlags.Instance);
38+
} catch (Exception ex) {
39+
if (!failed)
40+
{
41+
Logger.WarnInternal("UpdateSubPhase not found: " + ex.ToString());
42+
}
43+
failed = true;
44+
}
45+
}
46+
return _updateSubPhase;
47+
}
48+
}
49+
1250
// Returned true means success, false means something went wrong.
1351
internal static bool SetCustom(string text)
1452
{
@@ -18,12 +56,8 @@ internal static bool SetCustom(string text)
1856
// VerboseInit does extra logging, so turning it if off while we change the phase.
1957
bool ogVerboseInit = Engine.Current.VerboseInit;
2058
Engine.Current.VerboseInit = false;
21-
Traverse.Create(Engine.Current)
22-
.Method("UpdateInitPhase", new Type[] { typeof(string), typeof(bool) })
23-
.GetValue("~ NeosModLoader ~", false);
24-
Traverse.Create(Engine.Current)
25-
.Method("UpdateInitSubphase", new Type[] { typeof(string), typeof(bool) })
26-
.GetValue(text, false);
59+
UpdatePhase.Invoke(Engine.Current, new object[] { "~ NeosModLoader ~", false });
60+
UpdateSubPhase.Invoke(Engine.Current, new object[] { text, false });
2761
Engine.Current.VerboseInit = ogVerboseInit;
2862
return true;
2963
}

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ Your Neos directory should now look similar to the following. Files not related
2020

2121
```
2222
<Neos Install Directory>
23-
│ 0Harmony.dll
2423
│ Neos.exe
2524
│ NeosLauncher.exe
2625
@@ -32,11 +31,16 @@ Your Neos directory should now look similar to the following. Files not related
3231
│ MotionBlurDisable.dll
3332
│ NeosContactsSort.dll
3433
| <More mods go here>
34+
├───nml_libs
35+
│ 0Harmony.dll
36+
| <More libs go here>
3537
3638
└───Libraries
3739
NeosModLoader.dll
3840
```
3941

42+
Note that the libraries can also be in the root of the Neos install directory if you prefer, but the loading of those happens outside of NML itself.
43+
4044
## Finding Mods
4145

4246
A list of known mods is available in the [Neos Mod List](https://github.com/zkxs/neos-mod-list/blob/master/README.md). New mods and updates are also announced in [our Discord][Neos Modding Discord].

doc/modloader_config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Not all keys are required to be present. Missing keys will use the defaults outl
1414
| `debug` | `false` | If `true`, NeosMod.Debug() logs will appear in your log file. Otherwise, they are hidden. |
1515
| `hidevisuals` | `false` | If `true`, NML won't show any visual indication of being installed, such as a loading indicator on the splash screen. |
1616
| `nomods` | `false` | If `true`, mods will not be loaded. |
17+
| `nolibraries` | `false` | If `true`, extra libraries will not be loaded. |
1718
| `advertiseversion` | `false` | If `false`, your version will be spoofed and will resemble `2021.8.29.1240`. If `true`, your version will be left unaltered and will resemble `2021.8.29.1240+NeosModLoader.dll`. This version string is visible to other players under certain circumstances. |
1819
| `unsafe` | `false` | If `true`, the version spoofing safety check is disabled and it will still work even if you have other Neos plugins. DO NOT load plugin components in multiplayer sessions, as it will break things and cause crashes. Plugin components should only be used in your local home or user space. |
1920
| `logconflicts` | `true` | If `false`, conflict logging will be disabled. If `true`, potential mod conflicts will be logged. If `debug` is also `true` this will be more verbose. |

0 commit comments

Comments
 (0)