diff --git a/Data/latest-version.txt b/Data/latest-version.txt
index fc931ed3..094c9526 100644
--- a/Data/latest-version.txt
+++ b/Data/latest-version.txt
@@ -1 +1 @@
-4.1.4.0
\ No newline at end of file
+4.2.0.0
\ No newline at end of file
diff --git a/Dependencies/BZ.EXP/BepInEx.cfg b/Dependencies/BZ.EXP/BepInEx.cfg
new file mode 100644
index 00000000..e15c9378
--- /dev/null
+++ b/Dependencies/BZ.EXP/BepInEx.cfg
@@ -0,0 +1,16 @@
+[Preloader.Entrypoint]
+
+## The local filename of the assembly to target.
+# Setting type: String
+# Default value: UnityEngine.CoreModule.dll
+Assembly = Assembly-CSharp.dll
+
+## The name of the type in the entrypoint assembly to search for the entrypoint method.
+# Setting type: String
+# Default value: Application
+Type = PreStartScreen
+
+## The name of the method in the specified entrypoint assembly and type to hook and load Chainloader from.
+# Setting type: String
+# Default value: .cctor
+Method = Start
diff --git a/Dependencies/BZ.STABLE/BepInEx.cfg b/Dependencies/BZ.STABLE/BepInEx.cfg
new file mode 100644
index 00000000..e15c9378
--- /dev/null
+++ b/Dependencies/BZ.STABLE/BepInEx.cfg
@@ -0,0 +1,16 @@
+[Preloader.Entrypoint]
+
+## The local filename of the assembly to target.
+# Setting type: String
+# Default value: UnityEngine.CoreModule.dll
+Assembly = Assembly-CSharp.dll
+
+## The name of the type in the entrypoint assembly to search for the entrypoint method.
+# Setting type: String
+# Default value: Application
+Type = PreStartScreen
+
+## The name of the method in the specified entrypoint assembly and type to hook and load Chainloader from.
+# Setting type: String
+# Default value: .cctor
+Method = Start
diff --git a/Dependencies/BepInEx/BepInEx/patchers/BepInEx.MultiFolderLoader.dll b/Dependencies/BepInEx/BepInEx/patchers/BepInEx.MultiFolderLoader.dll
new file mode 100644
index 00000000..138e5768
Binary files /dev/null and b/Dependencies/BepInEx/BepInEx/patchers/BepInEx.MultiFolderLoader.dll differ
diff --git a/Dependencies/BepInEx/doorstop_config.ini b/Dependencies/BepInEx/doorstop_config.ini
index a68f30f1..2e860502 100644
--- a/Dependencies/BepInEx/doorstop_config.ini
+++ b/Dependencies/BepInEx/doorstop_config.ini
@@ -13,4 +13,7 @@ ignoreDisableSwitch=false
# (e.g. mscorlib is stripped in original game)
# This option causes Mono to seek mscorlib and core libraries from a different folder before Managed
# Original Managed folder is added as a secondary folder in the search path
-dllSearchPathOverride=
\ No newline at end of file
+dllSearchPathOverride=
+
+[MultiFolderLoader]
+baseDir=.\QMods
\ No newline at end of file
diff --git a/Dependencies/SN.EXP/BepInEx.cfg b/Dependencies/SN.EXP/BepInEx.cfg
new file mode 100644
index 00000000..e15c9378
--- /dev/null
+++ b/Dependencies/SN.EXP/BepInEx.cfg
@@ -0,0 +1,16 @@
+[Preloader.Entrypoint]
+
+## The local filename of the assembly to target.
+# Setting type: String
+# Default value: UnityEngine.CoreModule.dll
+Assembly = Assembly-CSharp.dll
+
+## The name of the type in the entrypoint assembly to search for the entrypoint method.
+# Setting type: String
+# Default value: Application
+Type = PreStartScreen
+
+## The name of the method in the specified entrypoint assembly and type to hook and load Chainloader from.
+# Setting type: String
+# Default value: .cctor
+Method = Start
diff --git a/Dependencies/SN.STABLE/BepInEx.cfg b/Dependencies/SN.STABLE/BepInEx.cfg
new file mode 100644
index 00000000..40ed9366
--- /dev/null
+++ b/Dependencies/SN.STABLE/BepInEx.cfg
@@ -0,0 +1,16 @@
+[Preloader.Entrypoint]
+
+## The local filename of the assembly to target.
+# Setting type: String
+# Default value: UnityEngine.CoreModule.dll
+Assembly = Assembly-CSharp.dll
+
+## The name of the type in the entrypoint assembly to search for the entrypoint method.
+# Setting type: String
+# Default value: Application
+Type = SystemsSpawner
+
+## The name of the method in the specified entrypoint assembly and type to hook and load Chainloader from.
+# Setting type: String
+# Default value: .cctor
+Method = Awake
diff --git a/Executable/Properties/AssemblyInfo.cs b/Executable/Properties/AssemblyInfo.cs
index d74be537..d9e46dd6 100644
--- a/Executable/Properties/AssemblyInfo.cs
+++ b/Executable/Properties/AssemblyInfo.cs
@@ -12,5 +12,5 @@
[assembly: ComVisible(false)]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
diff --git a/Installer/BZ.EXP.iss b/Installer/BZ.EXP.iss
index 8e53f360..3fa94a28 100644
--- a/Installer/BZ.EXP.iss
+++ b/Installer/BZ.EXP.iss
@@ -5,7 +5,7 @@
#endif
#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.1.4"
+#define Version "4.2"
#define Author "QModManager"
#define URL "https://github.com/QModManager/QModManager"
#define SupportURL "https://discord.gg/UpWuWwq"
@@ -59,7 +59,7 @@ Source: "InstallerExtensions.dll"; Flags: DontCopy
; Files required by QModManager itself
; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
+Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
@@ -76,6 +76,7 @@ Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QMod
; BepInEx
Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
+Source: "..\..\Dependencies\BZ.EXP\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
[Dirs]
Name: "{app}\QMods"
diff --git a/Installer/BZ.STABLE.iss b/Installer/BZ.STABLE.iss
index 21ffb3ea..64fcb3e8 100644
--- a/Installer/BZ.STABLE.iss
+++ b/Installer/BZ.STABLE.iss
@@ -5,7 +5,7 @@
#endif
#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.1.4"
+#define Version "4.2"
#define Author "QModManager"
#define URL "https://github.com/QModManager/QModManager"
#define SupportURL "https://discord.gg/UpWuWwq"
@@ -59,7 +59,7 @@ Source: "InstallerExtensions.dll"; Flags: DontCopy
; Files required by QModManager itself
; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
+Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
@@ -76,6 +76,7 @@ Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QMod
; BepInEx
Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
+Source: "..\..\Dependencies\BZ.STABLE\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
[Dirs]
Name: "{app}\QMods"
diff --git a/Installer/Properties/AssemblyInfo.cs b/Installer/Properties/AssemblyInfo.cs
index 7d03deec..842baa71 100644
--- a/Installer/Properties/AssemblyInfo.cs
+++ b/Installer/Properties/AssemblyInfo.cs
@@ -14,5 +14,5 @@
[assembly: Guid("8c6c9a0b-80c4-43d2-89f2-749e6f09fdda")]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
diff --git a/Installer/SN.EXP.iss b/Installer/SN.EXP.iss
index 4abd8e00..5ff504b7 100644
--- a/Installer/SN.EXP.iss
+++ b/Installer/SN.EXP.iss
@@ -5,7 +5,7 @@
#endif
#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.1.4"
+#define Version "4.2"
#define Author "QModManager"
#define URL "https://github.com/QModManager/QModManager"
#define SupportURL "https://discord.gg/UpWuWwq"
@@ -59,7 +59,7 @@ Source: "InstallerExtensions.dll"; Flags: DontCopy
; Files required by QModManager itself
; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
+Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\Oculus.Newtonsoft.Json.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
@@ -76,6 +76,7 @@ Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QMod
; BepInEx
Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
+Source: "..\..\Dependencies\SN.EXP\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
[Dirs]
Name: "{app}\QMods"
diff --git a/Installer/SN.STABLE.iss b/Installer/SN.STABLE.iss
index 9639659b..c8a57150 100644
--- a/Installer/SN.STABLE.iss
+++ b/Installer/SN.STABLE.iss
@@ -5,7 +5,7 @@
#endif
#define Name "QModManager" ; The name of the game will be added after it
-#define Version "4.1.4"
+#define Version "4.2"
#define Author "QModManager"
#define URL "https://github.com/QModManager/QModManager"
#define SupportURL "https://discord.gg/UpWuWwq"
@@ -59,7 +59,7 @@ Source: "InstallerExtensions.dll"; Flags: DontCopy
; Files required by QModManager itself
; Dependencies
-Source: "..\..\packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
+Source: "..\..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
Source: "..\..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion;
; QMM
@@ -74,6 +74,7 @@ Source: "QModManager.UnityAudioFixer.xml"; DestDir: "{app}\BepInEx\patchers\QMod
; BepInEx
Source: "..\..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt;
+Source: "..\..\Dependencies\SN.STABLE\BepInEx.cfg"; DestDir: "{app}\BepInEx\config"; Flags: ignoreversion sharedfile uninsnosharedfileprompt;
[Dirs]
Name: "{app}\QMods"
diff --git a/OculusNewtonsoftRedirect/Properties/AssemblyInfo.cs b/OculusNewtonsoftRedirect/Properties/AssemblyInfo.cs
index 308f449b..ddd3cc07 100644
--- a/OculusNewtonsoftRedirect/Properties/AssemblyInfo.cs
+++ b/OculusNewtonsoftRedirect/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
diff --git a/QModManager.sln b/QModManager.sln
index d4623aec..bb32a167 100644
--- a/QModManager.sln
+++ b/QModManager.sln
@@ -36,6 +36,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QModManager.QModPluginGener
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QModManager.OculusNewtonsoftRedirect", "OculusNewtonsoftRedirect\QModManager.OculusNewtonsoftRedirect.csproj", "{25558450-FF33-4FDF-91C7-0C5C00A94A57}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Scripts", "Build Scripts", "{075884D3-CC6F-4B56-B050-C31A8E11FA52}"
+ ProjectSection(SolutionItems) = preProject
+ Scripts\QModPluginGenerator-post-build.cmd = Scripts\QModPluginGenerator-post-build.cmd
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
BZ.EXP|Any CPU = BZ.EXP|Any CPU
@@ -224,6 +229,9 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {075884D3-CC6F-4B56-B050-C31A8E11FA52} = {5A8D179E-C749-4346-AD81-05F11C082A1C}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54A73C4F-4981-48CC-AD91-27D3D090D5D4}
EndGlobalSection
diff --git a/QModManager/BepInex/Plugins/LogFilter.cs b/QModManager/BepInex/Plugins/LogFilter.cs
new file mode 100644
index 00000000..9281deef
--- /dev/null
+++ b/QModManager/BepInex/Plugins/LogFilter.cs
@@ -0,0 +1,54 @@
+using BepInEx;
+using HarmonyLib;
+using System.Text.RegularExpressions;
+
+namespace QModInstaller.BepInEx.Plugins
+{
+ ///
+ /// Handles filtering noisy logs from the QModManager logs.
+ ///
+ [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
+ [BepInProcess("Subnautica"), BepInProcess("SubnauticaZero")]
+ internal class LogFilter : BaseUnityPlugin
+ {
+ internal const string PluginGuid = "QModManager.LogFilter";
+ internal const string PluginName = PluginGuid;
+ internal const string PluginVersion = "4.2";
+
+ private void Awake()
+ {
+ var harmony = new Harmony(PluginGuid);
+ harmony.Patch(AccessTools.Method("MirrorInternalLogs.Util.LibcHelper:Format"),
+ postfix: new HarmonyMethod(AccessTools.Method(typeof(LogFilter), nameof(LogFilter.LibcHelper_Format_Postfix))));
+ }
+
+ private readonly static Regex[] DirtyRegexPatterns = new Regex[] {
+ new Regex(@"([\r\n]+)?(\(Filename: .*\))", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(Replacing cell.*)", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(Resetting cell with.*)", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(Fallback handler could not load.*)", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(Heartbeat CSV.*,[0-9])", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(L[0-9]: .*)", RegexOptions.Compiled),
+ new Regex(@"([\r\n]+)?(Kinematic body only supports Speculative Continuous collision detection)", RegexOptions.Compiled)
+ };
+
+ private static void LibcHelper_Format_Postfix(ref string __result)
+ {
+ bool match = false;
+ foreach (Regex pattern in DirtyRegexPatterns)
+ {
+ if (pattern.IsMatch(__result))
+ {
+ __result = pattern.Replace(__result, string.Empty).Trim();
+ match = true;
+ }
+ }
+
+ // if our filtering resulted in an empty string, return null so that MirrorInternalLogs will skip the line
+ if (match && string.IsNullOrWhiteSpace(__result))
+ {
+ __result = null;
+ }
+ }
+ }
+}
diff --git a/QModManager/BepInex/Plugins/QMMLoader.cs b/QModManager/BepInex/Plugins/QMMLoader.cs
new file mode 100644
index 00000000..7e3e57bd
--- /dev/null
+++ b/QModManager/BepInex/Plugins/QMMLoader.cs
@@ -0,0 +1,123 @@
+using BepInEx;
+#if !SUBNAUTICA_STABLE
+using HarmonyLib;
+#if !BELOWZERO
+using System.Collections;
+#endif
+#endif
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace QModInstaller.BepInEx.Plugins
+{
+ using QModManager.API.ModLoading;
+ using QModManager.Patching;
+ using QModManager.Utility;
+
+ ///
+ /// QMMLoader - simply fires up the QModManager entry point.
+ ///
+ [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
+ [BepInProcess("Subnautica"), BepInProcess("SubnauticaZero")]
+ public class QMMLoader : BaseUnityPlugin
+ {
+ internal const string PluginGuid = "QModManager.QMMLoader";
+ internal const string PluginName = "QMMLoader";
+ internal const string PluginVersion = "4.2";
+
+ internal static List QModsToLoad;
+ private static Initializer Initializer;
+
+ ///
+ /// Prevents a default instance of the class from being created
+ /// Also ensures the root bepinex object does not get destroyed if the game reloads for steam.
+ ///
+ private void Awake()
+ {
+ GameObject obj = gameObject;
+
+ while (obj.transform.parent?.gameObject != null)
+ {
+ obj = obj.transform.parent.gameObject;
+ }
+
+ obj.EnsureComponent();
+ DontDestroyOnLoad(obj);
+
+ PreInitializeQMods();
+ }
+
+ private void PreInitializeQMods()
+ {
+ Patcher.Patch(); // Run QModManager patch
+
+ if (QModsToLoad is null)
+ {
+ Logger.LogWarning("QModsToLoad is null!");
+ return;
+ }
+
+ Initializer = new Initializer(Patcher.CurrentlyRunningGame);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.MetaPreInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.PreInitialize);
+
+#if SUBNAUTICA_STABLE
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.NormalInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.PostInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.MetaPostInitialize);
+
+ SummaryLogger.ReportIssues(QModsToLoad);
+ SummaryLogger.LogSummaries(QModsToLoad);
+ foreach (Dialog dialog in Patcher.Dialogs)
+ {
+ dialog.Show();
+ }
+#else
+ var harmony = new Harmony(PluginGuid);
+ harmony.Patch(
+ AccessTools.Method(
+#if SUBNAUTICA
+ typeof(PlatformUtils), nameof(PlatformUtils.PlatformInitAsync)
+#elif BELOWZERO
+ typeof(SpriteManager), nameof(SpriteManager.OnLoadedSpriteAtlases)
+#endif
+ ),
+ postfix: new HarmonyMethod(AccessTools.Method(typeof(QMMLoader), nameof(QMMLoader.InitializeQMods)))
+ );
+#endif
+ }
+
+#if SUBNAUTICA_EXP
+ private static IEnumerator InitializeQMods(IEnumerator result)
+ {
+ yield return result;
+
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.NormalInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.PostInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.MetaPostInitialize);
+
+ SummaryLogger.ReportIssues(QModsToLoad);
+ SummaryLogger.LogSummaries(QModsToLoad);
+ foreach (Dialog dialog in Patcher.Dialogs)
+ {
+ dialog.Show();
+ }
+ }
+#elif BELOWZERO
+ private static void InitializeQMods()
+ {
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.NormalInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.PostInitialize);
+ Initializer.InitializeMods(QModsToLoad, PatchingOrder.MetaPostInitialize);
+
+ SummaryLogger.ReportIssues(QModsToLoad);
+ SummaryLogger.LogSummaries(QModsToLoad);
+
+ foreach (Dialog dialog in Patcher.Dialogs)
+ {
+ dialog.Show();
+ }
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/QModManager/Properties/AssemblyInfo.cs b/QModManager/Properties/AssemblyInfo.cs
index 911d7312..6396d299 100644
--- a/QModManager/Properties/AssemblyInfo.cs
+++ b/QModManager/Properties/AssemblyInfo.cs
@@ -13,8 +13,8 @@
[assembly: ComVisible(false)]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
[assembly: InternalsVisibleTo("QMMTests")]
[assembly: InternalsVisibleTo("QModManager")]
diff --git a/QModManager/QMMLoader.cs b/QModManager/QMMLoader.cs
deleted file mode 100644
index 737d483c..00000000
--- a/QModManager/QMMLoader.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace QModInstaller
-{
- using BepInEx;
- using System;
- using UnityEngine;
-
- ///
- /// QMMLoader - simply fires up the QModManager entry point.
- ///
- [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
- [BepInProcess(SubnauticaProcessName)]
- [BepInProcess(SubnauticaZeroProcessName)]
- public class QMMLoader: BaseUnityPlugin
- {
- internal const string PluginGuid = "QModManager.QMMLoader";
- internal const string PluginName = "QMMLoader";
- internal const string PluginVersion = "1.0.2";
-
- internal const string SubnauticaProcessName = "Subnautica";
- internal const string SubnauticaZeroProcessName = "SubnauticaZero";
-
- ///
- /// Prevents a default instance of the class from being created
- /// Also ensures the root bepinex object does not get destroyed if the game reloads for steam.
- ///
- [Obsolete("DO NOT USE!", true)]
- private QMMLoader()
- {
- GameObject obj = gameObject;
-
- while(obj.transform.parent?.gameObject != null)
- obj = obj.transform.parent.gameObject;
-
- obj.EnsureComponent();
- DontDestroyOnLoad(obj);
- }
- }
-}
\ No newline at end of file
diff --git a/QModManager/QModManager.csproj b/QModManager/QModManager.csproj
index 697fdd42..9c85c1d1 100644
--- a/QModManager/QModManager.csproj
+++ b/QModManager/QModManager.csproj
@@ -78,10 +78,6 @@
..\Dependencies\$(Configuration)\Newtonsoft.Json.dll
False
-
- ..\Dependencies\$(Configuration)\Sentry.dll
- False
-
..\Dependencies\$(Configuration)\UnityEngine.dll
@@ -112,10 +108,17 @@
False
+
+
+ ..\Dependencies\$(Configuration)\Sentry.dll
+ False
+
+
+
@@ -131,7 +134,7 @@
-
+
@@ -167,6 +170,7 @@
+
diff --git a/QModPluginEmulator/Properties/AssemblyInfo.cs b/QModPluginEmulator/Properties/AssemblyInfo.cs
index f9692a53..65db3109 100644
--- a/QModPluginEmulator/Properties/AssemblyInfo.cs
+++ b/QModPluginEmulator/Properties/AssemblyInfo.cs
@@ -33,8 +33,8 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: InternalsVisibleTo("QModManager.QMMLoader")]
diff --git a/QModPluginEmulator/QModManager.QModPluginGenerator.csproj b/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
index ce9c8d23..91da93a4 100644
--- a/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
+++ b/QModPluginEmulator/QModManager.QModPluginGenerator.csproj
@@ -13,42 +13,27 @@
512
true
-
-
- ..\Build\$(Configuration)\
- SUBNAUTICA;SUBNAUTICA_STABLE
true
pdbonly
AnyCPU
7.3
prompt
+
+ ..\Build\$(Configuration)\
+ SUBNAUTICA;SUBNAUTICA_STABLE
+
..\Build\$(Configuration)\
SUBNAUTICA;SUBNAUTICA_EXP
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
..\Build\$(Configuration)\
BELOWZERO;BELOWZERO_STABLE
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
..\Build\$(Configuration)\
BELOWZERO;BELOWZERO_EXP
- true
- pdbonly
- AnyCPU
- 7.3
- prompt
OnBuildSuccess
@@ -100,30 +85,11 @@
QModManager
+
+
+
- rmdir "$(SolutionDir)VortexBuild\$(Configuration)" /q /s
-xcopy "$(SolutionDir)Dependencies\BepInEx" "$(SolutionDir)VortexBuild\$(Configuration)" /E /H /I /Q /Y
-xcopy "$(SolutionDir)Dependencies\cldb.dat" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(SolutionDir)packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModManager.exe" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModManager.QModPluginGenerator.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModManager.UnityAudioFixer.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModManager.UnityAudioFixer.xml" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-
-if NOT "$(ConfigurationName)" =="SN.STABLE" (
-
-xcopy "$(SolutionDir)Dependencies\Oculus.Newtonsoft.Json.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModManager.OculusNewtonsoftRedirect.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\patchers\QModManager\" /I /Q /Y
-)
-
-xcopy "$(TargetDir)QModInstaller.dll" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\plugins\QModManager\" /I /Q /Y
-xcopy "$(TargetDir)QModInstaller.xml" "$(SolutionDir)VortexBuild\$(Configuration)\BepInEx\plugins\QModManager\" /I /Q /Y
-
-powershell Compress-Archive -Path '$(SolutionDir)VortexBuild\$(Configuration)\Bepinex' -DestinationPath '$(SolutionDir)VortexBuild\QModManager_$(Configuration).zip' -Force
-powershell Compress-Archive -LiteralPath '$(SolutionDir)VortexBuild\$(Configuration)\doorstop_config.ini', '$(SolutionDir)VortexBuild\$(Configuration)\winhttp.dll' -DestinationPath '$(SolutionDir)VortexBuild\QModManager_$(Configuration).zip' -Update
-
-echo F|xcopy /S /Q /Y /F "$(SolutionDir)Installer\$(Configuration).iss" "$(TargetDir)\QModsInstallerScript.iss"
-"$(SolutionDir)Dependencies\Inno\ISCC.exe" "$(TargetDir)QModsInstallerScript.iss"
+ call "$(SolutionDir)\Scripts\QModPluginGenerator-post-build.cmd" "$(SolutionDir)" "$(TargetDir)" "$(ConfigurationName)"
\ No newline at end of file
diff --git a/QModPluginEmulator/QModPluginGenerator.cs b/QModPluginEmulator/QModPluginGenerator.cs
index 3ea1978b..283596d3 100644
--- a/QModPluginEmulator/QModPluginGenerator.cs
+++ b/QModPluginEmulator/QModPluginGenerator.cs
@@ -4,6 +4,7 @@
using HarmonyLib;
using Mono.Cecil;
#if SUBNAUTICA_STABLE
+using System.Collections;
using Oculus.Newtonsoft.Json;
#else
using Newtonsoft.Json;
@@ -12,19 +13,18 @@
using QModManager.Patching;
using QModManager.Utility;
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
-using System.Text.RegularExpressions;
-using QModManager.API.ModLoading;
using TypeloaderCache = System.Collections.Generic.Dictionary>;
using QMMAssemblyCache = System.Collections.Generic.Dictionary;
namespace QModManager
{
+ using QModInstaller.BepInEx.Plugins;
+
public static class QModPluginGenerator
{
internal static readonly string QModsPath = Path.Combine(Paths.GameRootPath, "QMods");
@@ -44,36 +44,25 @@ public static class QModPluginGenerator
internal static Dictionary QModsToLoadById;
internal static Dictionary QModPluginInfos;
internal static List InitialisedQModPlugins;
- private static Initializer Initializer;
- private static List ModsToLoad;
private static Harmony Harmony;
internal static IVersionParser VersionParserService { get; set; } = new VersionParser();
private static TypeloaderCache PluginCache;
[Obsolete("Should not be used!", true)]
- public static void Finish()
+ public static void Initialize()
{
try
{
PluginCache = GetPluginCache();
Harmony = new Harmony("QModManager.QModPluginGenerator");
- foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
- {
- if (assembly?.GetName()?.Name?.Contains("MirrorInternalLogs") ?? false)
- {
- Type type = AccessTools.TypeByName("MirrorInternalLogs.Util.LibcHelper");
- var method = type?.GetMethod("Format");
-
- if (method != null)
- Harmony.Patch(method, postfix: new HarmonyMethod(typeof(QModPluginGenerator), nameof(QModPluginGenerator.LibcHelper_Format_Postfix)));
- break;
- }
- }
Harmony.Patch(
typeof(TypeLoader).GetMethod(nameof(TypeLoader.FindPluginTypes)).MakeGenericMethod(typeof(PluginInfo)),
postfix: new HarmonyMethod(typeof(QModPluginGenerator).GetMethod(nameof(TypeLoaderFindPluginTypesPostfix))));
+ Harmony.Patch(
+ typeof(MetadataHelper).GetMethod(nameof(MetadataHelper.GetMetadata), new Type[] { typeof(object) }),
+ prefix: new HarmonyMethod(AccessTools.Method(typeof(QModPluginGenerator), nameof(QModPluginGenerator.MetadataHelperGetMetadataPrefix))));
}
catch (Exception ex)
{
@@ -83,115 +72,8 @@ public static void Finish()
}
}
- private readonly static List DirtyRegexPatterns = new List() {
- new Regex(@"([\r\n]+)?(\(Filename: .*\))$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(Replacing cell.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(Resetting cell with.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(PerformGarbage.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(Fallback handler could not load.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(Heartbeat CSV.*,[0-9])$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(L0: PerformGarbageCollection ->.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(L0: CellManager::EstimateBytes.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- new Regex(@"^(Kinematic body only supports Speculative Continuous collision detection.*)$", RegexOptions.Compiled | RegexOptions.Multiline),
- };
-
- private static void LibcHelper_Format_Postfix(ref string __result)
- {
- foreach (Regex pattern in DirtyRegexPatterns)
- {
- __result = pattern.Replace(__result, string.Empty).Trim();
- }
- }
-
-#if SUBNAUTICA_STABLE
- [HarmonyPatch(typeof(SystemsSpawner), nameof(SystemsSpawner.Awake))]
- [HarmonyPrefix]
- private static void PreInitializeQMM()
- {
- Patcher.Patch(); // Run QModManager patch
-
- ModsToLoad = QModsToLoad.ToList();
- Initializer = new Initializer(Patcher.CurrentlyRunningGame);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.MetaPreInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.PreInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.NormalInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.PostInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.MetaPostInitialize);
-
- SummaryLogger.ReportIssues(ModsToLoad);
- SummaryLogger.LogSummaries(ModsToLoad);
- foreach(Dialog dialog in Patcher.Dialogs)
- {
- dialog.Show();
- }
-
- }
-#else
- [HarmonyPatch(typeof(PreStartScreen), nameof(PreStartScreen.Start))]
- [HarmonyPrefix]
- private static void PreInitializeQMM()
- {
-
-
- Patcher.Patch(); // Run QModManager patch
-
- ModsToLoad = QModsToLoad.ToList();
- Initializer = new Initializer(Patcher.CurrentlyRunningGame);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.MetaPreInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.PreInitialize);
-
- Harmony.Patch(
- AccessTools.Method(
-#if SUBNAUTICA
- typeof(PlatformUtils), nameof(PlatformUtils.PlatformInitAsync)
-#elif BELOWZERO
- typeof(SpriteManager), nameof(SpriteManager.OnLoadedSpriteAtlases)
-#endif
- ), postfix: new HarmonyMethod(AccessTools.Method(typeof(QModPluginGenerator), nameof(QModPluginGenerator.InitializeQMM))));
- }
-
-#if SUBNAUTICA_EXP
- private static IEnumerator InitializeQMM(IEnumerator result)
- {
- if (ModsToLoad != null)
- {
- yield return result;
-
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.NormalInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.PostInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.MetaPostInitialize);
-
- SummaryLogger.ReportIssues(ModsToLoad);
- SummaryLogger.LogSummaries(ModsToLoad);
- foreach (Dialog dialog in Patcher.Dialogs)
- {
- dialog.Show();
- }
- }
- yield break;
- }
-#elif BELOWZERO
- private static void InitializeQMM()
- {
- if (ModsToLoad != null)
- {
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.NormalInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.PostInitialize);
- Initializer.InitializeMods(ModsToLoad, PatchingOrder.MetaPostInitialize);
-
- SummaryLogger.ReportIssues(ModsToLoad);
- SummaryLogger.LogSummaries(ModsToLoad);
- foreach (Dialog dialog in Patcher.Dialogs)
- {
- dialog.Show();
- }
- }
- }
-#endif
-#endif
-
- private static string[] QMMKnownAssemblyPaths = new[] {
+ private readonly static string[] QMMKnownAssemblyPaths = new[] {
#if !SUBNAUTICA_STABLE
Path.Combine(QMMPatchersPath, "QModManager.OculusNewtonsoftRedirect.dll"),
#endif
@@ -328,7 +210,6 @@ private static TypeloaderCache GetPluginCache()
[Obsolete("Should not be used!", true)]
public static void TypeLoaderFindPluginTypesPostfix(ref Dictionary> __result, string directory)
{
- Harmony.PatchAll(typeof(QModPluginGenerator));
if (directory != Paths.PluginPath)
return;
@@ -418,17 +299,18 @@ public static void TypeLoaderFindPluginTypesPostfix(ref Dictionary
+
+
+
\ No newline at end of file
diff --git a/Scripts/QModPluginGenerator-post-build.cmd b/Scripts/QModPluginGenerator-post-build.cmd
new file mode 100644
index 00000000..e9e22b18
--- /dev/null
+++ b/Scripts/QModPluginGenerator-post-build.cmd
@@ -0,0 +1,27 @@
+set solutionDir=%~f1
+set targetDir=%~f2
+set configName=%3
+
+rmdir "%solutionDir%VortexBuild\%configName%" /q /s
+xcopy "%solutionDir%Dependencies\BepInEx" "%solutionDir%VortexBuild\%configName%" /E /H /I /Q /Y
+xcopy "%solutionDir%Dependencies\%configName%\BepInEx.cfg" "%solutionDir%VortexBuild\%configName%\BepInEx\config\" /I /Q /Y
+mkdir "%solutionDir%VortexBuild\%configName%\QMods"
+xcopy "%solutionDir%Dependencies\cldb.dat" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+xcopy "%solutionDir%packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+xcopy "%targetDir%QModManager.exe" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+xcopy "%targetDir%QModManager.QModPluginGenerator.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+xcopy "%targetDir%QModManager.UnityAudioFixer.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+xcopy "%targetDir%QModManager.UnityAudioFixer.xml" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+
+if NOT "%configName%" =="SN.STABLE" (
+ xcopy "%solutionDir%Dependencies\Oculus.Newtonsoft.Json.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+ xcopy "%targetDir%QModManager.OculusNewtonsoftRedirect.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\patchers\QModManager\" /I /Q /Y
+)
+
+xcopy "%targetDir%QModInstaller.dll" "%solutionDir%VortexBuild\%configName%\BepInEx\plugins\QModManager\" /I /Q /Y
+xcopy "%targetDir%QModInstaller.xml" "%solutionDir%VortexBuild\%configName%\BepInEx\plugins\QModManager\" /I /Q /Y
+
+%solutionDir%packages\7-Zip.CommandLine.18.1.0\tools\7za.exe a "%solutionDir%VortexBuild\QModManager_%configName%.zip" "%solutionDir%VortexBuild\%configName%\*"
+
+echo F|xcopy /S /Q /Y /F "%solutionDir%Installer\%configName%.iss" "%targetDir%\QModsInstallerScript.iss"
+"%solutionDir%Dependencies\Inno\ISCC.exe" "%targetDir%QModsInstallerScript.iss"
\ No newline at end of file
diff --git a/UnityAudioFixer/Properties/AssemblyInfo.cs b/UnityAudioFixer/Properties/AssemblyInfo.cs
index 3572587f..8ac3042a 100644
--- a/UnityAudioFixer/Properties/AssemblyInfo.cs
+++ b/UnityAudioFixer/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("4.1.4")]
-[assembly: AssemblyFileVersion("4.1.4")]
+[assembly: AssemblyVersion("4.2")]
+[assembly: AssemblyFileVersion("4.2")]
diff --git a/UnityAudioFixer/QModManager.UnityAudioFixer.csproj b/UnityAudioFixer/QModManager.UnityAudioFixer.csproj
index bf5eb61e..004ccd32 100644
--- a/UnityAudioFixer/QModManager.UnityAudioFixer.csproj
+++ b/UnityAudioFixer/QModManager.UnityAudioFixer.csproj
@@ -55,9 +55,8 @@
prompt
-
- ..\packages\AssetsTools.NET.2.0.3\lib\net35\AssetsTools.NET.dll
- False
+
+ ..\packages\AssetsTools.NET.2.0.9\lib\net40\AssetsTools.NET.dll
..\Dependencies\BepInEx\BepInEx\core\BepInEx.dll
diff --git a/UnityAudioFixer/UnityAudioFixer.cs b/UnityAudioFixer/UnityAudioFixer.cs
index 6ec0e4c2..c9fa666d 100644
--- a/UnityAudioFixer/UnityAudioFixer.cs
+++ b/UnityAudioFixer/UnityAudioFixer.cs
@@ -90,7 +90,7 @@ private static void ChangeDisableUnityAudio(string path, bool newValue, QModGame
AssetsFileInstance afi = am.LoadAssetsFile(path, false);
am.LoadClassDatabase(Path.Combine(UnityAudioFixerPath, "cldb.dat"));
AssetFileInfoEx audioInfo = afi.table.GetAssetInfo(4);
- AssetTypeInstance audioAti = am.GetATI(afi.file, audioInfo);
+ AssetTypeInstance audioAti = am.GetTypeInstance(afi.file, audioInfo);
AssetTypeValueField audioBaseField = audioAti.GetBaseField();
audioBaseField.Get("m_DisableAudio").GetValue().Set(newValue);
byte[] audioAsset;
diff --git a/UnityAudioFixer/packages.config b/UnityAudioFixer/packages.config
index 76f7717a..e70d9e05 100644
--- a/UnityAudioFixer/packages.config
+++ b/UnityAudioFixer/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file