diff --git a/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll b/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll
index ca35c3a90..268f7989a 100644
Binary files a/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll and b/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll differ
diff --git a/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll b/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll
new file mode 100644
index 000000000..90f9891f9
Binary files /dev/null and b/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 405477901..0e120e667 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
### Version List:
+[v0.2.6 Open-Beta](#v026-open-beta)
[v0.2.5 Open-Beta](#v025-open-beta)
[v0.2.4 Open-Beta](#v024-open-beta)
[v0.2.3 Open-Beta](#v023-open-beta)
@@ -13,6 +14,57 @@
---
+### v0.2.6 Open-Beta:
+
+1. Fixed Issue with Logger Timestamp making New Lines.
+2. Added Silent Launch Option to Installer. (Credits to trevtv :P)
+3. Fixed Internal Failure caused by DisableAnalytics for certain users.
+4. Added Operating System log to startup info.
+5. Added ValueTupleBridge to Managed Folder.
+6. Added Unified Attributes for both Plugins and Mods.
+7. Added Legacy Attribute Support.
+8. Fixed Logger Issue with new Unified Attributes.
+9. Deprecated MelonModLogger, use MelonLogger instead.
+10. Deprecated ModPrefs, use MelonPrefs instead.
+11. Added HarmonyInstance Method for easier Unpatching of Mods and Plugins.
+12. Create MelonLoaderComponent after first scene load in Mono Games. (Credits to knah :D)
+13. Removed Launch Option --melonloader.devpluginsonly.
+14. Removed Launch Option --melonloader.devmodsonly.
+15. Added Launch Option --melonloader.loadmodeplugins.
+16. Added Launch Option --melonloader.loadmodemods.
+17. Fixed Issue with Debug Mode causing Crashes for Some Games.
+18. Fixed Issue with Manual Zip overriding Latest Version.
+19. Fixed Issue where Manual Zip would get set as Default Selection.
+20. Unity Analytics now redirected to localhost, rather than throwing null and exceptions. (Credits to Emilia :3)
+21. Plugins are now able to use OnUpdate.
+22. Plugins are now able to use OnLateUpdate.
+23. Plugins are now able to use OnGUI.
+24. Added GetUnityTlsInterface to Imports.
+25. Implemented Native Sided SSL/TLS/HTTPS Fix.
+26. Temporarily disabled DisableAnalytics.
+27. Fixed Issue with Support Modules not loading due to Missing Method errors.
+28. Fixed Issue with attaching dnSpy on Il2Cpp Games.
+29. Replaced mono-2.0-bdwgc.dll with dnSpy Debug dll.
+30. Debug Mode will now use LoadFrom instead of Load for breakpoint compatibility.
+31. Fixed Crash Issue with DisableAnalytics.
+32. Temporarily Disabled Console Logger Callbacks.
+33. Revised Console Logger Callbacks.
+34. Fixed Issue with LogMelonError not running Callbacks.
+35. Deprecated MelonLoader.Main use MelonLoaderBase or MelonHandler instead.
+36. Revised Base Melon Handling.
+37. Revised IniFile.
+38. Fixed Issue with Plugins not getting OnUpdate called.
+39. Fixed Issue with Plugins not getting OnLateUpdate called.
+40. Fixed Issue with Plugins not getting OnGUI called.
+41. Plugins are now able to use VRChat_OnUiManagerInit.
+42. Fixed Coroutine Queue for Mono Games.
+43. Added Launch Option --melonloader.consoleontop. (Credits to trevtv :P)
+44. Fixed Issue with Assembly Generator not stopping when failing to Download.
+45. Escalated Assembly Generator failures to Internal Failures.
+46. Fixed Issue where Assembly Generator failures would cause a Crash.
+
+---
+
### v0.2.5 Open-Beta:
1. Fixed Issue that prevented deserializing structs with TinyJSON. (Credits to zeobviouslyfakeacc :3)
diff --git a/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs b/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs
index 09d30b0b7..4bb6746c8 100644
--- a/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs
+++ b/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs
@@ -26,6 +26,7 @@ internal static class Main
private static string localConfigPath = null;
private static LocalConfig localConfig = null;
private static Il2CppConfig il2cppConfig = new Il2CppConfig();
+ private static bool DownloadedSuccessfully = true;
internal static bool Initialize(string unityVersion, string gameRoot, string gameDataDir)
{
@@ -86,14 +87,36 @@ private static bool AssemblyGenerateCheck(string unityVersion)
private static void DownloadDependencies(string unityVersion)
{
Logger.Log("Downloading Il2CppDumper");
- DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppDumperUrl, ExternalToolVersions.Il2CppDumperVersion, localConfig.DumperVersion, Il2CppDumper.BaseFolder, TempFileCache.CreateFile());
- localConfig.DumperVersion = ExternalToolVersions.Il2CppDumperVersion;
- localConfig.Save(localConfigPath);
+ try
+ {
+ DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppDumperUrl, ExternalToolVersions.Il2CppDumperVersion, localConfig.DumperVersion, Il2CppDumper.BaseFolder, TempFileCache.CreateFile());
+ localConfig.DumperVersion = ExternalToolVersions.Il2CppDumperVersion;
+ localConfig.Save(localConfigPath);
+ }
+ catch (Exception ex)
+ {
+ DownloadedSuccessfully = false;
+ Logger.LogError(ex.ToString());
+ Logger.Log("Can't download Il2CppDumper!");
+ }
+ if (!DownloadedSuccessfully)
+ return;
Logger.Log("Downloading Il2CppAssemblyUnhollower");
- DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppAssemblyUnhollowerUrl, ExternalToolVersions.Il2CppAssemblyUnhollowerVersion, localConfig.UnhollowerVersion, Il2CppAssemblyUnhollower.BaseFolder, TempFileCache.CreateFile());
- localConfig.UnhollowerVersion = ExternalToolVersions.Il2CppAssemblyUnhollowerVersion;
- localConfig.Save(localConfigPath);
+ try
+ {
+ DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppAssemblyUnhollowerUrl, ExternalToolVersions.Il2CppAssemblyUnhollowerVersion, localConfig.UnhollowerVersion, Il2CppAssemblyUnhollower.BaseFolder, TempFileCache.CreateFile());
+ localConfig.UnhollowerVersion = ExternalToolVersions.Il2CppAssemblyUnhollowerVersion;
+ localConfig.Save(localConfigPath);
+ }
+ catch (Exception ex)
+ {
+ DownloadedSuccessfully = false;
+ Logger.LogError(ex.ToString());
+ Logger.Log("Can't download Il2CppAssemblyUnhollower!");
+ }
+ if (!DownloadedSuccessfully)
+ return;
Logger.Log("Downloading Unity Dependencies");
string tempfile = TempFileCache.CreateFile();
@@ -142,8 +165,8 @@ private static void DownloadDependencies(string unityVersion)
}
catch (Exception ex)
{
- Logger.LogError("Can't download Unity Dependencies, Unstripping will NOT be done!");
Logger.LogError(ex.ToString());
+ Logger.LogError("Can't download Unity Dependencies, Unstripping will NOT be done!");
}
}
}
@@ -151,7 +174,9 @@ private static void DownloadDependencies(string unityVersion)
private static bool AssemblyGenerate(string gameRoot, string unityVersion, string gameDataDir)
{
DownloadDependencies(unityVersion);
-
+ if (!DownloadedSuccessfully)
+ return false;
+
FixIl2CppDumperConfig();
Logger.Log("Executing Il2CppDumper...");
diff --git a/MelonLoader.AssemblyGenerator/AssemblyInfo.cs b/MelonLoader.AssemblyGenerator/AssemblyInfo.cs
index 55e114ca8..dab900161 100644
--- a/MelonLoader.AssemblyGenerator/AssemblyInfo.cs
+++ b/MelonLoader.AssemblyGenerator/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".GeneratorProcess")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("b323399d-2c8d-46ee-9ce5-2f970f7f2db9")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.AssemblyGenerator/Program.cs b/MelonLoader.AssemblyGenerator/Program.cs
index bce321962..a11bd0b29 100644
--- a/MelonLoader.AssemblyGenerator/Program.cs
+++ b/MelonLoader.AssemblyGenerator/Program.cs
@@ -12,7 +12,7 @@ public static class Program
public static int Main(string[] args)
{
ServicePointManager.Expect100Continue = true;
- ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | (SecurityProtocolType)3072;
webClient.Headers.Add("User-Agent", "Unity web player");
if (args.Length < 4)
diff --git a/MelonLoader.Installer/MainForm.cs b/MelonLoader.Installer/MainForm.cs
index 8766ee5bc..6f0dfd709 100644
--- a/MelonLoader.Installer/MainForm.cs
+++ b/MelonLoader.Installer/MainForm.cs
@@ -117,7 +117,7 @@ private void btnInstall_Click(object sender, EventArgs e)
string selectedVersion = ((((string)cbVersions.Items[cbVersions.SelectedIndex]).StartsWith("Latest")) ? (string)cbVersions.Items[cbVersions.SelectedIndex + 1] : (string)cbVersions.Items[cbVersions.SelectedIndex]);
bool legacy_install = (selectedVersion.Equals("v0.2.1") || selectedVersion.Equals("v0.2") || selectedVersion.Equals("v0.1.0"));
- Program.Install(dirpath, selectedVersion, legacy_install);
+ Program.Install(dirpath, selectedVersion, legacy_install, false);
Program.SetDisplayText("SUCCESS!");
MessageBox.Show("Installation Successful!", Program.Title, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
diff --git a/MelonLoader.Installer/MelonLoader.Installer.csproj b/MelonLoader.Installer/MelonLoader.Installer.csproj
index e2e0034d3..2101fdaa8 100644
--- a/MelonLoader.Installer/MelonLoader.Installer.csproj
+++ b/MelonLoader.Installer/MelonLoader.Installer.csproj
@@ -5,7 +5,7 @@
net472
true
latest
- 2.0.0
+ 2.0.1
Lava Gang
discord.gg/2Wn3N2P
Lava Gang
@@ -15,7 +15,7 @@
icon.ico
app.manifest
MelonLoader.Installer.Program
- 2.0.0.0
+ 2.0.1.0
Installer for MelonLoader
MelonLoader.Installer
MelonLoader.Installer
diff --git a/MelonLoader.Installer/Program.cs b/MelonLoader.Installer/Program.cs
index 9619d7906..30df6fc2b 100644
--- a/MelonLoader.Installer/Program.cs
+++ b/MelonLoader.Installer/Program.cs
@@ -5,10 +5,9 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Reflection;
using System.Text;
-using System.Threading;
using System.Windows.Forms;
-using System.Windows.Forms.VisualStyles;
using MelonLoader.LightJson;
#pragma warning disable 0168
@@ -16,6 +15,7 @@ namespace MelonLoader.Installer
{
static class Program
{
+ internal static bool ShouldCheckForUpdates = true;
internal static string Title = "MelonLoader Installer";
private static string ExeName = null;
internal static string ManualZipPath = null;
@@ -25,18 +25,32 @@ static class Program
private static readonly string[] filesToCleanUp = new string[] { "Mono.Cecil.dll", "version.dll", "winmm.dll" };
private static readonly string[] foldersToCleanUp = new string[] { "Logs", "MelonLoader" };
+ public static bool silent;
+ public static string silentPath;
+
[STAThread]
static void Main()
{
ServicePointManager.Expect100Continue = true;
- ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | (SecurityProtocolType)3072;
webClient.Headers.Add("User-Agent", "Unity web player");
webClient.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs info) => SetPercentage(info.ProgressPercentage);
Application.SetCompatibleTextRenderingDefault(false);
Application.EnableVisualStyles();
+ var args = Environment.GetCommandLineArgs();
+ foreach (string arg in args)
+ {
+ bool isSelf = Path.GetFullPath(arg) == Assembly.GetExecutingAssembly().Location;
+ if (File.Exists(arg) && arg.EndsWith(".exe") && !isSelf)
+ {
+ silent = true;
+ silentPath = Path.GetDirectoryName(arg);
+ }
+ }
+
ExeName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
- if (ExeName.EndsWith(".tmp.exe"))
+ if (ShouldCheckForUpdates && ExeName.EndsWith(".tmp.exe"))
{
string original_exe_path = (Directory.GetCurrentDirectory() + "\\" + ExeName.Substring(0, (ExeName.Length - 8)));
File.Delete(original_exe_path);
@@ -46,30 +60,75 @@ static void Main()
}
else
{
- string tempfilepath = (Directory.GetCurrentDirectory() + "\\" + ExeName + ".tmp.exe");
- if (File.Exists(tempfilepath))
- File.Delete(tempfilepath);
+ if (ShouldCheckForUpdates)
+ {
+ string tempfilepath = (Directory.GetCurrentDirectory() + "\\" + ExeName + ".tmp.exe");
+ if (File.Exists(tempfilepath))
+ File.Delete(tempfilepath);
+ }
ManualZipPath = (Directory.GetCurrentDirectory() + "\\MelonLoader.zip");
- Install_GUI();
+
+ if (silent)
+ Install_NoGUI();
+ else
+ Install_GUI();
+ }
+ }
+
+ static void Install_NoGUI()
+ {
+ try
+ {
+ string version = null;
+ JsonArray data = (JsonArray)JsonValue.Parse(webClient.DownloadString("https://api.github.com/repos/HerpDerpinstine/MelonLoader/releases")).AsJsonArray;
+ if (data.Count > 0)
+ version = data[0]["tag_name"].AsString;
+ try
+ {
+ Install(silentPath, version, false, true);
+ Environment.Exit(0);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Installation failed; upload the created log to #melonloader-support on discord", Title);
+ File.WriteAllText(Directory.GetCurrentDirectory() + $@"\MLInstaller_{DateTime.Now:yy-M-dd_HH-mm-ss.fff}.log", ex.ToString());
+ Environment.Exit(-1);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Failed to Get Version List; upload the created log to #melonloader-support on discord", Title);
+ File.WriteAllText(Directory.GetCurrentDirectory() + $@"\MLInstaller_{DateTime.Now:yy-M-dd_HH-mm-ss.fff}.log", ex.ToString());
+ Environment.Exit(-1);
}
}
static void Install_GUI()
{
+ int selected_index = 0;
+
try
{
mainForm = new MainForm();
mainForm.cbVersions.Items.Clear();
- if (File.Exists(ManualZipPath))
+
+ bool should_add_manual = File.Exists(ManualZipPath);
+ if (should_add_manual)
mainForm.cbVersions.Items.Add("Manual Zip");
JsonArray data = (JsonArray)JsonValue.Parse(webClient.DownloadString("https://api.github.com/repos/HerpDerpinstine/MelonLoader/releases")).AsJsonArray;
if (data.Count > 0)
{
+ bool has_added_latest = false;
foreach (var x in data)
{
string version = x["tag_name"].AsString;
- if (mainForm.cbVersions.Items.Count <= 0)
+ if (!has_added_latest)
+ {
+ has_added_latest = true;
mainForm.cbVersions.Items.Add("Latest (" + version + ")");
+ selected_index = (should_add_manual ? 1 : 0);
+ }
mainForm.cbVersions.Items.Add(version);
}
}
@@ -83,18 +142,22 @@ static void Install_GUI()
Application.Exit();
}
- mainForm.cbVersions.SelectedIndex = 0;
- mainForm.cbVersions.SelectedItem = mainForm.cbVersions.Items[0];
+ if ((selected_index < 0) || (selected_index > (mainForm.cbVersions.Items.Count - 1)))
+ selected_index = 0;
+ mainForm.cbVersions.SelectedIndex = selected_index;
+ mainForm.cbVersions.SelectedItem = mainForm.cbVersions.Items[selected_index];
mainForm.Show();
Application.Run(mainForm);
}
- internal static void Install(string dirpath, string selectedVersion, bool legacy_install)
+ internal static void Install(string dirpath, string selectedVersion, bool legacy_install, bool silent_install)
{
if (File.Exists(ManualZipPath) && selectedVersion.Equals("Manual Zip"))
Install_ManualZip(dirpath);
else if (legacy_install)
Install_Legacy(dirpath, selectedVersion);
+ else if (silent_install)
+ Install_Silent(dirpath, selectedVersion);
else
Install_Normal(dirpath, selectedVersion);
TempFileCache.ClearCache();
@@ -116,6 +179,16 @@ private static void Install_Legacy(string dirpath, string selectedVersion)
Install_Legacy_02(dirpath, selectedVersion);
}
+ private static void Install_Silent(string dirpath, string selectedVersion)
+ {
+ string tempfilepath = TempFileCache.CreateFile();
+ webClient.DownloadFileAsync(new Uri("https://github.com/HerpDerpinstine/MelonLoader/releases/download/" + selectedVersion + "/MelonLoader.zip"), tempfilepath);
+ while (webClient.IsBusy) { }
+ Cleanup(dirpath);
+ ExtractZip(dirpath, tempfilepath);
+ CreateDirectories(dirpath, selectedVersion, false);
+ }
+
internal static void SetDisplayText(string text)
{
mainForm.Invoke(new Action(() => {
@@ -125,6 +198,9 @@ internal static void SetDisplayText(string text)
internal static void SetPercentage(int percent)
{
+ if (silent)
+ return;
+
mainForm.Invoke(new Action(() => {
mainForm.progressBar.Value = percent;
mainForm.lblProgressPer.Text = percent.ToString() + "%";
@@ -349,6 +425,8 @@ internal static string GetUnityVersion(string exepath)
internal static void CheckForUpdates()
{
+ if (!ShouldCheckForUpdates)
+ return;
try
{
string response = webClient.DownloadString("https://github.com/HerpDerpinstine/MelonLoader/raw/master/MelonLoader.Installer/MelonLoader.Installer.csproj");
diff --git a/MelonLoader.Installer/version.txt b/MelonLoader.Installer/version.txt
deleted file mode 100644
index d4858f8bf..000000000
--- a/MelonLoader.Installer/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-UNUSED
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/AssemblyGenerator.cs b/MelonLoader.ModHandler/AssemblyGenerator.cs
index 12605ebef..948934328 100644
--- a/MelonLoader.ModHandler/AssemblyGenerator.cs
+++ b/MelonLoader.ModHandler/AssemblyGenerator.cs
@@ -1,42 +1,64 @@
using System.Diagnostics;
using System.IO;
using System.Threading;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
-namespace MelonLoader.AssemblyGenerator
+namespace MelonLoader
{
- internal static class Main
+ internal static class AssemblyGenerator
{
- internal static bool Initialize()
+ internal static bool HasGeneratedAssembly = false;
+
+ internal static void Check()
+ {
+ if (!Imports.IsIl2CppGame() || Initialize())
+ HasGeneratedAssembly = true;
+ else
+ MelonLoaderBase.UNLOAD(false);
+ }
+
+ private static bool Initialize()
{
string GeneratorProcessPath = Path.Combine(Path.Combine(Path.Combine(Path.Combine(Imports.GetGameDirectory(), "MelonLoader"), "Dependencies"), "AssemblyGenerator"), "MelonLoader.AssemblyGenerator.exe");
if (File.Exists(GeneratorProcessPath))
{
var generatorProcessInfo = new ProcessStartInfo(GeneratorProcessPath);
- generatorProcessInfo.Arguments = $"\"{MelonLoader.Main.UnityVersion}\" \"{Imports.GetGameDirectory()}\" \"{Imports.GetGameDataDirectory()}\" {(Imports.AG_Force_Regenerate() ? "true" : "false")} {(string.IsNullOrEmpty(Imports.AG_Force_Version_Unhollower()) ? "" : Imports.AG_Force_Version_Unhollower())}";
+ generatorProcessInfo.Arguments = $"\"{MelonLoaderBase.UnityVersion}\" \"{Imports.GetGameDirectory()}\" \"{Imports.GetGameDataDirectory()}\" {(Force_Regenerate() ? "true" : "false")} {(string.IsNullOrEmpty(Force_Version_Unhollower()) ? "" : Force_Version_Unhollower())}";
generatorProcessInfo.UseShellExecute = false;
generatorProcessInfo.RedirectStandardOutput = true;
generatorProcessInfo.CreateNoWindow = true;
var process = Process.Start(generatorProcessInfo);
if (process == null)
- MelonModLogger.LogError("Unable to Start Assembly Generator!");
+ MelonLogger.LogError("Unable to Start Assembly Generator!");
else
{
var stdout = process.StandardOutput;
while (!stdout.EndOfStream)
{
var line = stdout.ReadLine();
- MelonModLogger.Log(line);
+ MelonLogger.Log(line);
}
while (!process.HasExited)
Thread.Sleep(100);
- if (Imports.IsDebugMode())
- MelonModLogger.Log($"Assembly Generator exited with code {process.ExitCode}");
- return (process.ExitCode == 0);
+ if (process.ExitCode == 0)
+ {
+ if (Imports.IsDebugMode())
+ MelonLogger.Log($"Assembly Generator ran Successfully!");
+ return true;
+ }
+ MelonLogger.Native_ThrowInternalError($"Assembly Generator exited with code {process.ExitCode}");
}
}
else
- MelonModLogger.LogError("MelonLoader.AssemblyGenerator.exe does not Exist!");
+ MelonLogger.LogError("MelonLoader.AssemblyGenerator.exe does not Exist!");
return false;
}
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool Force_Regenerate();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [return: MarshalAs(UnmanagedType.LPStr)]
+ private extern static string Force_Version_Unhollower();
}
}
diff --git a/MelonLoader.ModHandler/AssemblyInfo.cs b/MelonLoader.ModHandler/AssemblyInfo.cs
index 6bd24b3b0..517feedcb 100644
--- a/MelonLoader.ModHandler/AssemblyInfo.cs
+++ b/MelonLoader.ModHandler/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name)]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("26082f09-2902-45d6-a793-2ed9ff556bdf")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/BuildInfo.cs b/MelonLoader.ModHandler/BuildInfo.cs
index 871149216..94daa9b5d 100644
--- a/MelonLoader.ModHandler/BuildInfo.cs
+++ b/MelonLoader.ModHandler/BuildInfo.cs
@@ -6,6 +6,6 @@ public static class BuildInfo
public const string Description = "MelonLoader";
public const string Author = "Lava Gang";
public const string Company = "discord.gg/2Wn3N2P";
- public const string Version = "0.2.5";
+ public const string Version = "0.2.6";
}
}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/Console.cs b/MelonLoader.ModHandler/Console.cs
deleted file mode 100644
index 465609f52..000000000
--- a/MelonLoader.ModHandler/Console.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.CompilerServices;
-
-namespace MelonLoader
-{
- public class Console
- {
- public static bool Enabled = false;
-
- internal static void Create()
- {
- Allocate();
- System.Console.SetOut(new StreamWriter(System.Console.OpenStandardOutput()) { AutoFlush = true });
- System.Console.SetIn(new StreamReader(System.Console.OpenStandardInput()));
- SetTitle(BuildInfo.Name + " v" + BuildInfo.Version + " Open-Beta");
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void Allocate();
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void SetTitle(string title);
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void SetColor(ConsoleColor color);
- //[MethodImpl(MethodImplOptions.InternalCall)]
- //public static extern bool ShouldShowGameLogs();
-
- private static void RunLogCallbacks(string msg) => LogCallbackHandler?.Invoke(msg);
- public static event Action LogCallbackHandler;
- private static void RunWarningCallbacks(string msg) => WarningCallbackHandler1?.Invoke(msg);
- private static void RunWarningCallbacks(string namesection, string msg) => WarningCallbackHandler2?.Invoke(namesection, msg);
- public static event Action WarningCallbackHandler1;
- public static event Action WarningCallbackHandler2;
- private static void RunErrorCallbacks(string msg) => ErrorCallbackHandler1?.Invoke(msg);
- private static void RunErrorCallbacks(string namesection, string msg) => ErrorCallbackHandler2?.Invoke(namesection, msg);
- public static event Action ErrorCallbackHandler1;
- public static event Action ErrorCallbackHandler2;
- }
-}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/DependencyGraph.cs b/MelonLoader.ModHandler/DependencyGraph.cs
index d020f0d22..42de7279b 100644
--- a/MelonLoader.ModHandler/DependencyGraph.cs
+++ b/MelonLoader.ModHandler/DependencyGraph.cs
@@ -62,7 +62,7 @@ private DependencyGraph(IList mods, Func modNameGetter) {
if (modsWithMissingDeps.Count > 0) {
// Some mods are missing dependencies. Don't load these mods and show an error message
- MelonModLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps));
+ MelonLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps));
}
}
@@ -74,7 +74,7 @@ private static bool TryLoad(AssemblyName assembly) {
} catch (FileNotFoundException) {
return false;
} catch (Exception ex) {
- MelonModLogger.LogError("Loading mod dependency failed: " + ex);
+ MelonLogger.LogError("Loading mod dependency failed: " + ex);
return false;
}
}
@@ -138,7 +138,7 @@ private void TopologicalSortInto(IList loadedMods) {
errorMessage.Append($"- '{vertices[i].name}'\n");
}
errorMessage.Length -= 1; // Remove trailing newline
- MelonModLogger.LogError(errorMessage.ToString());
+ MelonLogger.LogError(errorMessage.ToString());
}
}
diff --git a/MelonLoader.ModHandler/Deprecated.cs b/MelonLoader.ModHandler/Deprecated.cs
new file mode 100644
index 000000000..bf3e52228
--- /dev/null
+++ b/MelonLoader.ModHandler/Deprecated.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+#pragma warning disable 0108
+
+namespace MelonLoader
+{
+ [Obsolete("Main is obsolete. Please use MelonLoaderBase or MelonHandler instead.")]
+ public static class Main
+ {
+ public static List Mods = null;
+ public static List Plugins = null;
+ public static bool IsVRChat = false;
+ public static bool IsBoneworks = false;
+ public static string GetUnityVersion() => MelonLoaderBase.UnityVersion;
+ public static string GetUserDataPath() => MelonLoaderBase.UserDataPath;
+ internal static void LegacySupport(List mods, List plugins, bool isVRChat, bool isBoneworks)
+ {
+ Mods = mods;
+ Plugins = plugins;
+ IsVRChat = isVRChat;
+ IsBoneworks = isBoneworks;
+ }
+ }
+ [Obsolete("MelonModGame is obsolete. Please use MelonGame instead.")]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class MelonModGameAttribute : Attribute
+ {
+ public string Developer { get; }
+ public string GameName { get; }
+ public MelonModGameAttribute(string developer = null, string gameName = null)
+ {
+ Developer = developer;
+ GameName = gameName;
+ }
+ internal MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName);
+ }
+ [Obsolete("MelonModInfo is obsolete. Please use MelonInfo instead.")]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public class MelonModInfoAttribute : Attribute
+ {
+ public Type SystemType { get; }
+ public string Name { get; }
+ public string Version { get; }
+ public string Author { get; }
+ public string DownloadLink { get; }
+
+ public MelonModInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
+ {
+ SystemType = type;
+ Name = name;
+ Version = version;
+ Author = author;
+ DownloadLink = downloadLink;
+ }
+ internal MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink);
+ }
+ [Obsolete("MelonPluginGame is obsolete. Please use MelonGame instead.")]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class MelonPluginGameAttribute : Attribute
+ {
+ public string Developer { get; }
+ public string GameName { get; }
+ public MelonPluginGameAttribute(string developer = null, string gameName = null)
+ {
+ Developer = developer;
+ GameName = gameName;
+ }
+ public MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName);
+ }
+ [Obsolete("MelonPluginInfo is obsolete. Please use MelonInfo instead.")]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public class MelonPluginInfoAttribute : Attribute
+ {
+ public Type SystemType { get; }
+ public string Name { get; }
+ public string Version { get; }
+ public string Author { get; }
+ public string DownloadLink { get; }
+
+ public MelonPluginInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
+ {
+ SystemType = type;
+ Name = name;
+ Version = version;
+ Author = author;
+ DownloadLink = downloadLink;
+ }
+ public MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink);
+ }
+ [Obsolete("MelonModLogger is obsolete. Please use MelonLogger instead.")]
+ public class MelonModLogger : MelonLogger {}
+ [Obsolete("ModPrefs is obsolete. Please use MelonPrefs instead.")]
+ public class ModPrefs : MelonPrefs
+ {
+ public static Dictionary> GetPrefs()
+ {
+ Dictionary> output = new Dictionary>();
+ Dictionary> prefs = GetPreferences();
+ for (int i = 0; i < prefs.Values.Count; i++)
+ {
+ Dictionary prefsdict = prefs.Values.ElementAt(i);
+ Dictionary newprefsdict = new Dictionary();
+ for (int j = 0; j < prefsdict.Values.Count; j++)
+ {
+ MelonPreference pref = prefsdict.Values.ElementAt(j);
+ PrefDesc newpref = new PrefDesc(pref.Value, (PrefType)pref.Type, pref.Hidden, pref.DisplayText);
+ newpref.ValueEdited = pref.ValueEdited;
+ newprefsdict.Add(prefsdict.Keys.ElementAt(j), newpref);
+ }
+ output.Add(prefs.Keys.ElementAt(i), newprefsdict);
+ }
+ return output;
+ }
+ public static void RegisterPrefString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) => RegisterString(section, name, defaultValue, displayText, hideFromList);
+ public static void RegisterPrefBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) => RegisterBool(section, name, defaultValue, displayText, hideFromList);
+ public static void RegisterPrefInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) => RegisterInt(section, name, defaultValue, displayText, hideFromList);
+ public static void RegisterPrefFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) => RegisterFloat(section, name, defaultValue, displayText, hideFromList);
+ public enum PrefType
+ {
+ STRING,
+ BOOL,
+ INT,
+ FLOAT
+ }
+ public class PrefDesc : MelonPreference
+ {
+ public PrefType Type { get => (PrefType)base.Type; }
+ public PrefDesc(string value, PrefType type, bool hidden, string displayText) : base(value, type, hidden, displayText)
+ {
+ Value = value;
+ ValueEdited = value;
+ base.Type = (MelonPreferenceType)type;
+ Hidden = hidden;
+ DisplayText = displayText;
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs b/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs
index 12f6f30d3..4b3bfe897 100644
--- a/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs
+++ b/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs
@@ -1,4 +1,5 @@
using Harmony.Tools;
+using MelonLoader;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -93,8 +94,6 @@ private MethodBase GetOutsideCaller()
throw new Exception("Unexpected end of stack trace");
}
- //
-
public void PatchAll()
{
var method = new StackTrace().GetFrame(1).GetMethod();
@@ -136,7 +135,30 @@ public void UnpatchAll(string harmonyID = null)
}
}
- public static void UnpatchAllInstances()
+ public void UnpatchAll(MelonBase melon)
+ {
+ bool IDCheck(Patch patchInfo) => (patchInfo.patch.Module.Assembly == melon.Assembly);
+ var originals = GetPatchedMethods().ToList();
+ foreach (var original in originals)
+ {
+ var info = GetPatchInfo(original);
+ info.Postfixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
+ info.Prefixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
+ info.Transpilers.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
+ }
+ }
+
+ public static void UnpatchAllMelonInstances(MelonBase melon)
+ {
+ if (instancelist.Count > 0)
+ {
+ for (int i = 0; i < instancelist.Count; i++)
+ instancelist[i].UnpatchAll(melon);
+ instancelist.Clear();
+ }
+ }
+
+ public static void UnpatchAllInstances()
{
if (instancelist.Count > 0)
{
diff --git a/MelonLoader.ModHandler/Harmony/PatchFunctions.cs b/MelonLoader.ModHandler/Harmony/PatchFunctions.cs
index c3a6cc6bd..fae3bb801 100644
--- a/MelonLoader.ModHandler/Harmony/PatchFunctions.cs
+++ b/MelonLoader.ModHandler/Harmony/PatchFunctions.cs
@@ -157,7 +157,7 @@ private static IEnumerable UnhollowerTranspiler(MethodBase meth
}
if (!found) {
- MelonModLogger.LogError("Harmony transpiler could not rewrite Unhollower method. Expect a stack overflow.");
+ MelonLogger.LogError("Harmony transpiler could not rewrite Unhollower method. Expect a stack overflow.");
return instructions;
}
@@ -259,13 +259,13 @@ private static DynamicMethod CreateIl2CppShim(DynamicMethod patch, MethodBase or
// Catch any exceptions that may have been thrown
Emitter.MarkBlockBefore(il, new ExceptionBlock(ExceptionBlockType.BeginCatchBlock, typeof(Exception)), out _);
- // MelonModLogger.LogError("Exception in ...\n" + exception.ToString());
+ // MelonLogger.LogError("Exception in ...\n" + exception.ToString());
Emitter.Emit(il, OpCodes.Stloc, exceptionLocal);
Emitter.Emit(il, OpCodes.Ldstr, $"Exception in Harmony patch of method {original.FullDescription()}:\n");
Emitter.Emit(il, OpCodes.Ldloc, exceptionLocal);
Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(Exception), "ToString", new Type[0]));
Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(string), "Concat", new Type[] { typeof(string), typeof(string) }));
- Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(MelonModLogger), "LogError", new Type[] { typeof(string) }));
+ Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(MelonLogger), "LogError", new Type[] { typeof(string) }));
// Close the exception block
Emitter.MarkBlockAfter(il, new ExceptionBlock(ExceptionBlockType.EndExceptionBlock, null));
diff --git a/MelonLoader.ModHandler/Imports.cs b/MelonLoader.ModHandler/Imports.cs
index d271da7ad..1faf1a2a3 100644
--- a/MelonLoader.ModHandler/Imports.cs
+++ b/MelonLoader.ModHandler/Imports.cs
@@ -6,8 +6,6 @@ namespace MelonLoader
{
public static class Imports
{
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void UNLOAD_MELONLOADER();
[MethodImpl(MethodImplOptions.InternalCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static string GetCompanyName();
@@ -34,23 +32,8 @@ public static class Imports
[MethodImpl(MethodImplOptions.InternalCall)]
public extern static bool IsDebugMode();
[MethodImpl(MethodImplOptions.InternalCall)]
- public extern static bool IsConsoleEnabled();
- [MethodImpl(MethodImplOptions.InternalCall)]
public extern static void Hook(IntPtr target, IntPtr detour);
[MethodImpl(MethodImplOptions.InternalCall)]
public extern static void Unhook(IntPtr target, IntPtr detour);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool IsOldMono();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool IsQuitFix();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool IsDevModsOnly();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool IsDevPluginsOnly();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool AG_Force_Regenerate();
- [MethodImpl(MethodImplOptions.InternalCall)]
- [return: MarshalAs(UnmanagedType.LPStr)]
- public extern static string AG_Force_Version_Unhollower();
}
}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/IniFile.cs b/MelonLoader.ModHandler/IniFile.cs
index ff002b049..2152b5c6a 100644
--- a/MelonLoader.ModHandler/IniFile.cs
+++ b/MelonLoader.ModHandler/IniFile.cs
@@ -11,16 +11,62 @@ public class IniFile
[DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int WritePrivateProfileString(string lpSection, string lpKey, string lpValue, string lpFileName);
private string _path = "";
- public string Path { get { return _path; } set { if (!File.Exists(value)) File.WriteAllText(value, "", Encoding.Unicode); _path = value; } }
- public IniFile(string INIPath) { this.Path = INIPath; }
- public void IniWriteValue(string Section, string Key, string Value) { WritePrivateProfileString(Section, Key, Value, this.Path); }
- public string IniReadValue(string Section, string Key)
+ public string Path { get { return _path; } internal set { if (!File.Exists(value)) File.WriteAllText(value, "", Encoding.Unicode); _path = value; } }
+ public IniFile(string INIPath) { Path = INIPath; }
+ private void IniWriteValue(string Section, string Key, string Value) { WritePrivateProfileString(Section, Key, Value, Path); }
+ private string IniReadValue(string Section, string Key)
{
const int MAX_CHARS = 1023;
StringBuilder result = new StringBuilder(MAX_CHARS);
- GetPrivateProfileString(Section, Key, " _", result, MAX_CHARS, this.Path);
+ GetPrivateProfileString(Section, Key, " _", result, MAX_CHARS, Path);
if (result.ToString().Equals(" _")) return null;
return result.ToString();
}
+
+ public bool HasKey(string section, string name) { return IniReadValue(section, name) != null; }
+
+ public string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
+ {
+ string value = IniReadValue(section, name);
+ if (!string.IsNullOrEmpty(value))
+ return value;
+ else if (autoSave)
+ SetString(section, name, defaultValue);
+ return defaultValue;
+ }
+ public void SetString(string section, string name, string value) { IniWriteValue(section, name, value.Trim()); }
+
+ public int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
+ {
+ int value;
+ if (int.TryParse(IniReadValue(section, name), out value))
+ return value;
+ else if (autoSave)
+ SetInt(section, name, defaultValue);
+ return defaultValue;
+ }
+ public void SetInt(string section, string name, int value) { IniWriteValue(section, name, value.ToString()); }
+
+ public float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
+ {
+ float value;
+ if (float.TryParse(IniReadValue(section, name), out value))
+ return value;
+ else if (autoSave)
+ SetFloat(section, name, defaultValue);
+ return defaultValue;
+ }
+ public void SetFloat(string section, string name, float value) { IniWriteValue(section, name, value.ToString()); }
+
+ public bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
+ {
+ string sVal = GetString(section, name, null);
+ if ("true".Equals(sVal) || "1".Equals(sVal) || "0".Equals(sVal) || "false".Equals(sVal))
+ return ("true".Equals(sVal) || "1".Equals(sVal));
+ else if (autoSave)
+ SetBool(section, name, defaultValue);
+ return defaultValue;
+ }
+ public void SetBool(string section, string name, bool value) { IniWriteValue(section, name, value ? "true" : "false"); }
}
}
diff --git a/MelonLoader.ModHandler/Main.cs b/MelonLoader.ModHandler/Main.cs
deleted file mode 100644
index ee475c7f7..000000000
--- a/MelonLoader.ModHandler/Main.cs
+++ /dev/null
@@ -1,649 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using Harmony;
-using MelonLoader.ICSharpCode.SharpZipLib.Zip;
-#pragma warning disable 0618
-
-namespace MelonLoader
-{
- public static class Main
- {
- public static List Mods = new List();
- public static List Plugins = new List();
- public static List TempPlugins = null;
- internal static MelonModGameAttribute CurrentGameAttribute = null;
- public static bool IsVRChat = false;
- public static bool IsBoneworks = false;
- public static string UnityVersion = null;
- private static Assembly Assembly_CSharp = null;
- private static bool HasGeneratedAssembly = false;
-
- private static void Initialize()
- {
- if (string.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory))
- ((AppDomainSetup)typeof(AppDomain).GetProperty("SetupInformationNoCopy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(AppDomain.CurrentDomain, new object[0])).ApplicationBase = Imports.GetGameDirectory();
- Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
- AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;
-
- CurrentGameAttribute = new MelonModGameAttribute(Imports.GetCompanyName(), Imports.GetProductName());
- UnityVersion = GetUnityVersion();
-
- if (Imports.IsIl2CppGame())
- {
- IsVRChat = CurrentGameAttribute.IsGame("VRChat", "VRChat");
- IsBoneworks = CurrentGameAttribute.IsGame("Stress Level Zero", "BONEWORKS");
- }
-
- if (!Imports.IsDebugMode()
-#if !DEBUG
- && Imports.IsConsoleEnabled()
-#endif
- )
- {
- Console.Enabled = true;
- Console.Create();
- }
-
- if (!Imports.IsIl2CppGame() || AssemblyGenerator.Main.Initialize())
- HasGeneratedAssembly = true;
- else
- Imports.UNLOAD_MELONLOADER();
-
- if (HasGeneratedAssembly)
- {
- LoadDLLs(true);
- if (Plugins.Count > 0)
- {
- HashSet failedPlugins = new HashSet();
- TempPlugins = Plugins.Where(plugin => (plugin.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)).ToList();
- DependencyGraph.TopologicalSort(TempPlugins, plugin => plugin.InfoAttribute.Name);
- for (int i = 0; i < TempPlugins.Count; i++)
- {
- MelonPlugin plugin = TempPlugins[i];
- if (plugin != null)
- try { plugin.OnPreInitialization(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); failedPlugins.Add(plugin); }
- }
- TempPlugins.RemoveAll(plugin => failedPlugins.Contains(plugin));
- }
- }
- }
-
- private static void OnApplicationStart()
- {
- if (!HasGeneratedAssembly)
- return;
-
- if (Imports.IsIl2CppGame())
- {
- if (IsVRChat)
- Assembly_CSharp = Assembly.Load("Assembly-CSharp");
- UnhollowerSupport.Initialize();
- }
- SupportModule.Initialize();
-
- MelonModLogger.Log("------------------------------");
- MelonModLogger.Log("Unity " + UnityVersion);
- MelonModLogger.Log("------------------------------");
- MelonModLogger.Log("Name: " + CurrentGameAttribute.GameName);
- MelonModLogger.Log("Developer: " + CurrentGameAttribute.Developer);
- MelonModLogger.Log("Type: " + (Imports.IsIl2CppGame() ? "Il2Cpp" : (Imports.IsOldMono() ? "Mono" : "MonoBleedingEdge")));
- MelonModLogger.Log("------------------------------");
- MelonModLogger.Log("Using v" + BuildInfo.Version + " Open-Beta");
- MelonModLogger.Log("------------------------------");
-
- LoadDLLs();
- if (Plugins.Count > 0)
- {
- for (int i = 0; i < Plugins.Count; i++)
- {
- MelonPlugin plugin = Plugins[i];
- if (plugin != null)
- {
- MelonModLogger.Log(plugin.InfoAttribute.Name
- + (!string.IsNullOrEmpty(plugin.InfoAttribute.Version)
- ? (" v" + plugin.InfoAttribute.Version) : "")
- + (!string.IsNullOrEmpty(plugin.InfoAttribute.Author)
- ? (" by " + plugin.InfoAttribute.Author) : "")
- + (!string.IsNullOrEmpty(plugin.InfoAttribute.DownloadLink)
- ? (" (" + plugin.InfoAttribute.DownloadLink + ")")
- : "")
- );
- MelonModLogger.LogDLLStatus(plugin.Compatibility);
- MelonModLogger.Log("------------------------------");
- }
- }
- Plugins = TempPlugins;
- }
- if (Plugins.Count <= 0)
- {
- MelonModLogger.Log("No Plugins Loaded!");
- MelonModLogger.Log("------------------------------");
- }
-
- if (Mods.Count > 0)
- {
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- {
- MelonModLogger.Log(mod.InfoAttribute.Name
- + (!string.IsNullOrEmpty(mod.InfoAttribute.Version)
- ? (" v" + mod.InfoAttribute.Version) : "")
- + (!string.IsNullOrEmpty(mod.InfoAttribute.Author)
- ? (" by " + mod.InfoAttribute.Author) : "")
- + (!string.IsNullOrEmpty(mod.InfoAttribute.DownloadLink)
- ? (" (" + mod.InfoAttribute.DownloadLink + ")")
- : "")
- );
- MelonModLogger.LogDLLStatus(mod.Compatibility);
- MelonModLogger.Log("------------------------------");
- }
- }
- Mods.RemoveAll((MelonMod mod) => (mod.Compatibility >= MelonBase.MelonCompatibility.INCOMPATIBLE));
- DependencyGraph.TopologicalSort(Mods, mod => mod.InfoAttribute.Name);
- }
- if (Mods.Count <= 0)
- {
- MelonModLogger.Log("No Mods Loaded!");
- MelonModLogger.Log("------------------------------");
- }
-
- if ((Plugins.Count > 0) || (Mods.Count > 0))
- AddUnityDebugLog();
-
- if (Plugins.Count > 0)
- {
- HashSet failedPlugins = new HashSet();
- for (int i = 0; i < Plugins.Count; i++)
- {
- MelonPlugin plugin = Plugins[i];
- if (plugin != null)
- try { InitializeModOrPlugin(plugin); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); failedPlugins.Add(plugin); }
- }
- Plugins.RemoveAll(plugin => failedPlugins.Contains(plugin));
- }
-
- if (Mods.Count > 0)
- {
- HashSet failedMods = new HashSet();
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { InitializeModOrPlugin(mod); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); failedMods.Add(mod); }
- }
- Mods.RemoveAll(mod => failedMods.Contains(mod));
- }
-
- if ((Plugins.Count <= 0) && (Mods.Count <= 0))
- SupportModule.Destroy();
- }
-
- private static void InitializeModOrPlugin(MelonBase modOrPlugin) {
- string harmonyId = modOrPlugin.Assembly.FullName;
- HarmonyInstance harmony = HarmonyInstance.Create(harmonyId);
- try
- {
- harmony.PatchAll(modOrPlugin.Assembly);
- modOrPlugin.OnApplicationStart();
- }
- catch (Exception)
- {
- harmony.UnpatchAll(harmonyId);
- throw;
- }
- }
-
- public static void OnApplicationQuit()
- {
- if (Plugins.Count > 0)
- {
- for (int i = 0; i < Plugins.Count; i++)
- {
- MelonPlugin plugin = Plugins[i];
- if (plugin != null)
- try { plugin.OnApplicationQuit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); }
- }
- }
- if (Mods.Count > 0)
- {
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnApplicationQuit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
- if ((Plugins.Count > 0) || (Mods.Count > 0))
- ModPrefs.SaveConfig();
- Harmony.HarmonyInstance.UnpatchAllInstances();
- Imports.UNLOAD_MELONLOADER();
- if (Imports.IsQuitFix()) Process.GetCurrentProcess().Kill();
- }
-
- public static void OnModSettingsApplied()
- {
- if (Plugins.Count > 0)
- for (int i = 0; i < Plugins.Count; i++)
- {
- MelonPlugin plugin = Plugins[i];
- if (plugin != null)
- try { plugin.OnModSettingsApplied(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); }
- }
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnModSettingsApplied(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- public static void OnUpdate()
- {
- SceneHandler.CheckForSceneChange();
- if (Imports.IsIl2CppGame() && IsVRChat)
- VRChat_CheckUiManager();
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- public static void OnFixedUpdate()
- {
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnFixedUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- public static void OnLateUpdate()
- {
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnLateUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- public static void OnGUI()
- {
- if (Mods.Count > 0)
- {
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnGUI(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
- }
-
- internal static void OnLevelIsLoading()
- {
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnLevelIsLoading(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- internal static void OnLevelWasLoaded(int level)
- {
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnLevelWasLoaded(level); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- internal static void OnLevelWasInitialized(int level)
- {
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.OnLevelWasInitialized(level); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
-
- private static bool ShouldCheckForUiManager = true;
- private static Type VRCUiManager = null;
- private static PropertyInfo VRCUiManager_Instance = null;
- private static void VRChat_CheckUiManager()
- {
- if (ShouldCheckForUiManager)
- {
- if (VRCUiManager == null)
- VRCUiManager = Assembly_CSharp.GetType("VRCUiManager");
- if (VRCUiManager != null)
- {
- if (VRCUiManager_Instance == null)
- VRCUiManager_Instance = VRCUiManager.GetProperty("field_Protected_Static_VRCUiManager_0");
- if (VRCUiManager_Instance != null)
- {
- object returnval = VRCUiManager_Instance.GetValue(null, new object[0]);
- if (returnval != null)
- {
- ShouldCheckForUiManager = false;
- if (Mods.Count > 0)
- for (int i = 0; i < Mods.Count; i++)
- {
- MelonMod mod = Mods[i];
- if (mod != null)
- try { mod.VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); }
- }
- }
- }
- }
- }
- }
-
- private static void LoadDLLs(bool plugins = false)
- {
- string searchdir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, (plugins ? "Plugins" : "Mods"));
- if (!Directory.Exists(searchdir))
- Directory.CreateDirectory(searchdir);
- else
- {
- // DLL
- string[] files = Directory.GetFiles(searchdir, "*.dll");
- if (files.Length > 0)
- {
- for (int i = 0; i < files.Length; i++)
- {
- string file = files[i];
- if (!string.IsNullOrEmpty(file))
- {
- if (plugins)
- {
- if ((Imports.IsDevPluginsOnly() && !file.EndsWith("-dev.dll")) || (!Imports.IsDevPluginsOnly() && file.EndsWith("-dev.dll")))
- continue;
- }
- else
- {
- if ((Imports.IsDevModsOnly() && !file.EndsWith("-dev.dll")) || (!Imports.IsDevModsOnly() && file.EndsWith("-dev.dll")))
- continue;
- }
- try
- {
- LoadAssembly(File.ReadAllBytes(file), plugins, file);
- }
- catch (Exception e)
- {
- MelonModLogger.LogError("Unable to load " + file + ":\n" + e.ToString());
- MelonModLogger.Log("------------------------------");
- }
- }
- }
- }
-
- // ZIP
- string[] zippedFiles = Directory.GetFiles(searchdir, "*.zip");
- if (zippedFiles.Length > 0)
- {
- for (int i = 0; i < zippedFiles.Length; i++)
- {
- string file = zippedFiles[i];
- if (!string.IsNullOrEmpty(file))
- {
- try
- {
- using (var fileStream = File.OpenRead(file))
- {
- using (var zipInputStream = new ZipInputStream(fileStream))
- {
- ZipEntry entry;
- while ((entry = zipInputStream.GetNextEntry()) != null)
- {
- string filename = Path.GetFileName(entry.Name);
- if (string.IsNullOrEmpty(filename) || !filename.EndsWith(".dll"))
- continue;
-
- if (plugins)
- {
- if ((Imports.IsDevPluginsOnly() && !filename.EndsWith("-dev.dll")) || (!Imports.IsDevPluginsOnly() && filename.EndsWith("-dev.dll")))
- continue;
- }
- else
- {
- if ((Imports.IsDevModsOnly() && !filename.EndsWith("-dev.dll")) || (!Imports.IsDevModsOnly() && filename.EndsWith("-dev.dll")))
- continue;
- }
-
- using (var unzippedFileStream = new MemoryStream())
- {
- int size = 0;
- byte[] buffer = new byte[4096];
- while (true)
- {
- size = zipInputStream.Read(buffer, 0, buffer.Length);
- if (size > 0)
- unzippedFileStream.Write(buffer, 0, size);
- else
- break;
- }
- LoadAssembly(unzippedFileStream.ToArray(), plugins, (file + "/" + filename));
- }
- }
- }
- }
- }
- catch (Exception e)
- {
- MelonModLogger.LogError("Unable to load " + file + ":\n" + e.ToString());
- MelonModLogger.Log("------------------------------");
- }
- }
- }
- }
- }
- }
-
- private static void LoadDLLFromAssembly(Assembly assembly, bool isPlugin = false, string filelocation = null)
- {
- if (isPlugin)
- LoadPluginFromAssembly(assembly, filelocation);
- else
- LoadModFromAssembly(assembly, filelocation);
- }
-
- private static void LoadPluginFromAssembly(Assembly assembly, string filelocation = null)
- {
- MelonPluginInfoAttribute pluginInfoAttribute = assembly.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonPluginInfoAttribute))) as MelonPluginInfoAttribute;
- if ((pluginInfoAttribute != null) && (pluginInfoAttribute.SystemType != null) && pluginInfoAttribute.SystemType.IsSubclassOf(typeof(MelonPlugin)))
- {
- bool isCompatible = false;
- bool isUniversal = false;
- bool hasAttribute = true;
- MelonPluginGameAttribute[] pluginGameAttributes = assembly.GetCustomAttributes(typeof(MelonPluginGameAttribute), true) as MelonPluginGameAttribute[];
- int pluginGameAttributes_Count = pluginGameAttributes.Length;
- if (pluginGameAttributes_Count > 0)
- {
- for (int i = 0; i < pluginGameAttributes_Count; i++)
- {
- MelonPluginGameAttribute pluginGameAttribute = pluginGameAttributes[i];
- if (CurrentGameAttribute.IsCompatible(pluginGameAttribute))
- {
- isCompatible = true;
- isUniversal = CurrentGameAttribute.IsCompatibleBecauseUniversal(pluginGameAttribute);
- break;
- }
- }
- }
- else
- hasAttribute = false;
- try
- {
- MelonPlugin pluginInstance = Activator.CreateInstance(pluginInfoAttribute.SystemType) as MelonPlugin;
- if (pluginInstance != null)
- {
- pluginInstance.InfoAttribute = pluginInfoAttribute;
- if (pluginGameAttributes_Count > 0)
- pluginInstance.GameAttributes = pluginGameAttributes;
- else
- pluginInstance.GameAttributes = null;
- pluginInstance.Location = filelocation;
- pluginInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL : (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE : (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE)));
- if (pluginInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)
- {
- pluginInstance.Assembly = assembly;
- }
- Plugins.Add(pluginInstance);
- }
- else
- MelonModLogger.LogError("Unable to load Plugin in " + assembly.GetName() + "! Failed to Create Instance!");
- }
- catch (Exception e) { MelonModLogger.LogError("Unable to load Plugin in " + assembly.GetName() + "! " + e.ToString()); }
- }
- }
-
- private static void LoadModFromAssembly(Assembly assembly, string filelocation = null)
- {
- MelonModInfoAttribute modInfoAttribute = assembly.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonModInfoAttribute))) as MelonModInfoAttribute;
- if ((modInfoAttribute != null) && (modInfoAttribute.SystemType != null) && modInfoAttribute.SystemType.IsSubclassOf(typeof(MelonMod)))
- {
- bool isCompatible = false;
- bool isUniversal = false;
- bool hasAttribute = true;
- MelonModGameAttribute[] modGameAttributes = assembly.GetCustomAttributes(typeof(MelonModGameAttribute), true) as MelonModGameAttribute[];
- int modGameAttributes_Count = modGameAttributes.Length;
- if (modGameAttributes_Count > 0)
- {
- for (int i = 0; i < modGameAttributes_Count; i++)
- {
- MelonModGameAttribute modGameAttribute = modGameAttributes[i];
- if (CurrentGameAttribute.IsCompatible(modGameAttribute))
- {
- isCompatible = true;
- isUniversal = CurrentGameAttribute.IsCompatibleBecauseUniversal(modGameAttribute);
- break;
- }
- }
- }
- else
- hasAttribute = false;
- try
- {
- MelonMod modInstance = Activator.CreateInstance(modInfoAttribute.SystemType) as MelonMod;
- if (modInstance != null)
- {
- modInstance.InfoAttribute = modInfoAttribute;
- if (modGameAttributes_Count > 0)
- modInstance.GameAttributes = modGameAttributes;
- else
- modInstance.GameAttributes = null;
- modInstance.Location = filelocation;
- modInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL : (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE : (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE)));
- if (modInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)
- {
- modInstance.Assembly = assembly;
- }
- Mods.Add(modInstance);
- }
- else
- MelonModLogger.LogError("Unable to load Mod in " + assembly.GetName() + "! Failed to Create Instance!");
- }
- catch (Exception e) { MelonModLogger.LogError("Unable to load Mod in " + assembly.GetName() + "! " + e.ToString()); }
- }
- }
-
- private static void LoadAssembly(string file, bool isPlugin = false) => LoadDLLFromAssembly(Assembly.LoadFrom(file), isPlugin, file);
- private static void LoadAssembly(byte[] data, bool isPlugin = false, string filelocation = null) => LoadDLLFromAssembly(Assembly.Load(data), isPlugin, filelocation);
- private static void LoadAssembly(Assembly asm, bool isPlugin = false, string filelocation = null)
- {
- if (!asm.Equals(null))
- LoadDLLFromAssembly(asm, isPlugin, filelocation);
- else
- MelonModLogger.LogError("Unable to load " + asm);
- }
-
- private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonModLogger.LogError((e.ExceptionObject as Exception).ToString());
-
- internal static string GetUnityVersion()
- {
- string exepath = Imports.GetExePath();
- string ggm_path = Path.Combine(Imports.GetGameDataDirectory(), "globalgamemanagers");
- if (!File.Exists(ggm_path))
- {
- FileVersionInfo versioninfo = FileVersionInfo.GetVersionInfo(exepath);
- if ((versioninfo == null) || string.IsNullOrEmpty(versioninfo.FileVersion))
- return "UNKNOWN";
- return versioninfo.FileVersion.Substring(0, versioninfo.FileVersion.LastIndexOf('.'));
- }
- else
- {
- byte[] ggm_bytes = File.ReadAllBytes(ggm_path);
- if ((ggm_bytes == null) || (ggm_bytes.Length <= 0))
- return "UNKNOWN";
- int start_position = 0;
- for (int i = 10; i < ggm_bytes.Length; i++)
- {
- byte pos_byte = ggm_bytes[i];
- if ((pos_byte <= 0x39) && (pos_byte >= 0x30))
- {
- start_position = i;
- break;
- }
- }
- if (start_position == 0)
- return "UNKNOWN";
- int end_position = 0;
- for (int i = start_position; i < ggm_bytes.Length; i++)
- {
- byte pos_byte = ggm_bytes[i];
- if ((pos_byte != 0x2E) && ((pos_byte > 0x39) || (pos_byte < 0x30)))
- {
- end_position = (i - 1);
- break;
- }
- }
- if (end_position == 0)
- return "UNKNOWN";
- int verstr_byte_pos = 0;
- byte[] verstr_byte = new byte[((end_position - start_position) + 1)];
- for (int i = start_position; i <= end_position; i++)
- {
- verstr_byte[verstr_byte_pos] = ggm_bytes[i];
- verstr_byte_pos++;
- }
- return Encoding.UTF8.GetString(verstr_byte, 0, verstr_byte.Length);
- }
- }
-
- private static void AddUnityDebugLog()
- {
- SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------");
- SupportModule.UnityDebugLog("~ This Game has been MODIFIED using MelonLoader. DO NOT report any issues to the Developers! ~");
- SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------");
- }
-
- public static string GetUserDataPath()
- {
- string userDataDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UserData");
- if (!Directory.Exists(userDataDir))
- Directory.CreateDirectory(userDataDir);
- return userDataDir;
- }
- }
-}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonBase.cs b/MelonLoader.ModHandler/MelonBase.cs
index f2fd10726..4849bc52c 100644
--- a/MelonLoader.ModHandler/MelonBase.cs
+++ b/MelonLoader.ModHandler/MelonBase.cs
@@ -1,4 +1,7 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
namespace MelonLoader
{
@@ -7,7 +10,7 @@ public abstract class MelonBase
///
/// Gets the Assembly of the Mod or Plugin.
///
- public System.Reflection.Assembly Assembly { get; internal set; }
+ public Assembly Assembly { get; internal set; }
///
/// Gets the File Location of the Mod or Plugin.
@@ -15,7 +18,7 @@ public abstract class MelonBase
public string Location { get; internal set; }
///
- /// Enum for DLL Compatibility
+ /// Enum for Melon Compatibility
///
public enum MelonCompatibility
{
@@ -30,9 +33,43 @@ public enum MelonCompatibility
///
public MelonCompatibility Compatibility { get; internal set; }
+ ///
+ /// Gets the Info Attribute of the Mod or Plugin.
+ ///
+ public MelonInfoAttribute Info { get; internal set; }
+
+ [Obsolete()]
+ internal MelonModInfoAttribute LegacyModInfo { get; set; }
+ [Obsolete()]
+ internal MelonPluginInfoAttribute LegacyPluginInfo { get; set; }
+
+ ///
+ /// Gets the Game Attributes of the Mod or Plugin.
+ ///
+ public MelonOptionalDependenciesAttribute OptionalDependenciesAttribute { get; internal set; }
+
+ ///
+ /// Gets the Game Attributes of the Mod or Plugin.
+ ///
+ public MelonGameAttribute[] Games { get; internal set; }
+
+ ///
+ /// Gets the Auto-Created Harmony Instance of the Mod or Plugin.
+ ///
+ public Harmony.HarmonyInstance harmonyInstance { get; internal set; }
+
+ [Obsolete()]
+ internal MelonModGameAttribute[] LegacyModGames { get; set; }
+ [Obsolete()]
+ internal MelonPluginGameAttribute[] LegacyPluginGames { get; set; }
+
public virtual void OnApplicationStart() { }
+ public virtual void OnUpdate() { }
+ public virtual void OnLateUpdate() { }
+ public virtual void OnGUI() { }
public virtual void OnApplicationQuit() { }
public virtual void OnModSettingsApplied() { }
+ public virtual void VRChat_OnUiManagerInit() { }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
@@ -41,11 +78,221 @@ public class MelonOptionalDependenciesAttribute : Attribute
///
/// The (simple) assembly names of the dependencies that should be regarded as optional.
///
- public string[] AssemblyNames { get; }
+ public string[] AssemblyNames { get; internal set; }
public MelonOptionalDependenciesAttribute(params string[] assemblyNames)
{
AssemblyNames = assemblyNames;
}
}
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public class MelonInfoAttribute : Attribute
+ {
+ ///
+ /// Gets the System.Type of the Mod.
+ ///
+ public Type SystemType { get; internal set; }
+
+ ///
+ /// Gets the Name of the Mod.
+ ///
+ public string Name { get; internal set; }
+
+ ///
+ /// Gets the Version of the Mod.
+ ///
+ public string Version { get; internal set; }
+
+ ///
+ /// Gets the Author of the Mod.
+ ///
+ public string Author { get; internal set; }
+
+ ///
+ /// Gets the Download Link of the Mod.
+ ///
+ public string DownloadLink { get; internal set; }
+
+ public MelonInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
+ {
+ SystemType = type;
+ Name = name;
+ Version = version;
+ Author = author;
+ DownloadLink = downloadLink;
+ }
+
+ [Obsolete()]
+ internal MelonModInfoAttribute ConvertLegacy_Mod() => new MelonModInfoAttribute(SystemType, Name, Version, Author, DownloadLink);
+ [Obsolete()]
+ internal MelonPluginInfoAttribute ConvertLegacy_Plugin() => new MelonPluginInfoAttribute(SystemType, Name, Version, Author, DownloadLink);
+ }
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class MelonGameAttribute : Attribute
+ {
+ ///
+ /// Gets the target Developer
+ ///
+ public string Developer { get; internal set; }
+
+ ///
+ /// Gets target Game Name
+ ///
+ public string GameName { get; internal set; }
+
+ ///
+ /// Gets whether this Mod can target any Game.
+ ///
+ public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); }
+
+ ///
+ /// Mark this Mod as Universal or Compatible with specific Games.
+ ///
+ public MelonGameAttribute(string developer = null, string gameName = null)
+ {
+ Developer = developer;
+ GameName = gameName;
+ }
+
+ [Obsolete()]
+ internal MelonModGameAttribute ConvertLegacy_Mod() => new MelonModGameAttribute(Developer, GameName);
+ [Obsolete()]
+ internal MelonPluginGameAttribute ConvertLegacy_Plugin() => new MelonPluginGameAttribute(Developer, GameName);
+
+ public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName)));
+ public bool IsCompatible(MelonGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName)));
+ public bool IsCompatibleBecauseUniversal(MelonGameAttribute att) => ((att == null) || Universal || att.Universal);
+ }
+
+ [Obsolete()]
+ internal class MelonLegacyAttributeSupport
+ {
+ internal class Response_Info
+ {
+ internal MelonInfoAttribute Default;
+ [Obsolete()]
+ internal MelonModInfoAttribute Legacy_Mod;
+ [Obsolete()]
+ internal MelonPluginInfoAttribute Legacy_Plugin;
+ internal Response_Info(MelonInfoAttribute def, MelonModInfoAttribute legacy_mod, MelonPluginInfoAttribute legacy_plugin)
+ {
+ Default = def;
+ Legacy_Mod = legacy_mod;
+ Legacy_Plugin = legacy_plugin;
+ }
+ internal void SetupMelon(MelonBase baseInstance)
+ {
+ if (Default != null)
+ baseInstance.Info = Default;
+ if (Legacy_Mod != null)
+ baseInstance.LegacyModInfo = Legacy_Mod;
+ if (Legacy_Plugin != null)
+ baseInstance.LegacyPluginInfo = Legacy_Plugin;
+ }
+ }
+
+ internal class Response_Game
+ {
+ internal MelonGameAttribute[] Default;
+ internal MelonModGameAttribute[] Legacy_Mod;
+ internal MelonPluginGameAttribute[] Legacy_Plugin;
+ internal Response_Game(MelonGameAttribute[] def, MelonModGameAttribute[] legacy_mod, MelonPluginGameAttribute[] legacy_plugin)
+ {
+ Default = def;
+ Legacy_Mod = legacy_mod;
+ Legacy_Plugin = legacy_plugin;
+ }
+ internal void SetupMelon(MelonBase baseInstance)
+ {
+ if (Default.Length > 0)
+ baseInstance.Games = Default;
+ if (Legacy_Mod.Length > 0)
+ baseInstance.LegacyModGames = Legacy_Mod;
+ if (Legacy_Plugin.Length > 0)
+ baseInstance.LegacyPluginGames = Legacy_Plugin;
+ }
+ }
+
+ internal static Response_Info GetMelonInfoAttribute(Assembly asm, bool isPlugin = false)
+ {
+ MelonInfoAttribute def = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonInfoAttribute))) as MelonInfoAttribute;
+ MelonModInfoAttribute legacy_mod = null;
+ MelonPluginInfoAttribute legacy_plugin = null;
+ if (def == null)
+ {
+ if (isPlugin)
+ {
+ legacy_plugin = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonPluginInfoAttribute))) as MelonPluginInfoAttribute;
+ if ((legacy_plugin != null) && (def == null))
+ def = legacy_plugin.Convert();
+ }
+ else
+ {
+ legacy_mod = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonModInfoAttribute))) as MelonModInfoAttribute;
+ if ((legacy_mod != null) && (def == null))
+ def = legacy_mod.Convert();
+ }
+ }
+ else
+ {
+ if (isPlugin)
+ legacy_plugin = def.ConvertLegacy_Plugin();
+ else
+ legacy_mod = def.ConvertLegacy_Mod();
+ }
+ return new Response_Info(def, legacy_mod, legacy_plugin);
+ }
+
+ internal static Response_Game GetMelonGameAttributes(Assembly asm, bool isPlugin = false)
+ {
+ MelonGameAttribute[] def = asm.GetCustomAttributes(typeof(MelonGameAttribute), true) as MelonGameAttribute[];
+ MelonModGameAttribute[] legacy_mod = new MelonModGameAttribute[0];
+ MelonPluginGameAttribute[] legacy_plugin = new MelonPluginGameAttribute[0];
+ if (def.Length <= 0)
+ {
+ if (isPlugin)
+ {
+ legacy_plugin = asm.GetCustomAttributes(typeof(MelonPluginGameAttribute), true) as MelonPluginGameAttribute[];
+ if (legacy_plugin.Length > 0)
+ {
+ List deflist = new List();
+ foreach (MelonPluginGameAttribute att in legacy_plugin)
+ deflist.Add(att.Convert());
+ def = deflist.ToArray();
+ }
+ }
+ else
+ {
+ legacy_mod = asm.GetCustomAttributes(typeof(MelonModGameAttribute), true) as MelonModGameAttribute[];
+ if (legacy_mod.Length > 0)
+ {
+ List deflist = new List();
+ foreach (MelonModGameAttribute att in legacy_mod)
+ deflist.Add(att.Convert());
+ def = deflist.ToArray();
+ }
+ }
+ }
+ else
+ {
+ if (isPlugin)
+ {
+ List legacy_pluginlist = new List();
+ foreach (MelonGameAttribute att in def)
+ legacy_pluginlist.Add(att.ConvertLegacy_Plugin());
+ legacy_plugin = legacy_pluginlist.ToArray();
+ }
+ else
+ {
+ List legacy_modlist = new List();
+ foreach (MelonGameAttribute att in def)
+ legacy_modlist.Add(att.ConvertLegacy_Mod());
+ legacy_mod = legacy_modlist.ToArray();
+ }
+ }
+ return new Response_Game(def, legacy_mod, legacy_plugin);
+ }
+ }
}
diff --git a/MelonLoader.ModHandler/MelonConsole.cs b/MelonLoader.ModHandler/MelonConsole.cs
new file mode 100644
index 000000000..2f27b2522
--- /dev/null
+++ b/MelonLoader.ModHandler/MelonConsole.cs
@@ -0,0 +1,46 @@
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+namespace MelonLoader
+{
+ public class MelonConsole
+ {
+ public static bool Enabled { get; internal set; }
+
+ internal static void Check()
+ {
+ if (!Imports.IsDebugMode()
+#if !DEBUG
+ && IsConsoleEnabled()
+#endif
+ )
+ Create();
+ }
+
+ private static void Create()
+ {
+ Enabled = true;
+ Allocate();
+ Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
+ Console.SetIn(new StreamReader(Console.OpenStandardInput()));
+ SetTitle(BuildInfo.Name + " v" + BuildInfo.Version + " Open-Beta");
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool IsConsoleEnabled();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern void Allocate();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void SetTitle(string title);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern void SetColor(ConsoleColor color);
+
+ internal static void RunLogCallbacks(string namesection, string msg) => LogCallbackHandler?.Invoke(namesection, msg);
+ public static event Action LogCallbackHandler;
+ internal static void RunWarningCallbacks(string namesection, string msg) => WarningCallbackHandler?.Invoke(namesection, msg);
+ public static event Action WarningCallbackHandler;
+ internal static void RunErrorCallbacks(string namesection, string msg) => ErrorCallbackHandler?.Invoke(namesection, msg);
+ public static event Action ErrorCallbackHandler;
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonHandler.cs b/MelonLoader.ModHandler/MelonHandler.cs
new file mode 100644
index 000000000..6a9efa47d
--- /dev/null
+++ b/MelonLoader.ModHandler/MelonHandler.cs
@@ -0,0 +1,394 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using Harmony;
+using MelonLoader.ICSharpCode.SharpZipLib.Zip;
+#pragma warning disable 0612
+#pragma warning disable 0618
+
+namespace MelonLoader
+{
+ public static class MelonHandler
+ {
+ internal static bool HasMelons = false;
+ internal static Assembly Assembly_CSharp = null;
+ private static List _TempPlugins = null;
+ internal static List _Plugins = new List();
+ public static List Plugins { get => _Plugins; }
+ internal static List _Mods = new List();
+ public static List Mods { get => _Mods; }
+
+ internal static void LoadAll(bool plugins = false)
+ {
+ string searchdir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, (plugins ? "Plugins" : "Mods"));
+ if (!Directory.Exists(searchdir))
+ {
+ Directory.CreateDirectory(searchdir);
+ return;
+ }
+ LoadMode loadmode = (plugins ? GetLoadMode_Plugins() : GetLoadMode_Mods());
+
+ // DLL
+ string[] files = Directory.GetFiles(searchdir, "*.dll");
+ if (files.Length > 0)
+ {
+ for (int i = 0; i < files.Length; i++)
+ {
+ string file = files[i];
+ if (string.IsNullOrEmpty(file))
+ continue;
+
+ bool file_extension_check = Path.GetFileNameWithoutExtension(file).EndsWith("-dev");
+ if ((loadmode != LoadMode.BOTH) && ((loadmode == LoadMode.DEV) ? !file_extension_check : file_extension_check))
+ continue;
+
+ try
+ {
+ LoadFromFile(file, plugins);
+ }
+ catch (Exception e)
+ {
+ MelonLogger.LogError("Unable to load " + file + ":\n" + e.ToString());
+ MelonLogger.Log("------------------------------");
+ }
+ }
+ }
+
+ // ZIP
+ string[] zippedFiles = Directory.GetFiles(searchdir, "*.zip");
+ if (zippedFiles.Length > 0)
+ {
+ for (int i = 0; i < zippedFiles.Length; i++)
+ {
+ string file = zippedFiles[i];
+ if (string.IsNullOrEmpty(file))
+ continue;
+ try
+ {
+ using (var fileStream = File.OpenRead(file))
+ {
+ using (var zipInputStream = new ZipInputStream(fileStream))
+ {
+ ZipEntry entry;
+ while ((entry = zipInputStream.GetNextEntry()) != null)
+ {
+ string filename = Path.GetFileName(entry.Name);
+ if (string.IsNullOrEmpty(filename) || !filename.EndsWith(".dll"))
+ continue;
+
+ bool file_extension_check = Path.GetFileNameWithoutExtension(file).EndsWith("-dev");
+ if ((loadmode != LoadMode.BOTH) && ((loadmode == LoadMode.DEV) ? !file_extension_check : file_extension_check))
+ continue;
+
+ using (var unzippedFileStream = new MemoryStream())
+ {
+ int size = 0;
+ byte[] buffer = new byte[4096];
+ while (true)
+ {
+ size = zipInputStream.Read(buffer, 0, buffer.Length);
+ if (size > 0)
+ unzippedFileStream.Write(buffer, 0, size);
+ else
+ break;
+ }
+ LoadFromAssembly(Assembly.Load(unzippedFileStream.ToArray()), plugins, (file + "/" + filename));
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ MelonLogger.LogError("Unable to load " + file + ":\n" + e.ToString());
+ MelonLogger.Log("------------------------------");
+ }
+ }
+ }
+ Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks);
+ }
+
+ public static void LoadFromFile(string filelocation, bool isPlugin = false) => LoadFromAssembly((Imports.IsDebugMode() ? Assembly.LoadFrom(filelocation) : Assembly.Load(File.ReadAllBytes(filelocation))), isPlugin, filelocation);
+ public static void LoadFromAssembly(Assembly asm, bool isPlugin = false, string filelocation = null)
+ {
+ if (!asm.Equals(null))
+ {
+ MelonLegacyAttributeSupport.Response_Info response_Info = MelonLegacyAttributeSupport.GetMelonInfoAttribute(asm, isPlugin);
+ MelonInfoAttribute InfoAttribute = response_Info.Default;
+ if ((InfoAttribute != null) && (InfoAttribute.SystemType != null) && InfoAttribute.SystemType.IsSubclassOf((isPlugin ? typeof(MelonPlugin) : typeof(MelonMod))))
+ {
+ bool isCompatible = false;
+ bool isUniversal = false;
+ bool hasAttribute = true;
+ MelonLegacyAttributeSupport.Response_Game response_Game = MelonLegacyAttributeSupport.GetMelonGameAttributes(asm, isPlugin);
+ MelonGameAttribute[] GameAttributes = response_Game.Default;
+ int GameAttributes_Count = GameAttributes.Length;
+ if (GameAttributes_Count > 0)
+ {
+ for (int i = 0; i < GameAttributes_Count; i++)
+ {
+ MelonGameAttribute GameAttribute = GameAttributes[i];
+ if (MelonLoaderBase.CurrentGameAttribute.IsCompatible(GameAttribute))
+ {
+ isCompatible = true;
+ isUniversal = MelonLoaderBase.CurrentGameAttribute.IsCompatibleBecauseUniversal(GameAttribute);
+ break;
+ }
+ }
+ }
+ else
+ hasAttribute = false;
+ MelonBase baseInstance = Activator.CreateInstance(InfoAttribute.SystemType) as MelonBase;
+ if (baseInstance != null)
+ {
+ response_Info.SetupMelon(baseInstance);
+ response_Game.SetupMelon(baseInstance);
+ baseInstance.OptionalDependenciesAttribute = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonOptionalDependenciesAttribute))) as MelonOptionalDependenciesAttribute;
+ baseInstance.Location = filelocation;
+ baseInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL :
+ (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE :
+ (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE)
+ )
+ );
+ if (baseInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)
+ {
+ baseInstance.Assembly = asm;
+ baseInstance.harmonyInstance = HarmonyInstance.Create(asm.FullName);
+ }
+ if (isPlugin)
+ _Plugins.Add((MelonPlugin)baseInstance);
+ else
+ _Mods.Add((MelonMod)baseInstance);
+ }
+ else
+ MelonLogger.LogError("Unable to load " + asm.GetName() + "! Failed to Create Instance!");
+ }
+ }
+ }
+
+ internal static void LogAndPrune()
+ {
+ if (_Plugins.Count > 0)
+ {
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ LogMelonInfo(_Plugins[i]);
+ _Plugins = _TempPlugins;
+ }
+ if (_Plugins.Count <= 0)
+ {
+ MelonLogger.Log("No Plugins Loaded!");
+ MelonLogger.Log("------------------------------");
+ }
+ else
+ HasMelons = true;
+
+ if (_Mods.Count > 0)
+ {
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ LogMelonInfo(_Mods[i]);
+ _Mods.RemoveAll((MelonMod mod) => ((mod == null) || (mod.Compatibility >= MelonBase.MelonCompatibility.INCOMPATIBLE)));
+ DependencyGraph.TopologicalSort(_Mods, mod => mod.Info.Name);
+ }
+ if (_Mods.Count <= 0)
+ {
+ MelonLogger.Log("No Mods Loaded!");
+ MelonLogger.Log("------------------------------");
+ }
+ else
+ HasMelons = true;
+ }
+
+ private static void LogMelonInfo(MelonBase melon)
+ {
+ MelonLogger.Log(melon.Info.Name
+ + (!string.IsNullOrEmpty(melon.Info.Version)
+ ? (" v" + melon.Info.Version) : "")
+ + (!string.IsNullOrEmpty(melon.Info.Author)
+ ? (" by " + melon.Info.Author) : "")
+ + (!string.IsNullOrEmpty(melon.Info.DownloadLink)
+ ? (" (" + melon.Info.DownloadLink + ")")
+ : "")
+ );
+ MelonLogger.LogMelonCompatibility(melon.Compatibility);
+ MelonLogger.Log("------------------------------");
+ }
+
+ internal static void OnPreInitialization()
+ {
+ if (_Plugins.Count > 0)
+ {
+ HashSet failedPlugins = new HashSet();
+ _TempPlugins = _Plugins.Where(plugin => (plugin.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)).ToList();
+ DependencyGraph.TopologicalSort(_TempPlugins, plugin => plugin.Info.Name);
+ for (int i = 0; i < _TempPlugins.Count; i++)
+ if (_TempPlugins[i] != null)
+ try { _TempPlugins[i].OnPreInitialization(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _TempPlugins[i].Info.Name); failedPlugins.Add(_TempPlugins[i]); }
+ _TempPlugins.RemoveAll(plugin => ((plugin == null) || failedPlugins.Contains(plugin)));
+ Main.LegacySupport(_Mods, _TempPlugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks);
+ }
+ }
+
+ internal static void OnApplicationStart()
+ {
+ if (_Plugins.Count > 0)
+ {
+ HashSet failedPlugins = new HashSet();
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].harmonyInstance.PatchAll(_Plugins[i].Assembly); _Plugins[i].OnApplicationStart(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); HarmonyInstance.UnpatchAllMelonInstances(_Plugins[i]); failedPlugins.Add(_Plugins[i]); }
+ _Plugins.RemoveAll(plugin => ((plugin == null) || failedPlugins.Contains(plugin)));
+ Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks);
+ }
+ if (_Mods.Count > 0)
+ {
+ HashSet failedMods = new HashSet();
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].harmonyInstance.PatchAll(_Mods[i].Assembly); _Mods[i].OnApplicationStart(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); HarmonyInstance.UnpatchAllMelonInstances(_Mods[i]); failedMods.Add(_Mods[i]); }
+ _Mods.RemoveAll(mod => ((mod == null) || failedMods.Contains(mod)));
+ Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks);
+ }
+ }
+
+ public static void OnApplicationQuit()
+ {
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].OnApplicationQuit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnApplicationQuit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ MelonLoaderBase.Quit();
+ }
+
+ public static void OnModSettingsApplied()
+ {
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ try { _Plugins[i].OnModSettingsApplied(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnModSettingsApplied(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ public static void OnUpdate()
+ {
+ SceneHandler.CheckForSceneChange();
+ if (Imports.IsIl2CppGame() && MelonLoaderBase._IsVRChat)
+ VRChat_CheckUiManager();
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].OnUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ public static void OnFixedUpdate()
+ {
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnFixedUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ public static void OnLateUpdate()
+ {
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].OnLateUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnLateUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ public static void OnGUI()
+ {
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].OnGUI(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnGUI(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ internal static void OnLevelIsLoading()
+ {
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnLevelIsLoading(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ internal static void OnLevelWasLoaded(int level)
+ {
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnLevelWasLoaded(level); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ internal static void OnLevelWasInitialized(int level)
+ {
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].OnLevelWasInitialized(level); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ }
+
+ private static bool ShouldCheckForUiManager = true;
+ private static Type VRCUiManager = null;
+ private static PropertyInfo VRCUiManager_Instance = null;
+ private static void VRChat_CheckUiManager()
+ {
+ if (!ShouldCheckForUiManager)
+ return;
+ if (VRCUiManager == null)
+ VRCUiManager = Assembly_CSharp.GetType("VRCUiManager");
+ if (VRCUiManager == null)
+ return;
+ if (VRCUiManager_Instance == null)
+ VRCUiManager_Instance = VRCUiManager.GetProperty("field_Protected_Static_VRCUiManager_0");
+ if (VRCUiManager_Instance == null)
+ return;
+ object returnval = VRCUiManager_Instance.GetValue(null, new object[0]);
+ if (returnval == null)
+ return;
+ ShouldCheckForUiManager = false;
+ if (_Mods.Count > 0)
+ for (int i = 0; i < _Mods.Count; i++)
+ if (_Mods[i] != null)
+ try { _Mods[i].VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); }
+ if (_Plugins.Count > 0)
+ for (int i = 0; i < _Plugins.Count; i++)
+ if (_Plugins[i] != null)
+ try { _Plugins[i].VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); }
+ }
+
+ private enum LoadMode
+ {
+ NORMAL,
+ DEV,
+ BOTH
+ }
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static LoadMode GetLoadMode_Plugins();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static LoadMode GetLoadMode_Mods();
+ }
+}
diff --git a/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj b/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj
index d2e4c1a57..a5e4e6a28 100644
--- a/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj
+++ b/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj
@@ -43,8 +43,9 @@
-
+
+
@@ -80,11 +81,12 @@
-
-
+
+
+
-
+
diff --git a/MelonLoader.ModHandler/MelonLoaderBase.cs b/MelonLoader.ModHandler/MelonLoaderBase.cs
new file mode 100644
index 000000000..126dd0ae5
--- /dev/null
+++ b/MelonLoader.ModHandler/MelonLoaderBase.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Harmony;
+
+namespace MelonLoader
+{
+ public static class MelonLoaderBase
+ {
+ internal static MelonGameAttribute CurrentGameAttribute = null;
+ internal static bool _IsVRChat = false;
+ public static bool IsVRChat { get => _IsVRChat; }
+ internal static bool _IsBoneworks = false;
+ public static bool IsBoneworks { get => _IsBoneworks; }
+
+ private static void Initialize()
+ {
+ Setup();
+ MelonConsole.Check();
+ AssemblyGenerator.Check();
+ if (!AssemblyGenerator.HasGeneratedAssembly)
+ return;
+ MelonHandler.LoadAll(true);
+ if (MelonHandler.HasMelons)
+ MelonPrefs.Setup();
+ MelonHandler.OnPreInitialization();
+ }
+
+ private static void Startup()
+ {
+ if (!AssemblyGenerator.HasGeneratedAssembly)
+ return;
+ SetupSupport();
+ WelcomeLog();
+ MelonHandler.LoadAll();
+ MelonHandler.LogAndPrune();
+ if (!MelonHandler.HasMelons)
+ return;
+ MelonPrefs.Setup();
+ AddUnityDebugLog();
+ MelonHandler.OnApplicationStart();
+ if (!MelonHandler.HasMelons)
+ SupportModule.Destroy();
+ }
+
+ internal static void Quit()
+ {
+ if (MelonHandler.HasMelons)
+ MelonPrefs.SaveConfig();
+ HarmonyInstance.UnpatchAllInstances();
+ UNLOAD();
+ if (IsQuitFix()) Process.GetCurrentProcess().Kill();
+ }
+
+ private static void Setup()
+ {
+ FixCurrentBaseDirectory();
+ AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;
+ CurrentGameAttribute = new MelonGameAttribute(Imports.GetCompanyName(), Imports.GetProductName());
+ if (Imports.IsIl2CppGame())
+ {
+ _IsVRChat = CurrentGameAttribute.IsGame("VRChat", "VRChat");
+ _IsBoneworks = CurrentGameAttribute.IsGame("Stress Level Zero", "BONEWORKS");
+ }
+ }
+
+ private static void SetupSupport()
+ {
+ if (Imports.IsIl2CppGame())
+ {
+ if (_IsVRChat)
+ MelonHandler.Assembly_CSharp = Assembly.Load("Assembly-CSharp");
+ UnhollowerSupport.Initialize();
+ }
+ SupportModule.Initialize();
+ }
+
+ private static void WelcomeLog()
+ {
+ MelonLogger.Log("------------------------------");
+ MelonLogger.Log("Unity " + _UnityVersion);
+ MelonLogger.Log("OS: " + Environment.OSVersion.ToString());
+ MelonLogger.Log("------------------------------");
+ MelonLogger.Log("Name: " + CurrentGameAttribute.GameName);
+ MelonLogger.Log("Developer: " + CurrentGameAttribute.Developer);
+ MelonLogger.Log("Type: " + (Imports.IsIl2CppGame() ? "Il2Cpp" : (IsOldMono() ? "Mono" : "MonoBleedingEdge")));
+ MelonLogger.Log("------------------------------");
+ MelonLogger.Log("Using v" + BuildInfo.Version + " Open-Beta");
+ MelonLogger.Log("------------------------------");
+ }
+
+ private static void AddUnityDebugLog()
+ {
+ SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------");
+ SupportModule.UnityDebugLog("~ This Game has been MODIFIED using MelonLoader. DO NOT report any issues to the Developers! ~");
+ SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------");
+ }
+
+ public static void FixCurrentBaseDirectory()
+ {
+ ((AppDomainSetup)typeof(AppDomain).GetProperty("SetupInformationNoCopy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(AppDomain.CurrentDomain, new object[0])).ApplicationBase = Imports.GetGameDirectory();
+ Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
+ }
+
+ private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonLogger.LogError((e.ExceptionObject as Exception).ToString());
+
+ private static string _UserDataPath = null;
+ public static string UserDataPath
+ {
+ get
+ {
+ if (_UserDataPath == null)
+ {
+ _UserDataPath = Path.Combine(Imports.GetGameDirectory(), "UserData");
+ if (!Directory.Exists(_UserDataPath))
+ Directory.CreateDirectory(_UserDataPath);
+ }
+ return _UserDataPath;
+ }
+ }
+
+ private static string _UnityVersion = null;
+ public static string UnityVersion
+ {
+ get
+ {
+ if (_UnityVersion != null)
+ return _UnityVersion;
+ string exepath = Imports.GetExePath();
+ string ggm_path = Path.Combine(Imports.GetGameDataDirectory(), "globalgamemanagers");
+ if (!File.Exists(ggm_path))
+ {
+ FileVersionInfo versioninfo = FileVersionInfo.GetVersionInfo(exepath);
+ if ((versioninfo == null) || string.IsNullOrEmpty(versioninfo.FileVersion))
+ return "UNKNOWN";
+ return versioninfo.FileVersion.Substring(0, versioninfo.FileVersion.LastIndexOf('.'));
+ }
+ byte[] ggm_bytes = File.ReadAllBytes(ggm_path);
+ if ((ggm_bytes == null) || (ggm_bytes.Length <= 0))
+ return "UNKNOWN";
+ int start_position = 0;
+ for (int i = 10; i < ggm_bytes.Length; i++)
+ {
+ byte pos_byte = ggm_bytes[i];
+ if ((pos_byte <= 0x39) && (pos_byte >= 0x30))
+ {
+ start_position = i;
+ break;
+ }
+ }
+ if (start_position == 0)
+ return "UNKNOWN";
+ int end_position = 0;
+ for (int i = start_position; i < ggm_bytes.Length; i++)
+ {
+ byte pos_byte = ggm_bytes[i];
+ if ((pos_byte != 0x2E) && ((pos_byte > 0x39) || (pos_byte < 0x30)))
+ {
+ end_position = (i - 1);
+ break;
+ }
+ }
+ if (end_position == 0)
+ return "UNKNOWN";
+ int verstr_byte_pos = 0;
+ byte[] verstr_byte = new byte[((end_position - start_position) + 1)];
+ for (int i = start_position; i <= end_position; i++)
+ {
+ verstr_byte[verstr_byte_pos] = ggm_bytes[i];
+ verstr_byte_pos++;
+ }
+ return _UnityVersion = Encoding.UTF8.GetString(verstr_byte, 0, verstr_byte.Length);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool IsOldMono();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private extern static bool IsQuitFix();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void UNLOAD(bool doquitfix = true);
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonLogger.cs b/MelonLoader.ModHandler/MelonLogger.cs
new file mode 100644
index 000000000..3227e2855
--- /dev/null
+++ b/MelonLoader.ModHandler/MelonLogger.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace MelonLoader
+{
+ public class MelonLogger
+ {
+ public static void Log(string s)
+ {
+ string namesection = GetNameSection();
+ Native_Log(namesection, s);
+ MelonConsole.RunLogCallbacks(namesection, s);
+ }
+
+ public static void Log(ConsoleColor color, string s)
+ {
+ string namesection = GetNameSection();
+ Native_LogColor(namesection, s, color);
+ MelonConsole.RunLogCallbacks(namesection, s);
+ }
+
+ public static void Log(string s, params object[] args)
+ {
+ string namesection = GetNameSection();
+ string fmt = string.Format(s, args);
+ Native_Log(namesection, fmt);
+ MelonConsole.RunLogCallbacks(namesection, fmt);
+ }
+
+ public static void Log(ConsoleColor color, string s, params object[] args)
+ {
+ string namesection = GetNameSection();
+ string fmt = string.Format(s, args);
+ Native_LogColor(namesection, fmt, color);
+ MelonConsole.RunLogCallbacks(namesection, fmt);
+ }
+
+ public static void LogWarning(string s)
+ {
+ string namesection = GetNameSection();
+ Native_LogWarning(namesection, s);
+ MelonConsole.RunWarningCallbacks(namesection, s);
+ }
+
+ public static void LogWarning(string s, params object[] args)
+ {
+ string namesection = GetNameSection();
+ string fmt = string.Format(s, args);
+ Native_LogWarning(namesection, fmt);
+ MelonConsole.RunWarningCallbacks(namesection, fmt);
+ Native_LogWarning(GetNameSection(), fmt);
+ }
+
+ public static void LogError(string s)
+ {
+ string namesection = GetNameSection();
+ Native_LogError(namesection, s);
+ MelonConsole.RunErrorCallbacks(namesection, s);
+ }
+ public static void LogError(string s, params object[] args)
+ {
+ string namesection = GetNameSection();
+ string fmt = string.Format(s, args);
+ Native_LogError(namesection, fmt);
+ MelonConsole.RunErrorCallbacks(namesection, fmt);
+ }
+
+ internal static void LogMelonError(string msg, string modname)
+ {
+ string namesection = (string.IsNullOrEmpty(modname) ? "" : ("[" + modname.Replace(" ", "_") + "] "));
+ Native_LogMelonError(namesection, msg);
+ MelonConsole.RunErrorCallbacks(namesection, msg);
+ }
+
+ internal static void LogMelonCompatibility(MelonBase.MelonCompatibility comp) => Native_LogMelonCompatibility(comp);
+
+ internal static string GetNameSection()
+ {
+ StackTrace st = new StackTrace(2, true);
+ StackFrame sf = st.GetFrame(0);
+ if (sf != null)
+ {
+ MethodBase method = sf.GetMethod();
+ if (!method.Equals(null))
+ {
+ Type methodClassType = method.DeclaringType;
+ if (!methodClassType.Equals(null))
+ {
+ Assembly asm = methodClassType.Assembly;
+ if (!asm.Equals(null))
+ {
+ MelonPlugin plugin = MelonHandler.Plugins.Find(x => (x.Assembly == asm));
+ if (plugin != null)
+ {
+ if (!string.IsNullOrEmpty(plugin.Info.Name))
+ return "[" + plugin.Info.Name.Replace(" ", "_") + "] ";
+ }
+ else
+ {
+ MelonMod mod = MelonHandler.Mods.Find(x => (x.Assembly == asm));
+ if (mod != null)
+ {
+ if (!string.IsNullOrEmpty(mod.Info.Name))
+ return "[" + mod.Info.Name.Replace(" ", "_") + "] ";
+ }
+ }
+ }
+ }
+ }
+ }
+ return "";
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_Log(string namesection, string txt);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_LogColor(string namesection, string txt, ConsoleColor color);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_LogWarning(string namesection, string txt);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_LogError(string namesection, string txt);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_LogMelonError(string namesection, string txt);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_LogMelonCompatibility(MelonBase.MelonCompatibility comp);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void Native_ThrowInternalError(string txt);
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonMod.cs b/MelonLoader.ModHandler/MelonMod.cs
index 390fd65c0..a90005d64 100644
--- a/MelonLoader.ModHandler/MelonMod.cs
+++ b/MelonLoader.ModHandler/MelonMod.cs
@@ -1,98 +1,18 @@
using System;
+#pragma warning disable 0108
namespace MelonLoader
{
public abstract class MelonMod : MelonBase
{
- ///
- /// Gets the Info Attribute of the Mod or Plugin.
- ///
- public MelonModInfoAttribute InfoAttribute { get; internal set; }
-
- ///
- /// Gets the Game Attributes of the Mod or Plugin.
- ///
- public MelonModGameAttribute[] GameAttributes { get; internal set; }
+ [Obsolete()]
+ public MelonModInfoAttribute InfoAttribute { get => LegacyModInfo; }
+ [Obsolete()]
+ public MelonModGameAttribute[] GameAttributes { get => LegacyModGames; }
public virtual void OnLevelIsLoading() {}
public virtual void OnLevelWasLoaded(int level) {}
public virtual void OnLevelWasInitialized(int level) {}
- public virtual void OnUpdate() {}
public virtual void OnFixedUpdate() {}
- public virtual void OnLateUpdate() {}
- public virtual void OnGUI() {}
- public virtual void VRChat_OnUiManagerInit() {}
- }
-
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public class MelonModGameAttribute : Attribute
- {
- ///
- /// Gets the target Developer
- ///
- public string Developer { get; }
-
- ///
- /// Gets target Game Name
- ///
- public string GameName { get; }
-
- ///
- /// Gets whether this Mod can target any Game.
- ///
- public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); }
-
- ///
- /// Mark this Mod as Universal or Compatible with specific Games.
- ///
- public MelonModGameAttribute(string developer = null, string gameName = null)
- {
- Developer = developer;
- GameName = gameName;
- }
-
- public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName)));
- public bool IsCompatible(MelonModGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName)));
- public bool IsCompatibleBecauseUniversal(MelonModGameAttribute att) => ((att == null) || Universal || att.Universal);
- public bool IsCompatible(MelonPluginGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName)));
- public bool IsCompatibleBecauseUniversal(MelonPluginGameAttribute att) => ((att == null) || Universal || att.Universal);
- }
-
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
- public class MelonModInfoAttribute : Attribute
- {
- ///
- /// Gets the System.Type of the Mod.
- ///
- public Type SystemType { get; }
-
- ///
- /// Gets the Name of the Mod.
- ///
- public string Name { get; }
-
- ///
- /// Gets the Version of the Mod.
- ///
- public string Version { get; }
-
- ///
- /// Gets the Author of the Mod.
- ///
- public string Author { get; }
-
- ///
- /// Gets the Download Link of the Mod.
- ///
- public string DownloadLink { get; }
-
- public MelonModInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
- {
- SystemType = type;
- Name = name;
- Version = version;
- Author = author;
- DownloadLink = downloadLink;
- }
}
}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonModLogger.cs b/MelonLoader.ModHandler/MelonModLogger.cs
deleted file mode 100644
index 4e9529243..000000000
--- a/MelonLoader.ModHandler/MelonModLogger.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-namespace MelonLoader
-{
- public class MelonModLogger
- {
- internal static bool consoleEnabled = false;
-
- private static string GetNameSection()
- {
- StackTrace st = new StackTrace(2, true);
- StackFrame sf = st.GetFrame(0);
- if (sf != null)
- {
- MethodBase method = sf.GetMethod();
- if (!method.Equals(null))
- {
- Type methodClassType = method.DeclaringType;
- if (!methodClassType.Equals(null))
- {
- Assembly asm = methodClassType.Assembly;
- if (!asm.Equals(null))
- {
- object[] attrArray = asm.GetCustomAttributes(typeof(MelonPluginInfoAttribute), false);
- if ((attrArray.Length > 0) && (attrArray[0] != null))
- {
- MelonPluginInfoAttribute attr = attrArray[0] as MelonPluginInfoAttribute;
- if (!string.IsNullOrEmpty(attr.Name))
- return "[" + attr.Name.Replace(" ", "_") + "] ";
- }
- attrArray = asm.GetCustomAttributes(typeof(MelonModInfoAttribute), false);
- if ((attrArray.Length > 0) && (attrArray[0] != null))
- {
- MelonModInfoAttribute attr = attrArray[0] as MelonModInfoAttribute;
- if (!string.IsNullOrEmpty(attr.Name))
- return "[" + attr.Name.Replace(" ", "_") + "] ";
- }
- }
- }
- }
- }
- return "";
- }
-
- public static void Log(string s) => Native_Log((GetNameSection() + s));
- public static void Log(ConsoleColor color, string s) => Native_LogColor((GetNameSection() + s), color);
- public static void Log(string s, params object[] args) => Native_Log((GetNameSection() + string.Format(s, args)));
- public static void Log(ConsoleColor color, string s, params object[] args) => Native_LogColor((GetNameSection() + string.Format(s, args)), color);
-
- public static void LogWarning(string s) => Native_LogWarning(GetNameSection(), s);
- public static void LogWarning(string s, params object[] args) => Native_LogWarning(GetNameSection(), string.Format(s, args));
-
- public static void LogError(string s) => Native_LogError(GetNameSection(), s);
- public static void LogError(string s, params object[] args) => Native_LogError(GetNameSection(), string.Format(s, args));
- internal static void LogDLLError(string msg, string modname) => Native_LogDLLError((string.IsNullOrEmpty(modname) ? "" : ("[" + modname.Replace(" ", "_") + "] ")), msg);
-
- internal static void LogDLLStatus(MelonBase.MelonCompatibility type) => Native_LogDLLStatus(type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_Log(string txt);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_LogColor(string txt, ConsoleColor color);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_LogWarning(string namesection, string txt);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_LogError(string namesection, string txt);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_LogDLLError(string namesection, string msg);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void Native_LogDLLStatus(MelonBase.MelonCompatibility type);
- }
-}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonPlugin.cs b/MelonLoader.ModHandler/MelonPlugin.cs
index af25c772f..17ff1d041 100644
--- a/MelonLoader.ModHandler/MelonPlugin.cs
+++ b/MelonLoader.ModHandler/MelonPlugin.cs
@@ -1,91 +1,15 @@
using System;
+#pragma warning disable 0108
namespace MelonLoader
{
public abstract class MelonPlugin : MelonBase
{
- ///
- /// Gets the Info Attribute of the Mod or Plugin.
- ///
- public MelonPluginInfoAttribute InfoAttribute { get; internal set; }
-
- ///
- /// Gets the Game Attributes of the Mod or Plugin.
- ///
- public MelonPluginGameAttribute[] GameAttributes { get; internal set; }
+ [Obsolete()]
+ public MelonPluginInfoAttribute InfoAttribute { get => LegacyPluginInfo; }
+ [Obsolete()]
+ public MelonPluginGameAttribute[] GameAttributes { get => LegacyPluginGames; }
public virtual void OnPreInitialization() { }
}
-
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public class MelonPluginGameAttribute : Attribute
- {
- ///
- /// Gets the target Developer
- ///
- public string Developer { get; }
-
- ///
- /// Gets target Game Name
- ///
- public string GameName { get; }
-
- ///
- /// Gets whether this Mod can target any Game.
- ///
- public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); }
-
- ///
- /// Mark this Mod as Universal or Compatible with specific Games.
- ///
- public MelonPluginGameAttribute(string developer = null, string gameName = null)
- {
- Developer = developer;
- GameName = gameName;
- }
-
- public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName)));
- public bool IsCompatible(MelonPluginGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName)));
- public bool IsCompatibleBecauseUniversal(MelonPluginGameAttribute att) => ((att == null) || Universal || att.Universal);
- public bool IsCompatible(MelonModGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName)));
- public bool IsCompatibleBecauseUniversal(MelonModGameAttribute att) => ((att == null) || Universal || att.Universal);
- }
-
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
- public class MelonPluginInfoAttribute : Attribute
- {
- ///
- /// Gets the System.Type of the Mod.
- ///
- public Type SystemType { get; }
-
- ///
- /// Gets the Name of the Mod.
- ///
- public string Name { get; }
-
- ///
- /// Gets the Version of the Mod.
- ///
- public string Version { get; }
-
- ///
- /// Gets the Author of the Mod.
- ///
- public string Author { get; }
-
- ///
- /// Gets the Download Link of the Mod.
- ///
- public string DownloadLink { get; }
-
- public MelonPluginInfoAttribute(Type type, string name, string version, string author, string downloadLink = null)
- {
- SystemType = type;
- Name = name;
- Version = version;
- Author = author;
- DownloadLink = downloadLink;
- }
- }
-}
+}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/MelonPrefs.cs b/MelonLoader.ModHandler/MelonPrefs.cs
new file mode 100644
index 000000000..c86e40c91
--- /dev/null
+++ b/MelonLoader.ModHandler/MelonPrefs.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace MelonLoader
+{
+ public class MelonPrefs
+ {
+ private static string ConfigFileName = "modprefs.ini";
+ private static IniFile ConfigFile = null;
+ private static Dictionary> prefs = new Dictionary>();
+ private static Dictionary categoryDisplayNames = new Dictionary();
+
+ internal static void Setup() { if (ConfigFile == null) ConfigFile = new IniFile(Path.Combine(MelonLoaderBase.UserDataPath, ConfigFileName)); }
+
+ public static void RegisterCategory(string name, string displayText) { categoryDisplayNames[name] = displayText; }
+ public static void RegisterString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, defaultValue, displayText, MelonPreferenceType.STRING, hideFromList); }
+ public static void RegisterBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, defaultValue ? "true" : "false", displayText, MelonPreferenceType.BOOL, hideFromList); }
+ public static void RegisterInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, "" + defaultValue, displayText, MelonPreferenceType.INT, hideFromList); }
+ public static void RegisterFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, "" + defaultValue, displayText, MelonPreferenceType.FLOAT, hideFromList); }
+ private static void Register(string section, string name, string defaultValue, string displayText, MelonPreferenceType type, bool hideFromList)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection))
+ {
+ if (prefsInSection.TryGetValue(name, out MelonPreference pref))
+ MelonLogger.LogError("Trying to registered Pref " + section + ":" + name + " more than one time");
+ else
+ {
+ string toStoreValue = defaultValue;
+ if (ConfigFile.HasKey(section, name))
+ toStoreValue = ConfigFile.GetString(section, name, defaultValue);
+ else ConfigFile.SetString(section, name, defaultValue);
+ prefsInSection.Add(name, new MelonPreference(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText));
+ }
+ }
+ else
+ {
+ Dictionary dic = new Dictionary();
+ string toStoreValue = defaultValue;
+ if (ConfigFile.HasKey(section, name))
+ toStoreValue = ConfigFile.GetString(section, name, defaultValue);
+ else ConfigFile.SetString(section, name, defaultValue);
+ dic.Add(name, new MelonPreference(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText));
+ prefs.Add(section, dic);
+ }
+ }
+
+ public static bool HasKey(string section, string name) { return prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.ContainsKey(name); }
+ public static Dictionary> GetPreferences() { return prefs; }
+ public static string GetCategoryDisplayName(string key) { if (categoryDisplayNames.TryGetValue(key, out string name)) return name; return key; }
+
+ public static void SaveConfig()
+ {
+ foreach (KeyValuePair> prefsInSection in prefs)
+ {
+ foreach (KeyValuePair pref in prefsInSection.Value)
+ {
+ pref.Value.Value = pref.Value.ValueEdited;
+ ConfigFile.SetString(prefsInSection.Key, pref.Key, pref.Value.Value);
+ }
+ }
+ MelonHandler.OnModSettingsApplied();
+ MelonLogger.Log("Config Saved!");
+ }
+
+ public static string GetString(string section, string name)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref))
+ return pref.Value;
+ MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
+ return "";
+ }
+
+ public static void SetString(string section, string name, string value)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref))
+ {
+ pref.Value = pref.ValueEdited = value;
+ ConfigFile.SetString(section, name, value);
+ }
+ else
+ MelonLogger.LogError("Trying to save unknown pref " + section + ":" + name);
+ }
+
+ public static bool GetBool(string section, string name)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref))
+ return (pref.Value.Equals("true") || pref.Value.Equals("1"));
+ MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
+ return false;
+ }
+ public static void SetBool(string section, string name, bool value) { SetString(section, name, value ? "true" : "false"); }
+
+ public static int GetInt(string section, string name)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref))
+ if (int.TryParse(pref.Value, out int valueI))
+ return valueI;
+ MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
+ return 0;
+ }
+ public static void SetInt(string section, string name, int value) { SetString(section, name, value.ToString()); }
+
+ public static float GetFloat(string section, string name)
+ {
+ if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref))
+ if (float.TryParse(pref.Value, out float valueF))
+ return valueF;
+ MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
+ return 0.0f;
+ }
+ public static void SetFloat(string section, string name, float value) { SetString(section, name, value.ToString()); }
+
+ public enum MelonPreferenceType
+ {
+ STRING,
+ BOOL,
+ INT,
+ FLOAT
+ }
+
+ public class MelonPreference
+ {
+ public string Value { get; set; }
+ public string ValueEdited { get; set; }
+ public MelonPreferenceType Type { get; internal set; }
+ public bool Hidden { get; internal set; }
+ public String DisplayText { get; internal set; }
+
+ public MelonPreference(string value, MelonPreferenceType type, bool hidden, string displayText)
+ {
+ Value = value;
+ ValueEdited = value;
+ Type = type;
+ Hidden = hidden;
+ DisplayText = displayText;
+ }
+
+ [Obsolete()]
+ public MelonPreference(string value, ModPrefs.PrefType type, bool hidden, string displayText)
+ {
+ Value = value;
+ ValueEdited = value;
+ Type = (MelonPreferenceType)type;
+ Hidden = hidden;
+ DisplayText = displayText;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/ModPrefs.cs b/MelonLoader.ModHandler/ModPrefs.cs
deleted file mode 100644
index 4ac528519..000000000
--- a/MelonLoader.ModHandler/ModPrefs.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-#pragma warning disable CS0618
-
-namespace MelonLoader
-{
- internal static class ModPrefsController
- {
- private static string filename = "modprefs.ini";
- private static IniFile _instance;
- private static IniFile Instance
- {
- get
- {
- if (_instance == null)
- _instance = new IniFile(Path.Combine(Main.GetUserDataPath(), filename));
- return _instance;
- }
- }
-
- internal static string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
- {
- string value = Instance.IniReadValue(section, name);
- if (!string.IsNullOrEmpty(value))
- return value;
- else if (autoSave)
- SetString(section, name, defaultValue);
- return defaultValue;
- }
- internal static void SetString(string section, string name, string value) { Instance.IniWriteValue(section, name, value.Trim()); }
-
- internal static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
- {
- int value;
- if (int.TryParse(Instance.IniReadValue(section, name), out value))
- return value;
- else if (autoSave)
- SetInt(section, name, defaultValue);
- return defaultValue;
- }
- internal static void SetInt(string section, string name, int value) { Instance.IniWriteValue(section, name, value.ToString()); }
-
- internal static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
- {
- float value;
- if (float.TryParse(Instance.IniReadValue(section, name), out value))
- return value;
- else if (autoSave)
- SetFloat(section, name, defaultValue);
- return defaultValue;
- }
- internal static void SetFloat(string section, string name, float value) { Instance.IniWriteValue(section, name, value.ToString()); }
-
- internal static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
- {
- string sVal = GetString(section, name, null);
- if ("true".Equals(sVal) || "1".Equals(sVal) || "0".Equals(sVal) || "false".Equals(sVal))
- return ("true".Equals(sVal) || "1".Equals(sVal));
- else if (autoSave)
- SetBool(section, name, defaultValue);
- return defaultValue;
- }
- internal static void SetBool(string section, string name, bool value) { Instance.IniWriteValue(section, name, value ? "true" : "false"); }
-
- internal static bool HasKey(string section, string name) { return Instance.IniReadValue(section, name) != null; }
- }
-
- public static class ModPrefs
- {
- private static Dictionary> prefs = new Dictionary>();
- private static Dictionary categoryDisplayNames = new Dictionary();
- public static void RegisterCategory(string name, string displayText) { categoryDisplayNames[name] = displayText; }
- public static void RegisterPrefString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, defaultValue, displayText, PrefType.STRING, hideFromList); }
- public static void RegisterPrefBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, defaultValue ? "true" : "false", displayText, PrefType.BOOL, hideFromList); }
- public static void RegisterPrefInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, "" + defaultValue, displayText, PrefType.INT, hideFromList); }
- public static void RegisterPrefFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, "" + defaultValue, displayText, PrefType.FLOAT, hideFromList); }
-
- private static void RegisterPref(string section, string name, string defaultValue, string displayText, PrefType type, bool hideFromList)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection))
- {
- if (prefsInSection.TryGetValue(name, out PrefDesc pref))
- MelonModLogger.LogError("Trying to registered Pref " + section + ":" + name + " more than one time");
- else
- {
- string toStoreValue = defaultValue;
- if (ModPrefsController.HasKey(section, name))
- toStoreValue = ModPrefsController.GetString(section, name, defaultValue);
- else ModPrefsController.SetString(section, name, defaultValue);
- prefsInSection.Add(name, new PrefDesc(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText));
- }
- }
- else
- {
- Dictionary dic = new Dictionary();
- string toStoreValue = defaultValue;
- if (ModPrefsController.HasKey(section, name))
- toStoreValue = ModPrefsController.GetString(section, name, defaultValue);
- else ModPrefsController.SetString(section, name, defaultValue);
- dic.Add(name, new PrefDesc(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText));
- prefs.Add(section, dic);
- }
- }
-
- public static bool HasKey(string section, string name) { return prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.ContainsKey(name); }
- public static Dictionary> GetPrefs() { return prefs; }
- public static string GetCategoryDisplayName(string key) { if (categoryDisplayNames.TryGetValue(key, out string name)) return name; return key; }
-
- public static void SaveConfig()
- {
- foreach (KeyValuePair> prefsInSection in prefs)
- {
- foreach (KeyValuePair pref in prefsInSection.Value)
- {
- pref.Value.Value = pref.Value.ValueEdited;
- ModPrefsController.SetString(prefsInSection.Key, pref.Key, pref.Value.Value);
- }
- }
- Main.OnModSettingsApplied();
- MelonModLogger.Log("Config Saved!");
- }
-
- public static string GetString(string section, string name)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref))
- return pref.Value;
- MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
- return "";
- }
-
- public static void SetString(string section, string name, string value)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref))
- {
- pref.Value = pref.ValueEdited = value;
- ModPrefsController.SetString(section, name, value);
- }
- else
- MelonModLogger.LogError("Trying to save unknown pref " + section + ":" + name);
- }
-
- public static bool GetBool(string section, string name)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref))
- return (pref.Value.Equals("true") || pref.Value.Equals("1"));
- MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
- return false;
- }
- public static void SetBool(string section, string name, bool value) { SetString(section, name, value ? "true" : "false"); }
-
- public static int GetInt(string section, string name)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref))
- if (int.TryParse(pref.Value, out int valueI))
- return valueI;
- MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
- return 0;
- }
- public static void SetInt(string section, string name, int value) { SetString(section, name, value.ToString()); }
-
- public static float GetFloat(string section, string name)
- {
- if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref))
- if (float.TryParse(pref.Value, out float valueF))
- return valueF;
- MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name);
- return 0.0f;
- }
- public static void SetFloat(string section, string name, float value) { SetString(section, name, value.ToString()); }
-
- public enum PrefType
- {
- STRING,
- BOOL,
- INT,
- FLOAT
- }
-
- public class PrefDesc
- {
- public string Value { get; set; }
- public string ValueEdited { get; set; }
- public PrefType Type { get; private set; }
- public bool Hidden { get; private set; }
- public String DisplayText { get; private set; }
-
- public PrefDesc(string value, PrefType type, bool hidden, string displayText)
- {
- Value = value;
- ValueEdited = value;
- Type = type;
- Hidden = hidden;
- DisplayText = displayText;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/MelonLoader.ModHandler/SceneHandler.cs b/MelonLoader.ModHandler/SceneHandler.cs
index 4702a8742..114e50936 100644
--- a/MelonLoader.ModHandler/SceneHandler.cs
+++ b/MelonLoader.ModHandler/SceneHandler.cs
@@ -14,7 +14,7 @@ public static void OnSceneLoad(int current_scene)
bool should_run = true;
if (Imports.IsIl2CppGame())
{
- if (Main.IsBoneworks)
+ if (MelonLoaderBase._IsBoneworks)
{
if (!Boneworks_HasGotLoadingSceneIndex)
{
@@ -55,8 +55,8 @@ public static void OnSceneLoad(int current_scene)
LastSceneIndex = current_scene;
if (!ShouldWait)
IsLoading = true;
- if (Main.IsBoneworks)
- Main.OnLevelIsLoading();
+ if (MelonLoaderBase._IsBoneworks)
+ MelonHandler.OnLevelIsLoading();
else
CheckForSceneFinishedLoading();
}
@@ -72,7 +72,7 @@ private static void CheckForSceneFinishedLoading()
{
if (IsLoading)
{
- Main.OnLevelWasLoaded(LastSceneIndex);
+ MelonHandler.OnLevelWasLoaded(LastSceneIndex);
HasFinishedLoading = true;
IsLoading = false;
}
@@ -82,7 +82,7 @@ private static void CheckForSceneInitialized()
{
if (HasFinishedLoading)
{
- Main.OnLevelWasInitialized(LastSceneIndex);
+ MelonHandler.OnLevelWasInitialized(LastSceneIndex);
HasFinishedLoading = false;
}
}
diff --git a/MelonLoader.ModHandler/SupportModule.cs b/MelonLoader.ModHandler/SupportModule.cs
index c26512472..90012a91a 100644
--- a/MelonLoader.ModHandler/SupportModule.cs
+++ b/MelonLoader.ModHandler/SupportModule.cs
@@ -61,14 +61,14 @@ internal static void Initialize()
}
else
{
- MelonModLogger.LogError("Unable to load Support Module! Support Module is Missing!");
- MelonModLogger.Log("------------------------------");
+ MelonLogger.LogError("Unable to load Support Module! Support Module is Missing!");
+ MelonLogger.Log("------------------------------");
}
}
catch (Exception e)
{
- MelonModLogger.LogError("Unable to load Support Module!\n" + e.ToString());
- MelonModLogger.Log("------------------------------");
+ MelonLogger.LogError("Unable to load Support Module!\n" + e.ToString());
+ MelonLogger.Log("------------------------------");
}
}
diff --git a/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs b/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs
index ea4ecd662..760cab23b 100644
--- a/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs
+++ b/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Il2Cpp")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("8eaaed02-b3c7-4380-9474-3dbe16394590")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.Support.Il2Cpp/Main.cs b/MelonLoader.Support.Il2Cpp/Main.cs
index 29be0eb40..00523de24 100644
--- a/MelonLoader.Support.Il2Cpp/Main.cs
+++ b/MelonLoader.Support.Il2Cpp/Main.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Reflection;
-using System.Runtime.InteropServices;
using UnhollowerBaseLib;
using UnhollowerBaseLib.Runtime;
using UnhollowerRuntimeLib;
@@ -24,42 +23,14 @@ internal static class Main
private static ISupportModule Initialize()
{
LogSupport.RemoveAllHandlers();
- if (Console.Enabled || Imports.IsDebugMode())
- LogSupport.InfoHandler += MelonModLogger.Log;
- LogSupport.WarningHandler += MelonModLogger.LogWarning;
- LogSupport.ErrorHandler += MelonModLogger.LogError;
+ if (MelonConsole.Enabled || Imports.IsDebugMode())
+ LogSupport.InfoHandler += MelonLogger.Log;
+ LogSupport.WarningHandler += MelonLogger.LogWarning;
+ LogSupport.ErrorHandler += MelonLogger.LogError;
if (Imports.IsDebugMode())
- LogSupport.TraceHandler += MelonModLogger.Log;
+ LogSupport.TraceHandler += MelonLogger.Log;
- try
- {
- Assembly il2cppSystem = Assembly.Load("Il2CppSystem");
- if (il2cppSystem != null)
- {
- Type unitytls = il2cppSystem.GetType("Il2CppMono.Unity.UnityTls");
- if (unitytls != null)
- {
- unsafe
- {
- var tlsHookTarget = typeof(Uri).Assembly.GetType("Mono.Unity.UnityTls").GetMethod("GetUnityTlsInterface", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).MethodHandle.GetFunctionPointer();
- var unityMethodField = UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(unitytls.GetMethod("GetUnityTlsInterface", BindingFlags.Public | BindingFlags.Static));
- var unityMethodPtr = (IntPtr)unityMethodField.GetValue(null);
- var unityMethod = *(IntPtr*)unityMethodPtr;
- Imports.Hook((IntPtr)(&tlsHookTarget), unityMethod);
- }
- }
- else
- throw new Exception("Failed to get Type Il2CppMono.Unity.UnityTls!");
- }
- else
- throw new Exception("Failed to get Assembly Il2CppSystem!");
- }
- catch (Exception ex)
- {
- MelonModLogger.LogWarning("Exception while setting up TLS, mods will not be able to use HTTPS: " + ex);
- }
-
- if (MelonLoader.Main.IsVRChat)
+ if (MelonLoaderBase.IsVRChat)
{
try
{
@@ -82,7 +53,7 @@ private static ISupportModule Initialize()
}
catch (Exception ex)
{
- MelonModLogger.LogWarning("Exception while setting up Auth Token Hider, Auth Tokens may show in Console: " + ex);
+ MelonLogger.LogWarning("Exception while setting up Auth Token Hider, Auth Tokens may show in Console: " + ex);
}
}
@@ -111,7 +82,7 @@ private static ISupportModule Initialize()
private static void GetUnityVersionNumbers(out int major, out int minor, out int patch)
{
- var unityVersionSplit = MelonLoader.Main.UnityVersion.Split('.');
+ var unityVersionSplit = MelonLoaderBase.UnityVersion.Split('.');
major = int.Parse(unityVersionSplit[0]);
minor = int.Parse(unityVersionSplit[1]);
var patchString = unityVersionSplit[2];
@@ -126,7 +97,7 @@ private static void GetUnityVersionNumbers(out int major, out int minor, out int
internal static SetAsLastSiblingDelegate SetAsLastSiblingDelegateField;
private static bool Il2CppSystem_Console_WriteLine_Patch() => false;
- private static void Transmtn_HttpConnection_get_Prefix() => harmonyInstance.Patch(Il2CppSystem_Console_WriteLine, new Harmony.HarmonyMethod(typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static)));
+ private static void Transmtn_HttpConnection_get_Prefix() => harmonyInstance.Patch(Il2CppSystem_Console_WriteLine, new HarmonyMethod(typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static)));
private static void Transmtn_HttpConnection_get_Postfix() => harmonyInstance.Unpatch(Il2CppSystem_Console_WriteLine, typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static));
}
@@ -134,9 +105,9 @@ public class MelonLoaderComponent : MonoBehaviour
{
internal static void Create()
{
- Main.obj = new GameObject("MelonLoader");
+ Main.obj = new GameObject();
DontDestroyOnLoad(Main.obj);
- Main.comp = Main.obj.AddComponent();
+ Main.comp = new MelonLoaderComponent(Main.obj.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of()).Pointer);
Main.SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(Main.obj.transform));
Main.SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(Main.comp.transform));
}
@@ -146,17 +117,17 @@ public MelonLoaderComponent(IntPtr intPtr) : base(intPtr) { }
void Update()
{
transform.SetAsLastSibling();
- MelonLoader.Main.OnUpdate();
+ MelonHandler.OnUpdate();
MelonCoroutines.Process();
}
void FixedUpdate()
{
- MelonLoader.Main.OnFixedUpdate();
+ MelonHandler.OnFixedUpdate();
MelonCoroutines.ProcessWaitForFixedUpdate();
}
- void LateUpdate() => MelonLoader.Main.OnLateUpdate();
- void OnGUI() => MelonLoader.Main.OnGUI();
+ void LateUpdate() => MelonHandler.OnLateUpdate();
+ void OnGUI() => MelonHandler.OnGUI();
void OnDestroy() { if (!Main.IsDestroying) Create(); }
- void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); }
+ void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); }
}
}
\ No newline at end of file
diff --git a/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs b/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs
index daa11bc5d..63024df8b 100644
--- a/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs
+++ b/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs
@@ -97,7 +97,7 @@ private static void ProcessNextOfCoroutine(IEnumerator enumerator)
}
catch (Exception e)
{
- MelonModLogger.LogError(e.ToString());
+ MelonLogger.LogError(e.ToString());
Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen
}
@@ -120,7 +120,7 @@ private static void ProcessNextOfCoroutine(IEnumerator enumerator)
if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction
next = new Il2CppEnumeratorWrapper(nextAsEnumerator);
else
- MelonModLogger.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
+ MelonLogger.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
break;
}
diff --git a/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs b/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs
index 8ed328b1d..7b6de44ad 100644
--- a/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs
+++ b/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono.Pre2017")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("19af2b6e-7217-4396-bb56-a5cffb37633d")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono.Pre2017.2/Main.cs b/MelonLoader.Support.Mono.Pre2017.2/Main.cs
index ba918a2f6..697764c7f 100644
--- a/MelonLoader.Support.Mono.Pre2017.2/Main.cs
+++ b/MelonLoader.Support.Mono.Pre2017.2/Main.cs
@@ -1,8 +1,6 @@
-using System;
-using System.Linq;
-using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
using UnityEngine;
-using Harmony;
namespace MelonLoader.Support
{
@@ -12,24 +10,30 @@ internal static class Main
internal static GameObject obj = null;
internal static MelonLoaderComponent comp = null;
internal static int CurrentScene = -9;
-
private static ISupportModule Initialize()
{
MelonLoaderComponent.Create();
return new Module();
}
}
+
public class MelonLoaderComponent : MonoBehaviour
{
+ internal static readonly List QueuedCoroutines = new List();
internal static void Create()
{
Main.obj = new GameObject("MelonLoader");
DontDestroyOnLoad(Main.obj);
- Main.comp = Main.obj.AddComponent();
+ Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent));
Main.obj.transform.SetAsLastSibling();
Main.comp.transform.SetAsLastSibling();
}
internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); }
+ void Awake()
+ {
+ foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine);
+ QueuedCoroutines.Clear();
+ }
void Start() => transform.SetAsLastSibling();
void Update()
{
@@ -39,12 +43,12 @@ void Update()
SceneHandler.OnSceneLoad(Application.loadedLevel);
Main.CurrentScene = Application.loadedLevel;
}
- MelonLoader.Main.OnUpdate();
+ MelonHandler.OnUpdate();
}
- void FixedUpdate() => MelonLoader.Main.OnFixedUpdate();
- void LateUpdate() => MelonLoader.Main.OnLateUpdate();
- void OnGUI() => MelonLoader.Main.OnGUI();
+ void FixedUpdate() => MelonHandler.OnFixedUpdate();
+ void LateUpdate() => MelonHandler.OnLateUpdate();
+ void OnGUI() => MelonHandler.OnGUI();
void OnDestroy() { if (!Main.IsDestroying) Create(); }
- void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); }
+ void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); }
}
}
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs b/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs
index 264a30caa..a12c35d0f 100644
--- a/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs
+++ b/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs
@@ -6,8 +6,21 @@ namespace MelonLoader.Support
public class Module : ISupportModule
{
public int GetActiveSceneIndex() => Main.CurrentScene;
- public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine);
- public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken);
+ public object StartCoroutine(IEnumerator coroutine)
+ {
+ if (Main.comp != null) return Main.comp.StartCoroutine(coroutine);
+
+ MelonLoaderComponent.QueuedCoroutines.Add(coroutine);
+ return coroutine;
+ }
+
+ public void StopCoroutine(object coroutineToken)
+ {
+ if (Main.comp == null)
+ MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator);
+ else
+ Main.comp.StopCoroutine(coroutineToken as Coroutine);
+ }
public void UnityDebugLog(string msg) => Debug.Log(msg);
public void Destroy() => MelonLoaderComponent.Destroy();
}
diff --git a/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs b/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs
index 8ed328b1d..2dac5064d 100644
--- a/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs
+++ b/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono.Pre2017")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("0c02945f-7ec8-4992-94d3-c21915f79eae")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono.Pre2017/Main.cs b/MelonLoader.Support.Mono.Pre2017/Main.cs
index d9e0afda9..53f900d4c 100644
--- a/MelonLoader.Support.Mono.Pre2017/Main.cs
+++ b/MelonLoader.Support.Mono.Pre2017/Main.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Linq;
-using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
-using Harmony;
namespace MelonLoader.Support
{
@@ -12,34 +10,41 @@ internal static class Main
internal static bool IsDestroying = false;
internal static GameObject obj = null;
internal static MelonLoaderComponent comp = null;
-
private static ISupportModule Initialize()
{
- MelonLoaderComponent.Create();
SceneManager.sceneLoaded += OnSceneLoad;
return new Module();
}
-
- private static void OnSceneLoad(Scene scene, LoadSceneMode mode) { if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); }
-
+ private static void OnSceneLoad(Scene scene, LoadSceneMode mode)
+ {
+ if (obj == null) MelonLoaderComponent.Create();
+ if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex);
+ }
}
+
public class MelonLoaderComponent : MonoBehaviour
{
+ internal static readonly List QueuedCoroutines = new List();
internal static void Create()
{
- Main.obj = new GameObject("MelonLoader");
+ Main.obj = new GameObject();
DontDestroyOnLoad(Main.obj);
- Main.comp = Main.obj.AddComponent();
+ Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent));
Main.obj.transform.SetAsLastSibling();
Main.comp.transform.SetAsLastSibling();
}
internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); }
+ void Awake()
+ {
+ foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine);
+ QueuedCoroutines.Clear();
+ }
void Start() => transform.SetAsLastSibling();
- void Update() { transform.SetAsLastSibling(); MelonLoader.Main.OnUpdate(); }
- void FixedUpdate() => MelonLoader.Main.OnFixedUpdate();
- void LateUpdate() => MelonLoader.Main.OnLateUpdate();
- void OnGUI() => MelonLoader.Main.OnGUI();
+ void Update() { transform.SetAsLastSibling(); MelonHandler.OnUpdate(); }
+ void FixedUpdate() => MelonHandler.OnFixedUpdate();
+ void LateUpdate() => MelonHandler.OnLateUpdate();
+ void OnGUI() => MelonHandler.OnGUI();
void OnDestroy() { if (!Main.IsDestroying) Create(); }
- void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); }
+ void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); }
}
}
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono.Pre2017/SupportModule.cs b/MelonLoader.Support.Mono.Pre2017/SupportModule.cs
index 75dd6be38..d6d752dcc 100644
--- a/MelonLoader.Support.Mono.Pre2017/SupportModule.cs
+++ b/MelonLoader.Support.Mono.Pre2017/SupportModule.cs
@@ -7,8 +7,22 @@ namespace MelonLoader.Support
public class Module : ISupportModule
{
public int GetActiveSceneIndex() => SceneManager.GetActiveScene().buildIndex;
- public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine);
- public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken);
+ public object StartCoroutine(IEnumerator coroutine)
+ {
+ if (Main.comp != null) return Main.comp.StartCoroutine(coroutine);
+
+ MelonLoaderComponent.QueuedCoroutines.Add(coroutine);
+ return coroutine;
+ }
+
+ public void StopCoroutine(object coroutineToken)
+ {
+ if (Main.comp == null)
+ MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator);
+ else
+ Main.comp.StopCoroutine(coroutineToken as Coroutine);
+ }
+
public void UnityDebugLog(string msg) => Debug.Log(msg);
public void Destroy() => MelonLoaderComponent.Destroy();
}
diff --git a/MelonLoader.Support.Mono/AssemblyInfo.cs b/MelonLoader.Support.Mono/AssemblyInfo.cs
index d61280ae9..2d5366faa 100644
--- a/MelonLoader.Support.Mono/AssemblyInfo.cs
+++ b/MelonLoader.Support.Mono/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)]
[assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("981297b8-ef03-41a8-ae14-1e0e4fcdee30")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono/Main.cs b/MelonLoader.Support.Mono/Main.cs
index 9eb5c9482..53f900d4c 100644
--- a/MelonLoader.Support.Mono/Main.cs
+++ b/MelonLoader.Support.Mono/Main.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Linq;
-using System.Reflection;
+using System.Collections;
+using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
-using Harmony;
namespace MelonLoader.Support
{
@@ -12,33 +10,41 @@ internal static class Main
internal static bool IsDestroying = false;
internal static GameObject obj = null;
internal static MelonLoaderComponent comp = null;
-
private static ISupportModule Initialize()
{
- MelonLoaderComponent.Create();
SceneManager.sceneLoaded += OnSceneLoad;
return new Module();
}
-
- private static void OnSceneLoad(Scene scene, LoadSceneMode mode) { if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); }
+ private static void OnSceneLoad(Scene scene, LoadSceneMode mode)
+ {
+ if (obj == null) MelonLoaderComponent.Create();
+ if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex);
+ }
}
+
public class MelonLoaderComponent : MonoBehaviour
{
+ internal static readonly List QueuedCoroutines = new List();
internal static void Create()
{
- Main.obj = new GameObject("MelonLoader");
+ Main.obj = new GameObject();
DontDestroyOnLoad(Main.obj);
- Main.comp = Main.obj.AddComponent();
+ Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent));
Main.obj.transform.SetAsLastSibling();
Main.comp.transform.SetAsLastSibling();
}
internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); }
+ void Awake()
+ {
+ foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine);
+ QueuedCoroutines.Clear();
+ }
void Start() => transform.SetAsLastSibling();
- void Update() { transform.SetAsLastSibling(); MelonLoader.Main.OnUpdate(); }
- void FixedUpdate() => MelonLoader.Main.OnFixedUpdate();
- void LateUpdate() => MelonLoader.Main.OnLateUpdate();
- void OnGUI() => MelonLoader.Main.OnGUI();
+ void Update() { transform.SetAsLastSibling(); MelonHandler.OnUpdate(); }
+ void FixedUpdate() => MelonHandler.OnFixedUpdate();
+ void LateUpdate() => MelonHandler.OnLateUpdate();
+ void OnGUI() => MelonHandler.OnGUI();
void OnDestroy() { if (!Main.IsDestroying) Create(); }
- void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); }
+ void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); }
}
}
\ No newline at end of file
diff --git a/MelonLoader.Support.Mono/SupportModule.cs b/MelonLoader.Support.Mono/SupportModule.cs
index 75dd6be38..d6d752dcc 100644
--- a/MelonLoader.Support.Mono/SupportModule.cs
+++ b/MelonLoader.Support.Mono/SupportModule.cs
@@ -7,8 +7,22 @@ namespace MelonLoader.Support
public class Module : ISupportModule
{
public int GetActiveSceneIndex() => SceneManager.GetActiveScene().buildIndex;
- public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine);
- public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken);
+ public object StartCoroutine(IEnumerator coroutine)
+ {
+ if (Main.comp != null) return Main.comp.StartCoroutine(coroutine);
+
+ MelonLoaderComponent.QueuedCoroutines.Add(coroutine);
+ return coroutine;
+ }
+
+ public void StopCoroutine(object coroutineToken)
+ {
+ if (Main.comp == null)
+ MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator);
+ else
+ Main.comp.StopCoroutine(coroutineToken as Coroutine);
+ }
+
public void UnityDebugLog(string msg) => Debug.Log(msg);
public void Destroy() => MelonLoaderComponent.Destroy();
}
diff --git a/MelonLoader/AssertionManager.cpp b/MelonLoader/AssertionManager.cpp
index 87a6b6a07..3db6bb526 100644
--- a/MelonLoader/AssertionManager.cpp
+++ b/MelonLoader/AssertionManager.cpp
@@ -17,16 +17,27 @@ void AssertionManager::ThrowError(std::string msg, const char* filepath)
{
if (!Result)
{
- Result = true;
msg += (" for [ " + std::string(FileName) + " | " + Position + " ]");
if (filepath != NULL)
msg += " in { " + std::string(filepath) + "}";
- Logger::LogError(msg);
+ ThrowInternalError(msg);
+ MelonLoader::UNLOAD(true);
+ }
+}
+
+void AssertionManager::ThrowInternalError(std::string msg)
+{
+ if (!Result)
+ {
+ Result = true;
+ Logger::LogTimestamp(ConsoleColor_Red);
+ Logger::LogFile << "[Error] " << msg << std::endl;
+ Console::Write("[MelonLoader] ", ConsoleColor_Red);
+ Console::WriteLine(("[Error] " + msg), ConsoleColor_Red);
if (MelonLoader::DebugMode)
MessageBox(NULL, msg.c_str(), "MelonLoader - INTERNAL FAILURE", MB_OK | MB_ICONERROR);
else
MessageBox(NULL, "Please Post your Latest Log File\non #internal-failure in the MelonLoader Discord!", "MelonLoader - INTERNAL FAILURE!", MB_OK | MB_ICONERROR);
- MelonLoader::UNLOAD();
}
}
diff --git a/MelonLoader/AssertionManager.h b/MelonLoader/AssertionManager.h
index cc5c191aa..6e028ed26 100644
--- a/MelonLoader/AssertionManager.h
+++ b/MelonLoader/AssertionManager.h
@@ -6,16 +6,15 @@
class AssertionManager
{
public:
+ static const char* FileName;
+ static const char* Position;
static bool Result;
static void Start(const char* filename, const char* position);
static void ThrowError(std::string msg, const char* filepath = NULL);
+ static void ThrowInternalError(std::string msg);
static void Decide(void* thing, const char* name);
static HMODULE LoadLib(const char* name, const char* filepath);
static HMODULE GetModuleHandlePtr(const char* name);
static FARPROC GetExport(HMODULE mod, const char* export_name);
-
-private:
- static const char* FileName;
- static const char* Position;
};
\ No newline at end of file
diff --git a/MelonLoader/Console.cpp b/MelonLoader/Console.cpp
index 2d63d7eb5..dae71b46e 100644
--- a/MelonLoader/Console.cpp
+++ b/MelonLoader/Console.cpp
@@ -9,6 +9,7 @@ bool Console::HordiniMode = false;
bool Console::HordiniMode_Random = false;
bool Console::ChromiumMode = false;
bool Console::ShouldShowGameLogs = false;
+bool Console::AlwaysOnTop = false;
void Console::Create()
{
@@ -19,6 +20,7 @@ void Console::Create()
hwndConsole = GetConsoleWindow();
SetTitle(("MelonLoader " + (MelonLoader::DebugMode ? std::string("Debug") : std::string("Normal")) + " Console").c_str());
SetForegroundWindow(hwndConsole);
+ AlwaysOnTopCheck();
freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout);
}
else
@@ -50,12 +52,22 @@ void Console::ChromiumCheck()
SetColor(ConsoleColor_Magenta);
}
+void Console::AlwaysOnTopCheck()
+{
+ if (IsInitialized() && AlwaysOnTop)
+ {
+ SetWindowPos(hwndConsole, HWND_TOPMOST, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+ ShowWindow(hwndConsole, SW_NORMAL);
+ }
+}
+
void Console::Write(const char* txt)
{
if (IsInitialized())
{
ChromiumCheck();
RainbowCheck();
+ AlwaysOnTopCheck();
std::cout << txt;
ResetColor();
}
diff --git a/MelonLoader/Console.h b/MelonLoader/Console.h
index 4a33c5b04..40e63a5dc 100644
--- a/MelonLoader/Console.h
+++ b/MelonLoader/Console.h
@@ -82,6 +82,7 @@ class Console
static bool HordiniMode_Random;
static bool ChromiumMode;
static bool ShouldShowGameLogs;
+ static bool AlwaysOnTop;
static bool IsInitialized() { return (hwndConsole != NULL); }
static void Create();
@@ -91,6 +92,7 @@ class Console
static void ResetColor() { SetColor(ConsoleColor_Gray); }
static void RainbowCheck();
static void ChromiumCheck();
+ static void AlwaysOnTopCheck();
static void Write(const char* txt);
static void Write(const char* txt, ConsoleColor color);
diff --git a/MelonLoader/DisableAnalytics.cpp b/MelonLoader/DisableAnalytics.cpp
index 7785ab41e..4eadb7159 100644
--- a/MelonLoader/DisableAnalytics.cpp
+++ b/MelonLoader/DisableAnalytics.cpp
@@ -1,7 +1,6 @@
#include "DisableAnalytics.h"
#include "HookManager.h"
#include "Logger.h"
-#include "AssertionManager.h"
gethostbyname_t DisableAnalytics::Original_gethostbyname = NULL;
getaddrinfo_t DisableAnalytics::Original_getaddrinfo = NULL;
@@ -27,27 +26,31 @@ std::list DisableAnalytics::URL_Blacklist = {
"crashlytics.com"
};
-bool DisableAnalytics::Setup()
+void DisableAnalytics::Setup()
{
- AssertionManager::Start("DisableAnalytics.cpp", "DisableAnalytics::Setup");
-
HMODULE wsock32 = GetModuleHandle("wsock32.dll");
if (wsock32 != NULL)
{
- Original_gethostbyname = (gethostbyname_t)AssertionManager::GetExport(wsock32, "gethostbyname");
+ Original_gethostbyname = (gethostbyname_t)GetProcAddress(wsock32, "gethostbyname");
if (Original_gethostbyname != NULL)
HookManager::Hook(&(LPVOID&)Original_gethostbyname, Hooked_gethostbyname);
+ else
+ Logger::DebugLogWarning("Failed to GetProcAddress ( gethostbyname ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]");
}
+ else
+ Logger::DebugLogWarning("Failed to GetModuleHandle ( wsock32.dll ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]");
- HMODULE ws2_32 = AssertionManager::GetModuleHandlePtr("ws2_32");
+ HMODULE ws2_32 = GetModuleHandle("ws2_32");
if (ws2_32 != NULL)
{
- Original_getaddrinfo = (getaddrinfo_t)AssertionManager::GetExport(ws2_32, "getaddrinfo");
+ Original_getaddrinfo = (getaddrinfo_t)GetProcAddress(ws2_32, "getaddrinfo");
if (Original_getaddrinfo != NULL)
HookManager::Hook(&(LPVOID&)Original_getaddrinfo, Hooked_getaddrinfo);
+ else
+ Logger::DebugLogWarning("Failed to GetProcAddress ( getaddrinfo ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]");
}
-
- return !AssertionManager::Result;
+ else
+ Logger::DebugLogWarning("Failed to GetModuleHandle ( ws2_32 ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]");
}
bool DisableAnalytics::CheckBlacklist(std::string url)
@@ -56,4 +59,18 @@ bool DisableAnalytics::CheckBlacklist(std::string url)
if (url_found)
Logger::DebugLog("Analytics URL Blocked: " + url);
return url_found;
-}
\ No newline at end of file
+}
+
+void* DisableAnalytics::Hooked_gethostbyname(const char* name)
+{
+ if (CheckBlacklist(name))
+ return NULL;
+ return Original_gethostbyname(name);
+}
+
+int DisableAnalytics::Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult)
+{
+ if (CheckBlacklist(pNodeName))
+ return WSAHOST_NOT_FOUND;
+ return Original_getaddrinfo(pNodeName, pServiceName, pHints, ppResult);
+}
diff --git a/MelonLoader/DisableAnalytics.h b/MelonLoader/DisableAnalytics.h
index 1d1acf74f..ad057f487 100644
--- a/MelonLoader/DisableAnalytics.h
+++ b/MelonLoader/DisableAnalytics.h
@@ -13,8 +13,8 @@ class DisableAnalytics
static getaddrinfo_t Original_getaddrinfo;
static std::listURL_Blacklist;
- static bool Setup();
+ static void Setup();
static bool CheckBlacklist(std::string url);
- static void* Hooked_gethostbyname(const char* name) { if (CheckBlacklist(name)) return NULL; return Original_gethostbyname(name); }
- static int Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult) { if (CheckBlacklist(pNodeName)) return WSAEHOSTDOWN; return Original_getaddrinfo(pNodeName, pServiceName, pHints, ppResult); }
+ static void* Hooked_gethostbyname(const char* name);
+ static int Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult);
};
\ No newline at end of file
diff --git a/MelonLoader/DllMain.cpp b/MelonLoader/DllMain.cpp
index f30ecb608..f34fe7552 100644
--- a/MelonLoader/DllMain.cpp
+++ b/MelonLoader/DllMain.cpp
@@ -1,6 +1,6 @@
#include
#include "MelonLoader.h"
-#include "ModHandler.h"
+#include "MelonLoader_Base.h"
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
diff --git a/MelonLoader/Exports.cpp b/MelonLoader/Exports.cpp
index 36524c0e0..b91173288 100644
--- a/MelonLoader/Exports.cpp
+++ b/MelonLoader/Exports.cpp
@@ -4,13 +4,14 @@
#include "Mono.h"
#include "HookManager.h"
#include "Logger.h"
+#include "AssertionManager.h"
-void Log(MonoString* txt) { Logger::Log(Mono::mono_string_to_utf8(txt)); }
-void LogColor(MonoString* txt, ConsoleColor color) { Logger::Log(Mono::mono_string_to_utf8(txt), color); }
+void Log(MonoString* namesection, MonoString* txt) { Logger::Log(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); }
+void LogColor(MonoString* namesection, MonoString* txt, ConsoleColor color) { Logger::Log(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt), color); }
void LogWarning(MonoString* namesection, MonoString* txt) { Logger::LogWarning(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); }
void LogError(MonoString* namesection, MonoString* txt) { Logger::LogError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); }
-void LogDLLError(MonoString* namesection, MonoString* msg) { Logger::LogDLLError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(msg)); }
-void LogDLLStatus(ModHandler_DLLStatus type) { Logger::LogDLLStatus(type); }
+void LogMelonError(MonoString* namesection, MonoString* txt) { Logger::LogMelonError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); }
+void LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp) { Logger::LogMelonCompatibility(comp); }
bool IsIl2CppGame() { return MelonLoader::IsGameIl2Cpp; }
bool IsDebugMode() { return MelonLoader::DebugMode; }
bool IsConsoleEnabled() { return Console::Enabled; }
@@ -26,43 +27,47 @@ MonoString* GetAssemblyDirectory() { return Mono::mono_string_new(Mono::Domain,
MonoString* GetMonoConfigDirectory() { return Mono::mono_string_new(Mono::Domain, Mono::ConfigPath); }
MonoString* GetExePath() { return Mono::mono_string_new(Mono::Domain, MelonLoader::ExePath); }
bool IsQuitFix() { return MelonLoader::QuitFix; }
-bool IsDevModsOnly() { return MelonLoader::DevModsOnly; }
-bool IsDevPluginsOnly() { return MelonLoader::DevPluginsOnly; }
+MelonLoader::LoadMode GetLoadMode_Plugins() { return MelonLoader::LoadMode_Plugins; }
+MelonLoader::LoadMode GetLoadMode_Mods() { return MelonLoader::LoadMode_Mods; }
bool AG_Force_Regenerate() { return MelonLoader::AG_Force_Regenerate; }
MonoString* AG_Force_Version_Unhollower() { if (MelonLoader::ForceUnhollowerVersion != NULL) return Mono::mono_string_new(Mono::Domain, MelonLoader::ForceUnhollowerVersion); return NULL; }
void SetTitleForConsole(MonoString* txt) { Console::SetTitle(Mono::mono_string_to_utf8(txt)); }
+void ThrowInternalError(MonoString* txt) { AssertionManager::ThrowInternalError(Mono::mono_string_to_utf8(txt)); }
void Exports::AddInternalCalls()
{
- Mono::mono_add_internal_call("MelonLoader.Imports::UNLOAD_MELONLOADER", MelonLoader::UNLOAD);
Mono::mono_add_internal_call("MelonLoader.Imports::IsIl2CppGame", IsIl2CppGame);
Mono::mono_add_internal_call("MelonLoader.Imports::IsDebugMode", IsDebugMode);
- Mono::mono_add_internal_call("MelonLoader.Imports::IsConsoleEnabled", IsConsoleEnabled);
Mono::mono_add_internal_call("MelonLoader.Imports::GetGameDirectory", GetGameDirectory);
Mono::mono_add_internal_call("MelonLoader.Imports::GetGameDataDirectory", GetGameDataDirectory);
Mono::mono_add_internal_call("MelonLoader.Imports::GetAssemblyDirectory", GetAssemblyDirectory);
Mono::mono_add_internal_call("MelonLoader.Imports::GetMonoConfigDirectory", GetMonoConfigDirectory);
Mono::mono_add_internal_call("MelonLoader.Imports::Hook", Hook);
Mono::mono_add_internal_call("MelonLoader.Imports::Unhook", Unhook);
- Mono::mono_add_internal_call("MelonLoader.Imports::IsOldMono", IsOldMono);
Mono::mono_add_internal_call("MelonLoader.Imports::GetCompanyName", GetCompanyName);
Mono::mono_add_internal_call("MelonLoader.Imports::GetProductName", GetProductName);
Mono::mono_add_internal_call("MelonLoader.Imports::GetExePath", GetExePath);
- Mono::mono_add_internal_call("MelonLoader.Imports::IsQuitFix", IsQuitFix);
- Mono::mono_add_internal_call("MelonLoader.Imports::IsDevModsOnly", IsDevModsOnly);
- Mono::mono_add_internal_call("MelonLoader.Imports::IsDevPluginsOnly", IsDevPluginsOnly);
- Mono::mono_add_internal_call("MelonLoader.Imports::AG_Force_Regenerate", AG_Force_Regenerate);
- Mono::mono_add_internal_call("MelonLoader.Imports::AG_Force_Version_Unhollower", AG_Force_Version_Unhollower);
- Mono::mono_add_internal_call("MelonLoader.Console::Allocate", Console::Create);
- Mono::mono_add_internal_call("MelonLoader.Console::SetTitle", SetTitleForConsole);
- Mono::mono_add_internal_call("MelonLoader.Console::SetColor", Console::SetColor);
- //Mono::mono_add_internal_call("MelonLoader.Console::ShouldShowGameLogs", ShouldShowGameLogs);
+ Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::IsOldMono", IsOldMono);
+ Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::IsQuitFix", IsQuitFix);
+ Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::UNLOAD", MelonLoader::UNLOAD);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_Log", Log);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogColor", LogColor);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogWarning", LogWarning);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogError", LogError);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogDLLError", LogDLLError);
- Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogDLLStatus", LogDLLStatus);
+ Mono::mono_add_internal_call("MelonLoader.MelonHandler::GetLoadMode_Plugins", GetLoadMode_Plugins);
+ Mono::mono_add_internal_call("MelonLoader.MelonHandler::GetLoadMode_Mods", GetLoadMode_Mods);
+
+ Mono::mono_add_internal_call("MelonLoader.MelonConsole::Allocate", Console::Create);
+ Mono::mono_add_internal_call("MelonLoader.MelonConsole::SetTitle", SetTitleForConsole);
+ Mono::mono_add_internal_call("MelonLoader.MelonConsole::SetColor", Console::SetColor);
+ Mono::mono_add_internal_call("MelonLoader.MelonConsole::IsConsoleEnabled", IsConsoleEnabled);
+
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_Log", Log);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogColor", LogColor);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogWarning", LogWarning);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogError", LogError);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogMelonError", LogMelonError);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogMelonCompatibility", LogMelonCompatibility);
+ Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_ThrowInternalError", ThrowInternalError);
+
+ Mono::mono_add_internal_call("MelonLoader.AssemblyGenerator::Force_Regenerate", AG_Force_Regenerate);
+ Mono::mono_add_internal_call("MelonLoader.AssemblyGenerator::Force_Version_Unhollower", AG_Force_Version_Unhollower);
}
\ No newline at end of file
diff --git a/MelonLoader/HookManager.cpp b/MelonLoader/HookManager.cpp
index 24162f660..72f01bf40 100644
--- a/MelonLoader/HookManager.cpp
+++ b/MelonLoader/HookManager.cpp
@@ -1,12 +1,13 @@
#include
#include "HookManager.h"
-#include "ModHandler.h"
+#include "MelonLoader_Base.h"
#include "MelonLoader.h"
#include "Console.h"
#include "Detours/detours.h"
#include "AssertionManager.h"
#include "Logger.h"
#include "Exports.h"
+#include "DisableAnalytics.h"
#include
#pragma region Core
@@ -126,8 +127,8 @@ HMODULE __stdcall HookManager::Hooked_LoadLibraryW(LPCWSTR lpLibFileName)
{
if (Il2Cpp::Setup(lib))
{
- Mono::CreateDomain();
Hook(&(LPVOID&)Il2Cpp::il2cpp_init, Hooked_il2cpp_init);
+ Hook(&(LPVOID&)Il2Cpp::il2cpp_unity_install_unitytls_interface, Hooked_il2cpp_unity_install_unitytls_interface);
}
LoadLibraryW_Unhook();
}
@@ -147,11 +148,23 @@ HMODULE __stdcall HookManager::Hooked_LoadLibraryW(LPCWSTR lpLibFileName)
}
#pragma endregion
+#pragma region il2cpp_unity_install_unitytls_interface
+void HookManager::Hooked_il2cpp_unity_install_unitytls_interface(const void* unitytlsInterfaceStruct)
+{
+ Il2Cpp::il2cpp_unity_install_unitytls_interface(unitytlsInterfaceStruct);
+ Mono::mono_unity_install_unitytls_interface(unitytlsInterfaceStruct);
+}
+#pragma endregion
+
#pragma region il2cpp_init
Il2CppDomain* HookManager::Hooked_il2cpp_init(const char* name)
{
- Exports::AddInternalCalls();
- ModHandler::Initialize();
+ if (Mono::Load() && Mono::Setup())
+ {
+ Mono::CreateDomain();
+ Exports::AddInternalCalls();
+ MelonLoader_Base::Initialize();
+ }
Il2Cpp::Domain = Il2Cpp::il2cpp_init(name);
Unhook(&(LPVOID&)Il2Cpp::il2cpp_init, Hooked_il2cpp_init);
return Il2Cpp::Domain;
@@ -165,7 +178,7 @@ MonoDomain* HookManager::Hooked_mono_jit_init_version(const char* name, const ch
Mono::Domain = Mono::mono_jit_init_version(name, version);
Mono::FixDomainBaseDir();
Exports::AddInternalCalls();
- ModHandler::Initialize();
+ MelonLoader_Base::Initialize();
return Mono::Domain;
}
#pragma endregion
@@ -184,7 +197,7 @@ void* HookManager::Hooked_runtime_invoke(const void* method, void* obj, void** p
Unhook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, Hooked_runtime_invoke);
else
Unhook(&(LPVOID&)Mono::mono_runtime_invoke, Hooked_runtime_invoke);
- ModHandler::OnApplicationStart();
+ MelonLoader_Base::Startup();
}
if (MelonLoader::IsGameIl2Cpp)
return Il2Cpp::il2cpp_runtime_invoke((Il2CppMethod*)method, obj, params, (Il2CppObject**)exc);
diff --git a/MelonLoader/HookManager.h b/MelonLoader/HookManager.h
index 8a7b5d7c2..bbaa3a9d0 100644
--- a/MelonLoader/HookManager.h
+++ b/MelonLoader/HookManager.h
@@ -37,4 +37,5 @@ class HookManager
static Il2CppDomain* Hooked_il2cpp_init(const char* name);
static MonoDomain* Hooked_mono_jit_init_version(const char* name, const char* version);
static void* Hooked_runtime_invoke(const void* method, void* obj, void** params, void** exc);
+ static void Hooked_il2cpp_unity_install_unitytls_interface(const void* unitytlsInterfaceStruct);
};
\ No newline at end of file
diff --git a/MelonLoader/IL2CPP.cpp b/MelonLoader/IL2CPP.cpp
index b09f6289d..963a24559 100644
--- a/MelonLoader/IL2CPP.cpp
+++ b/MelonLoader/IL2CPP.cpp
@@ -7,6 +7,7 @@ Il2CppDomain* Il2Cpp::Domain = NULL;
il2cpp_init_t Il2Cpp::il2cpp_init = NULL;
il2cpp_runtime_invoke_t Il2Cpp::il2cpp_runtime_invoke = NULL;
il2cpp_method_get_name_t Il2Cpp::il2cpp_method_get_name = NULL;
+il2cpp_unity_install_unitytls_interface_t Il2Cpp::il2cpp_unity_install_unitytls_interface = NULL;
bool Il2Cpp::Setup(HMODULE mod)
{
@@ -15,6 +16,7 @@ bool Il2Cpp::Setup(HMODULE mod)
il2cpp_init = (il2cpp_init_t)AssertionManager::GetExport(mod, "il2cpp_init");
il2cpp_runtime_invoke = (il2cpp_runtime_invoke_t)AssertionManager::GetExport(mod, "il2cpp_runtime_invoke");
il2cpp_method_get_name = (il2cpp_method_get_name_t)AssertionManager::GetExport(mod, "il2cpp_method_get_name");
+ il2cpp_unity_install_unitytls_interface = (il2cpp_unity_install_unitytls_interface_t)AssertionManager::GetExport(mod, "il2cpp_unity_install_unitytls_interface");
return !AssertionManager::Result;
}
\ No newline at end of file
diff --git a/MelonLoader/IL2CPP.h b/MelonLoader/IL2CPP.h
index 54c597756..3b4ed07bd 100644
--- a/MelonLoader/IL2CPP.h
+++ b/MelonLoader/IL2CPP.h
@@ -11,6 +11,7 @@ struct Il2CppObject;
typedef Il2CppDomain* (*il2cpp_init_t) (const char* name);
typedef Il2CppObject* (*il2cpp_runtime_invoke_t) (Il2CppMethod* method, void* obj, void** params, Il2CppObject** exc);
typedef const char* (*il2cpp_method_get_name_t) (Il2CppMethod* method);
+typedef void (*il2cpp_unity_install_unitytls_interface_t)(const void* unitytlsInterfaceStruct);
class Il2Cpp
{
@@ -19,6 +20,7 @@ class Il2Cpp
static il2cpp_init_t il2cpp_init;
static il2cpp_runtime_invoke_t il2cpp_runtime_invoke;
static il2cpp_method_get_name_t il2cpp_method_get_name;
+ static il2cpp_unity_install_unitytls_interface_t il2cpp_unity_install_unitytls_interface;
static bool Setup(HMODULE mod);
};
\ No newline at end of file
diff --git a/MelonLoader/Logger.cpp b/MelonLoader/Logger.cpp
index cde310650..b513b5746 100644
--- a/MelonLoader/Logger.cpp
+++ b/MelonLoader/Logger.cpp
@@ -24,13 +24,13 @@ void Logger::Initialize(std::string filepathstr)
MaxWarnings = 0;
MaxErrors = 0;
}
- auto now = std::chrono::system_clock::now();
- auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
- auto timer = std::chrono::system_clock::to_time_t(now);
+ std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
+ time_t timer = std::chrono::system_clock::to_time_t(now);
std::tm bt;
localtime_s(&bt, &timer);
std::string logFolderPath = filepathstr + "\\Logs";
- if (!MelonLoader::DirectoryExists(logFolderPath.c_str()))
+ if (!DirectoryExists(logFolderPath.c_str()))
int returnval = _mkdir(logFolderPath.c_str());
else
CleanOldLogs(logFolderPath);
@@ -64,9 +64,9 @@ void Logger::CleanOldLogs(std::string logFolderPath)
void Logger::LogTimestamp(ConsoleColor color)
{
- auto now = std::chrono::system_clock::now();
- auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
- auto timer = std::chrono::system_clock::to_time_t(now);
+ std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
+ time_t timer = std::chrono::system_clock::to_time_t(now);
std::tm bt;
localtime_s(&bt, &timer);
std::stringstream output;
@@ -85,7 +85,6 @@ void Logger::Log(const char* txt)
Console::Write("MelonLoader", ConsoleColor_Magenta);
Console::Write("] ");
Console::WriteLine(txt);
- ModHandler::RunLogCallbacks(txt);
}
void Logger::Log(const char* txt, ConsoleColor color)
@@ -96,7 +95,26 @@ void Logger::Log(const char* txt, ConsoleColor color)
Console::Write("MelonLoader", ConsoleColor_Magenta);
Console::Write("] ");
Console::WriteLine(txt, color);
- ModHandler::RunLogCallbacks(txt);
+}
+
+void Logger::Log(const char* namesection, const char* txt)
+{
+ LogTimestamp();
+ LogFile << namesection << txt << std::endl;
+ Console::Write("[");
+ Console::Write("MelonLoader", ConsoleColor_Magenta);
+ Console::Write("] ");
+ Console::WriteLine(std::string(namesection) + txt);
+}
+
+void Logger::Log(const char* namesection, const char* txt, ConsoleColor color)
+{
+ LogTimestamp(color);
+ LogFile << namesection << txt << std::endl;
+ Console::Write("[");
+ Console::Write("MelonLoader", ConsoleColor_Magenta);
+ Console::Write("] ");
+ Console::WriteLine(std::string(namesection) + txt, color);
}
void Logger::LogWarning(const char* txt)
@@ -109,7 +127,6 @@ void Logger::LogWarning(const char* txt)
{
Console::Write("[MelonLoader] ", ConsoleColor_Yellow);
Console::WriteLine(("[Warning] " + std::string(txt)), ConsoleColor_Yellow);
- ModHandler::RunWarningCallbacks(txt);
}
if (MaxWarnings > 0)
WarningCount++;
@@ -126,7 +143,6 @@ void Logger::LogWarning(const char* namesection, const char* txt)
{
Console::Write("[MelonLoader] ", ConsoleColor_Yellow);
Console::WriteLine((std::string(namesection) + "[Warning] " + std::string(txt)), ConsoleColor_Yellow);
- ModHandler::RunWarningCallbacks(namesection, txt);
}
if (MaxWarnings > 0)
WarningCount++;
@@ -141,7 +157,6 @@ void Logger::LogError(const char* txt)
LogFile << "[Error] " << txt << std::endl;
Console::Write("[MelonLoader] ", ConsoleColor_Red);
Console::WriteLine(("[Error] " + std::string(txt)), ConsoleColor_Red);
- ModHandler::RunErrorCallbacks(txt);
if (MaxErrors > 0)
ErrorCount++;
}
@@ -155,40 +170,49 @@ void Logger::LogError(const char* namesection, const char* txt)
LogFile << namesection << "[Error] " << txt << std::endl;
Console::Write("[MelonLoader] ", ConsoleColor_Red);
Console::WriteLine((std::string(namesection) + "[Error] " + std::string(txt)), ConsoleColor_Red);
- ModHandler::RunErrorCallbacks(namesection, txt);
if (MaxErrors > 0)
ErrorCount++;
}
}
-void Logger::LogDLLError(const char* namesection, const char* msg)
+void Logger::LogMelonError(const char* namesection, const char* txt)
{
if ((MaxErrors <= 0) || (ErrorCount < MaxErrors))
{
LogTimestamp(ConsoleColor_Yellow);
- LogFile << namesection << "[Error] " << msg << std::endl;
+ LogFile << namesection << "[Error] " << txt << std::endl;
Console::Write("[MelonLoader] ", ConsoleColor_Yellow);
- Console::WriteLine((std::string(namesection) + "[Error] " + std::string(msg)), ConsoleColor_Yellow);
- ModHandler::RunErrorCallbacks(namesection, msg);
+ Console::WriteLine((std::string(namesection) + "[Error] " + std::string(txt)), ConsoleColor_Yellow);
if (MaxErrors > 0)
ErrorCount++;
}
}
-void Logger::LogDLLStatus(ModHandler_DLLStatus type)
+void Logger::LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp)
{
LogTimestamp();
- LogFile << "Game Compatibility: " << ((type == ModHandler_DLLStatus_UNIVERSAL) ? "Universal" : ((type == ModHandler_DLLStatus_COMPATIBLE) ? "Compatible" : ((type == ModHandler_DLLStatus_NOATTRIBUTE) ? "No MelonModGameAttribute!" : "INCOMPATIBLE!"))) << std::endl;
Console::Write("[");
Console::Write("MelonLoader", ConsoleColor_Magenta);
Console::Write("] ");
+ LogFile << "Game Compatibility: ";
Console::Write("Game Compatibility: ", ConsoleColor_Blue);
- if (type == ModHandler_DLLStatus_UNIVERSAL)
+ switch (comp)
+ {
+ case MelonLoader_Base::MelonCompatibility::UNIVERSAL:
+ LogFile << "Universal";
Console::WriteLine("Universal", ConsoleColor_Cyan);
- else if (type == ModHandler_DLLStatus_COMPATIBLE)
+ break;
+ case MelonLoader_Base::MelonCompatibility::COMPATIBLE:
+ LogFile << "Compatible";
Console::WriteLine("Compatible", ConsoleColor_Green);
- else if (type == ModHandler_DLLStatus_NOATTRIBUTE)
- Console::WriteLine("No MelonModGameAttribute!", ConsoleColor_Yellow);
- else
+ break;
+ case MelonLoader_Base::MelonCompatibility::NOATTRIBUTE:
+ LogFile << "No MelonGameAttribute!";
+ Console::WriteLine("No MelonGameAttribute!", ConsoleColor_Yellow);
+ break;
+ default:
+ LogFile << "INCOMPATIBLE!";
Console::WriteLine("INCOMPATIBLE!", ConsoleColor_Red);
-}
+ };
+ LogFile << std::endl;
+}
\ No newline at end of file
diff --git a/MelonLoader/Logger.h b/MelonLoader/Logger.h
index dbe79cb25..379a0deb3 100644
--- a/MelonLoader/Logger.h
+++ b/MelonLoader/Logger.h
@@ -4,7 +4,7 @@
#include
#include
#include "Console.h"
-#include "ModHandler.h"
+#include "MelonLoader_Base.h"
class LogStream
{
@@ -43,6 +43,8 @@ class Logger
static void Log(const char* txt);
static void Log(const char* txt, ConsoleColor color);
+ static void Log(const char* namesection, const char* txt);
+ static void Log(const char* namesection, const char* txt, ConsoleColor color);
static void Log(std::string txt) { Log(txt.c_str()); };
static void Log(std::string txt, ConsoleColor color) { Log(txt.c_str(), color); }
@@ -53,21 +55,25 @@ class Logger
static void LogError(const char* txt);
static void LogError(const char* namesection, const char* txt);
static void LogError(std::string txt) { LogError(txt.c_str()); }
-
+
static void DebugLog(const char* txt) { if (MelonLoader::DebugMode) Log(txt); }
static void DebugLog(const char* txt, ConsoleColor color) { if (MelonLoader::DebugMode) Log(txt, color); };
static void DebugLog(std::string txt) { DebugLog(txt.c_str()); }
static void DebugLog(std::string txt, ConsoleColor color) { DebugLog(txt.c_str(), color); }
+ static void DebugLogWarning(const char* txt) { if (MelonLoader::DebugMode) LogWarning(txt); }
+ static void DebugLogWarning(const char* namesection, const char* txt) { if (MelonLoader::DebugMode) LogWarning(namesection, txt); }
+ static void DebugLogWarning(std::string txt) { DebugLogWarning(txt.c_str()); }
+
static void DebugLogError(const char* txt) { if (MelonLoader::DebugMode) LogError(txt); }
static void DebugLogError(const char* namesection, const char* txt) { if (MelonLoader::DebugMode) LogError(namesection, txt); }
static void DebugLogError(std::string txt) { DebugLogError(txt.c_str()); }
- static void LogDLLError(const char* namesection, const char* msg);
-
- static void LogDLLStatus(ModHandler_DLLStatus type);
+ static void LogMelonError(const char* namesection, const char* txt);
+ static void LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp);
+ static void LogTimestamp(ConsoleColor color = ConsoleColor_Black);
private:
- static void LogTimestamp(ConsoleColor color = ConsoleColor_Black);
static bool CompareWritetime(const std::filesystem::directory_entry& first, const std::filesystem::directory_entry& second) { return first.last_write_time().time_since_epoch() >= second.last_write_time().time_since_epoch(); }
+ static bool DirectoryExists(const char* path) { struct stat info; if (stat(path, &info) != NULL) return false; if (info.st_mode & S_IFDIR) return true; return false; }
};
\ No newline at end of file
diff --git a/MelonLoader/MelonLoader.cpp b/MelonLoader/MelonLoader.cpp
index 1ac6c2b55..c30683e71 100644
--- a/MelonLoader/MelonLoader.cpp
+++ b/MelonLoader/MelonLoader.cpp
@@ -8,7 +8,7 @@
#include "Mono.h"
#include "HookManager.h"
#include "Logger.h"
-#include "ModHandler.h"
+#include "MelonLoader_Base.h"
#include
#include "DisableAnalytics.h"
#pragma warning(disable:4996)
@@ -19,8 +19,6 @@ char* MelonLoader::CommandLineV[64];
bool MelonLoader::IsGameIl2Cpp = false;
bool MelonLoader::DebugMode = false;
bool MelonLoader::QuitFix = false;
-bool MelonLoader::DevModsOnly = false;
-bool MelonLoader::DevPluginsOnly = false;
bool MelonLoader::AG_Force_Regenerate = false;
char* MelonLoader::ExePath = NULL;;
char* MelonLoader::GamePath = NULL;
@@ -29,6 +27,8 @@ char* MelonLoader::CompanyName = NULL;
char* MelonLoader::ProductName = NULL;
char* MelonLoader::ForceUnhollowerVersion = NULL;
char* MelonLoader::ForceUnityVersion = NULL;
+MelonLoader::LoadMode MelonLoader::LoadMode_Plugins = MelonLoader::LoadMode::NORMAL;
+MelonLoader::LoadMode MelonLoader::LoadMode_Mods = MelonLoader::LoadMode::NORMAL;
void MelonLoader::Main()
{
@@ -60,7 +60,6 @@ void MelonLoader::Main()
#ifdef DEBUG
DebugMode = true;
- Console::ShouldShowGameLogs = true;
Console::Create();
#endif
@@ -134,16 +133,8 @@ void MelonLoader::Main()
ReadAppInfo();
- if (DisableAnalytics::Setup())
- {
- if (IsGameIl2Cpp)
- {
- if (Mono::Load() && Mono::Setup())
- HookManager::LoadLibraryW_Hook();
- }
- else
- HookManager::LoadLibraryW_Hook();
- }
+ DisableAnalytics::Setup();
+ HookManager::LoadLibraryW_Hook();
}
}
}
@@ -173,10 +164,26 @@ void MelonLoader::ParseCommandLine()
Console::HordiniMode = true;
else if (strstr(command, "--melonloader.randomrainbow") != NULL)
Console::HordiniMode_Random = true;
- else if (strstr(command, "--melonloader.devmodsonly") != NULL)
- DevModsOnly = true;
- else if (strstr(command, "--melonloader.devpluginsonly") != NULL)
- DevPluginsOnly = true;
+ else if (strstr(command, "--melonloader.consoleontop") != NULL)
+ Console::AlwaysOnTop = true;
+ else if (strstr(command, "--melonloader.loadmodeplugins") != NULL)
+ {
+ int loadmode = atoi(CommandLineV[i + 1]);
+ if (loadmode < 0)
+ loadmode = 0;
+ else if (loadmode > 2)
+ loadmode = 0;
+ LoadMode_Plugins = (LoadMode)loadmode;
+ }
+ else if (strstr(command, "--melonloader.loadmodemods") != NULL)
+ {
+ int loadmode = atoi(CommandLineV[i + 1]);
+ if (loadmode < 0)
+ loadmode = 0;
+ else if (loadmode > 2)
+ loadmode = 0;
+ LoadMode_Mods = (LoadMode)loadmode;
+ }
else if (strstr(command, "--melonloader.agregenerate") != NULL)
AG_Force_Regenerate = true;
else if (strstr(command, "--melonloader.agfvunhollower"))
@@ -194,8 +201,6 @@ void MelonLoader::ParseCommandLine()
Console::Enabled = false;
else if (strstr(command, "--melonloader.hidewarnings") != NULL)
Console::HideWarnings = false;
- //else if (strstr(command, "--melonloader.showgamelogs") != NULL)
- // Console::ShouldShowGameLogs = false;
else if (strstr(command, "--melonloader.debug") != NULL)
{
DebugMode = true;
@@ -245,11 +250,11 @@ bool MelonLoader::CheckOSVersion()
return true;
}
-void MelonLoader::UNLOAD()
+void MelonLoader::UNLOAD(bool doquitfix)
{
HookManager::UnhookAll();
Logger::Stop();
- if (MelonLoader::QuitFix)
+ if (doquitfix && MelonLoader::QuitFix)
MelonLoader::KillProcess();
}
@@ -263,59 +268,23 @@ void MelonLoader::KillProcess()
}
}
-int MelonLoader::CountSubstring(std::string pat, std::string txt)
-{
- size_t M = pat.length();
- size_t N = txt.length();
- int res = 0;
- for (int i = 0; i <= N - M; i++)
- {
- int j;
- for (j = 0; j < M; j++)
- if (txt[i + j] != pat[j])
- break;
- if (j == M)
- {
- res++;
- j = 0;
- }
- }
- return res;
-}
-
-bool MelonLoader::DirectoryExists(const char* path)
-{
- struct stat info;
- if (stat(path, &info) != NULL)
- return false;
- if (info.st_mode & S_IFDIR)
- return true;
- return false;
-}
-
-long MelonLoader::GetFileSize(std::string filename)
-{
- struct stat stat_buf;
- return ((stat(filename.c_str(), &stat_buf) == 0) ? stat_buf.st_size : -1);
-}
-
int MelonLoader::GetIntFromConstChar(const char* str, int defaultval)
{
- if (str == NULL || *str == '\0')
+ if ((str == NULL) || (*str == '\0'))
return defaultval;
bool negate = (str[0] == '-');
- if ( *str == '+' || *str == '-' )
+ if ((*str == '+') || (*str == '-'))
++str;
if (*str == '\0')
return defaultval;
int result = 0;
while(*str)
{
- if (*str >= '0' && *str <= '9')
- result = result * 10 - (*str - '0');
+ if ((*str >= '0') && (*str <= '9'))
+ result = ((result * 10) - (*str - '0'));
else
return defaultval;
++str;
}
- return negate ? result : -result;
+ return (negate ? result : -result);
}
\ No newline at end of file
diff --git a/MelonLoader/MelonLoader.h b/MelonLoader/MelonLoader.h
index d3bfbe986..2849f374f 100644
--- a/MelonLoader/MelonLoader.h
+++ b/MelonLoader/MelonLoader.h
@@ -11,8 +11,6 @@ class MelonLoader
static bool IsGameIl2Cpp;
static bool DebugMode;
static bool QuitFix;
- static bool DevModsOnly;
- static bool DevPluginsOnly;
static bool AG_Force_Regenerate;
static char* ExePath;
static char* GamePath;
@@ -22,14 +20,20 @@ class MelonLoader
static char* ForceUnhollowerVersion;
static char* ForceUnityVersion;
+ enum LoadMode
+ {
+ NORMAL,
+ DEV,
+ BOTH
+ };
+ static LoadMode LoadMode_Plugins;
+ static LoadMode LoadMode_Mods;
+
static void Main();
static void ParseCommandLine();
static void ReadAppInfo();
static bool CheckOSVersion();
- static void UNLOAD();
+ static void UNLOAD(bool doquitfix = true);
static void KillProcess();
- static int CountSubstring(std::string pat, std::string txt);
- static bool DirectoryExists(const char* path);
- static long GetFileSize(std::string filename);
static int GetIntFromConstChar(const char* str, int defaultval = 0);
};
\ No newline at end of file
diff --git a/MelonLoader/MelonLoader.vcxproj b/MelonLoader/MelonLoader.vcxproj
index a4faf29ae..45994604e 100644
--- a/MelonLoader/MelonLoader.vcxproj
+++ b/MelonLoader/MelonLoader.vcxproj
@@ -98,7 +98,7 @@
-
+
@@ -112,7 +112,7 @@
-
+
diff --git a/MelonLoader/MelonLoader.vcxproj.filters b/MelonLoader/MelonLoader.vcxproj.filters
index f20da8f8c..365ab21c0 100644
--- a/MelonLoader/MelonLoader.vcxproj.filters
+++ b/MelonLoader/MelonLoader.vcxproj.filters
@@ -6,19 +6,18 @@
-
+
-
@@ -30,6 +29,7 @@
Detours
+
diff --git a/MelonLoader/MelonLoader_Base.cpp b/MelonLoader/MelonLoader_Base.cpp
new file mode 100644
index 000000000..34d2cd203
--- /dev/null
+++ b/MelonLoader/MelonLoader_Base.cpp
@@ -0,0 +1,65 @@
+#include
+#include
+#include "MelonLoader_Base.h"
+#include "MelonLoader.h"
+#include "AssertionManager.h"
+#include "Logger.h"
+#include "HookManager.h"
+#include "Il2Cpp.h"
+
+bool MelonLoader_Base::HasInitialized = false;
+MonoMethod* MelonLoader_Base::startup = NULL;
+
+void MelonLoader_Base::Initialize()
+{
+ AssertionManager::Start("MelonLoader_Base.cpp", "MelonLoader_Base::Initialize");
+ if (Mono::Domain != NULL)
+ {
+ std::string modhandlerpath = std::string(MelonLoader::GamePath) + "\\MelonLoader\\MelonLoader.ModHandler.dll";
+ MonoAssembly* assembly = Mono::mono_domain_assembly_open(Mono::Domain, modhandlerpath.c_str());
+ AssertionManager::Decide(assembly, "MelonLoader.ModHandler.dll");
+ if (assembly != NULL)
+ {
+ MonoImage* image = Mono::mono_assembly_get_image(assembly);
+ AssertionManager::Decide(assembly, "Image");
+ if (image != NULL)
+ {
+ MonoClass* klass = Mono::mono_class_from_name(image, "MelonLoader", "MelonLoaderBase");
+ AssertionManager::Decide(assembly, "MelonLoader.MelonLoaderBase");
+ if (klass != NULL)
+ {
+ MonoMethod* initialize = Mono::mono_class_get_method_from_name(klass, "Initialize", NULL);
+ AssertionManager::Decide(initialize, "Initialize");
+ if (initialize != NULL)
+ {
+ MonoObject* exceptionObject = NULL;
+ Mono::mono_runtime_invoke(initialize, NULL, NULL, &exceptionObject);
+ if ((exceptionObject != NULL) && MelonLoader::DebugMode)
+ Mono::LogExceptionMessage(exceptionObject);
+ else
+ {
+ startup = Mono::mono_class_get_method_from_name(klass, "Startup", NULL);
+ AssertionManager::Decide(startup, "Startup");
+ if (MelonLoader::IsGameIl2Cpp)
+ HookManager::Hook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, HookManager::Hooked_runtime_invoke);
+ else
+ HookManager::Hook(&(LPVOID&)Mono::mono_runtime_invoke, HookManager::Hooked_runtime_invoke);
+ HasInitialized = true;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void MelonLoader_Base::Startup()
+{
+ if (startup != NULL)
+ {
+ MonoObject* exceptionObject = NULL;
+ Mono::mono_runtime_invoke(startup, NULL, NULL, &exceptionObject);
+ if ((exceptionObject != NULL) && MelonLoader::DebugMode)
+ Mono::LogExceptionMessage(exceptionObject);
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader/MelonLoader_Base.h b/MelonLoader/MelonLoader_Base.h
new file mode 100644
index 000000000..ab2812d34
--- /dev/null
+++ b/MelonLoader/MelonLoader_Base.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "Mono.h"
+
+class MelonLoader_Base
+{
+public:
+ enum MelonCompatibility
+ {
+ UNIVERSAL,
+ COMPATIBLE,
+ NOATTRIBUTE,
+ INCOMPATIBLE
+ };
+
+ static bool HasInitialized;
+ static MonoMethod* startup;
+
+ static void Initialize();
+ static void Startup();
+};
\ No newline at end of file
diff --git a/MelonLoader/ModHandler.cpp b/MelonLoader/ModHandler.cpp
deleted file mode 100644
index b2c5f3957..000000000
--- a/MelonLoader/ModHandler.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include
-#include
-#include "ModHandler.h"
-#include "MelonLoader.h"
-#include "AssertionManager.h"
-#include "Logger.h"
-#include "HookManager.h"
-#include "Il2Cpp.h"
-
-bool ModHandler::HasInitialized = false;
-MonoMethod* ModHandler::onApplicationStart = NULL;
-MonoMethod* ModHandler::onApplicationQuit = NULL;
-MonoMethod* ModHandler::runLogCallbacks = NULL;
-MonoMethod* ModHandler::runWarningCallbacks1 = NULL;
-MonoMethod* ModHandler::runWarningCallbacks2 = NULL;
-MonoMethod* ModHandler::runErrorCallbacks1 = NULL;
-MonoMethod* ModHandler::runErrorCallbacks2 = NULL;
-
-void ModHandler::Initialize()
-{
- AssertionManager::Start("ModHandler.cpp", "ModHandler::Initialize");
- if (Mono::Domain != NULL)
- {
- std::string modhandlerpath = std::string(MelonLoader::GamePath) + "\\MelonLoader\\MelonLoader.ModHandler.dll";
- MonoAssembly* assembly = Mono::mono_domain_assembly_open(Mono::Domain, modhandlerpath.c_str());
- AssertionManager::Decide(assembly, "MelonLoader.ModHandler.dll");
- if (assembly != NULL)
- {
- MonoImage* image = Mono::mono_assembly_get_image(assembly);
- AssertionManager::Decide(assembly, "Image");
- if (image != NULL)
- {
- MonoClass* klass = Mono::mono_class_from_name(image, "MelonLoader", "Main");
- AssertionManager::Decide(assembly, "MelonLoader.Main");
- if (klass != NULL)
- {
- MonoClass* klass2 = Mono::mono_class_from_name(image, "MelonLoader", "Console");
- AssertionManager::Decide(assembly, "MelonLoader.Console");
- if (klass2 != NULL)
- {
- MonoMethod* initialize = Mono::mono_class_get_method_from_name(klass, "Initialize", NULL);
- AssertionManager::Decide(initialize, "Initialize");
- if (initialize != NULL)
- {
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(initialize, NULL, NULL, &exceptionObject);
- if (exceptionObject && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- else
- {
- onApplicationStart = Mono::mono_class_get_method_from_name(klass, "OnApplicationStart", NULL);
- AssertionManager::Decide(onApplicationStart, "OnApplicationStart");
- onApplicationQuit = Mono::mono_class_get_method_from_name(klass, "OnApplicationQuit", NULL);
- AssertionManager::Decide(onApplicationQuit, "OnApplicationQuit");
- runLogCallbacks = Mono::mono_class_get_method_from_name(klass2, "RunLogCallbacks", 1);
- AssertionManager::Decide(runLogCallbacks, "RunLogCallbacks");
- runWarningCallbacks1 = Mono::mono_class_get_method_from_name(klass2, "RunWarningCallbacks", 1);
- AssertionManager::Decide(runWarningCallbacks1, "RunWarningCallbacks (1)");
- runWarningCallbacks2 = Mono::mono_class_get_method_from_name(klass2, "RunWarningCallbacks", 2);
- AssertionManager::Decide(runWarningCallbacks2, "RunWarningCallbacks (2)");
- runErrorCallbacks1 = Mono::mono_class_get_method_from_name(klass2, "RunErrorCallbacks", 1);
- AssertionManager::Decide(runErrorCallbacks1, "RunErrorCallbacks (1)");
- runErrorCallbacks2 = Mono::mono_class_get_method_from_name(klass2, "RunErrorCallbacks", 2);
- AssertionManager::Decide(runErrorCallbacks2, "RunErrorCallbacks (2)");
- if (MelonLoader::IsGameIl2Cpp)
- HookManager::Hook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, HookManager::Hooked_runtime_invoke);
- else
- HookManager::Hook(&(LPVOID&)Mono::mono_runtime_invoke, HookManager::Hooked_runtime_invoke);
- HasInitialized = true;
- }
- }
- }
- }
- }
- }
- }
-}
-
-void ModHandler::OnApplicationStart()
-{
- if (onApplicationStart != NULL)
- {
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(onApplicationStart, NULL, NULL, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::OnApplicationQuit()
-{
- if (onApplicationQuit != NULL)
- {
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(onApplicationQuit, NULL, NULL, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::RunLogCallbacks(const char* msg)
-{
- if (runLogCallbacks != NULL)
- {
- MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg);
- void* args[1] = { msgstr };
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(runLogCallbacks, NULL, args, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::RunWarningCallbacks(const char* msg)
-{
- if (runWarningCallbacks1 != NULL)
- {
- MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg);
- void* args[1] = { msgstr };
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(runWarningCallbacks1, NULL, args, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::RunWarningCallbacks(const char* namesection, const char* msg)
-{
- if (runWarningCallbacks2 != NULL)
- {
- MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg);
- MonoString* namesectionstr = Mono::mono_string_new(Mono::Domain, namesection);
- void* args[2] = { namesectionstr, msgstr };
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(runWarningCallbacks2, NULL, args, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::RunErrorCallbacks(const char* msg)
-{
- if (runErrorCallbacks1 != NULL)
- {
- MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg);
- void* args[1] = { msgstr };
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(runErrorCallbacks1, NULL, args, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
-
-void ModHandler::RunErrorCallbacks(const char* namesection, const char* msg)
-{
- if (runErrorCallbacks2 != NULL)
- {
- MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg);
- MonoString* namesectionstr = Mono::mono_string_new(Mono::Domain, namesection);
- void* args[2] = { namesectionstr, msgstr };
- MonoObject* exceptionObject = NULL;
- Mono::mono_runtime_invoke(runErrorCallbacks2, NULL, args, &exceptionObject);
- if ((exceptionObject != NULL) && MelonLoader::DebugMode)
- Mono::LogExceptionMessage(exceptionObject);
- }
-}
\ No newline at end of file
diff --git a/MelonLoader/ModHandler.h b/MelonLoader/ModHandler.h
deleted file mode 100644
index 9b5353341..000000000
--- a/MelonLoader/ModHandler.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-#include "Mono.h"
-
-class ModHandler
-{
-public:
- static bool HasInitialized;
- static MonoMethod* onApplicationStart;
- static MonoMethod* onApplicationQuit;
- static MonoMethod* runLogCallbacks;
- static MonoMethod* runWarningCallbacks1;
- static MonoMethod* runWarningCallbacks2;
- static MonoMethod* runErrorCallbacks1;
- static MonoMethod* runErrorCallbacks2;
-
- static void Initialize();
- static void OnApplicationStart();
- static void OnApplicationQuit();
- static void RunLogCallbacks(const char* msg);
- static void RunWarningCallbacks(const char* msg);
- static void RunWarningCallbacks(const char* namesection, const char* msg);
- static void RunErrorCallbacks(const char* msg);
- static void RunErrorCallbacks(const char* namesection, const char* msg);
-};
-
-enum ModHandler_DLLStatus
-{
- ModHandler_DLLStatus_UNIVERSAL,
- ModHandler_DLLStatus_COMPATIBLE,
- ModHandler_DLLStatus_NOATTRIBUTE,
- ModHandler_DLLStatus_INCOMPATIBLE
-};
\ No newline at end of file
diff --git a/MelonLoader/Mono.cpp b/MelonLoader/Mono.cpp
index b34d1ffba..3021286f1 100644
--- a/MelonLoader/Mono.cpp
+++ b/MelonLoader/Mono.cpp
@@ -2,7 +2,7 @@
#include "Mono.h"
#include "MelonLoader.h"
#include "AssertionManager.h"
-#include "ModHandler.h"
+#include "MelonLoader_Base.h"
#include "Logger.h"
bool Mono::IsOldMono = false;
@@ -34,9 +34,7 @@ mono_object_get_class_t Mono::mono_object_get_class = NULL;
mono_runtime_set_main_args_t Mono::mono_runtime_set_main_args = NULL;
mono_domain_set_config_t Mono::mono_domain_set_config = NULL;
mono_method_get_name_t Mono::mono_method_get_name = NULL;
-mono_debug_init_t Mono::mono_debug_init = NULL;
-mono_debug_domain_create_t Mono::mono_debug_domain_create = NULL;
-mono_jit_parse_options_t Mono::mono_jit_parse_options = NULL;
+mono_unity_install_unitytls_interface_t Mono::mono_unity_install_unitytls_interface = NULL;
bool Mono::Load()
{
@@ -84,11 +82,7 @@ bool Mono::Setup()
mono_object_get_class = (mono_object_get_class_t)AssertionManager::GetExport(Module, "mono_object_get_class");
if (MelonLoader::IsGameIl2Cpp)
- {
- mono_debug_init = (mono_debug_init_t)AssertionManager::GetExport(Module, "mono_debug_init");
- mono_debug_domain_create = (mono_debug_domain_create_t)AssertionManager::GetExport(Module, "mono_debug_domain_create");
- mono_jit_parse_options = (mono_jit_parse_options_t)AssertionManager::GetExport(Module, "mono_jit_parse_options");
- }
+ mono_unity_install_unitytls_interface = (mono_unity_install_unitytls_interface_t)AssertionManager::GetExport(Module, "mono_unity_install_unitytls_interface");
if (!IsOldMono)
{
@@ -109,9 +103,6 @@ void Mono::CreateDomain()
mono_runtime_set_main_args(MelonLoader::CommandLineC, MelonLoader::CommandLineV);
Domain = mono_jit_init("MelonLoader");
Mono::FixDomainBaseDir();
- mono_debug_init(MONO_DEBUG_FORMAT_MONO);
- mono_debug_domain_create(Domain);
- mono_jit_parse_options(MelonLoader::CommandLineC, MelonLoader::CommandLineV);
}
}
@@ -122,5 +113,43 @@ void Mono::FixDomainBaseDir()
mono_domain_set_config(Domain, MelonLoader::GamePath, "MelonLoader");
}
-const char* Mono::GetStringProperty(const char* propertyName, MonoClass* classType, MonoObject* classObject) { return mono_string_to_utf8((MonoString*)mono_runtime_invoke(mono_property_get_get_method(mono_class_get_property_from_name(classType, propertyName)), classObject, NULL, NULL)); }
-void Mono::LogExceptionMessage(MonoObject* exceptionObject, bool shouldThrow) { if (shouldThrow) AssertionManager::ThrowError(GetStringProperty("Message", mono_object_get_class(exceptionObject), exceptionObject)); else Logger::LogError(GetStringProperty("Message", mono_object_get_class(exceptionObject), exceptionObject)); }
\ No newline at end of file
+const char* Mono::GetStringProperty(const char* propertyName, MonoClass* classType, MonoObject* classObject)
+{
+ if (propertyName == NULL)
+ return NULL;
+ if (classType == NULL)
+ return NULL;
+ if (classObject == NULL)
+ return NULL;
+ MonoProperty* prop = mono_class_get_property_from_name(classType, propertyName);
+ if (prop == NULL)
+ return NULL;
+ MonoMethod* method = mono_property_get_get_method(prop);
+ if (method == NULL)
+ return NULL;
+ MonoString* returnstr = (MonoString*)mono_runtime_invoke(method, classObject, NULL, NULL);
+ if (returnstr == NULL)
+ return NULL;
+ return mono_string_to_utf8(returnstr);
+}
+
+void Mono::LogExceptionMessage(MonoObject* exceptionObject, bool shouldThrow)
+{
+ if (exceptionObject == NULL)
+ return;
+ MonoClass* klass = mono_object_get_class(exceptionObject);
+ if (klass == NULL)
+ return;
+ const char* stringprop = GetStringProperty("Message", klass, exceptionObject);
+ if (stringprop == NULL)
+ return;
+ if (shouldThrow)
+ AssertionManager::ThrowError(stringprop);
+ else
+ {
+ Logger::LogTimestamp(ConsoleColor_Red);
+ Logger::LogFile << "[Error] " << stringprop << std::endl;
+ Console::Write("[MelonLoader] ", ConsoleColor_Red);
+ Console::WriteLine(("[Error] " + std::string(stringprop)), ConsoleColor_Red);
+ }
+}
\ No newline at end of file
diff --git a/MelonLoader/Mono.h b/MelonLoader/Mono.h
index f67dbbd18..48a844356 100644
--- a/MelonLoader/Mono.h
+++ b/MelonLoader/Mono.h
@@ -43,6 +43,7 @@ typedef void (*mono_domain_set_config_t)(MonoDomain* domain, const char* configp
typedef void (*mono_debug_init_t) (MonoDebugFormat format);
typedef void (*mono_debug_domain_create_t) (MonoDomain* domain);
typedef void (*mono_jit_parse_options_t)(int argc, char* argv[]);
+typedef void (*mono_unity_install_unitytls_interface_t)(const void* unitytlsInterfaceStruct);
class Mono
{
@@ -76,9 +77,7 @@ class Mono
static mono_object_get_class_t mono_object_get_class;
static mono_runtime_set_main_args_t mono_runtime_set_main_args;
static mono_domain_set_config_t mono_domain_set_config;
- static mono_debug_init_t mono_debug_init;
- static mono_debug_domain_create_t mono_debug_domain_create;
- static mono_jit_parse_options_t mono_jit_parse_options;
+ static mono_unity_install_unitytls_interface_t mono_unity_install_unitytls_interface;
static bool Load();
static bool Setup();
diff --git a/README.md b/README.md
index fd759323f..d37db3caa 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,14 @@
+### GENERAL INFORMATION:
+
+- Normal Console is for Debugging and Displaying the Output of Plugins and Mods.
+- Debug Mode/Console is for Debugging MelonLoader Internals.
+- All Plugins get placed in the created Plugins folder inside the Game's Install Folder.
+- All Mods get placed in the created Mods folder inside the Game's Install Folder.
+- All Logs are made in the created Logs folder inside the Game's Install Folder.
+- In order to properly use dnSpy's Debugger run the game with the Debug Mode/Console launch option.
+
+---
+
### REQUIREMENTS:
- [.NET Framework 4.7.2 Runtime](https://dotnet.microsoft.com/download/dotnet-framework/net472)
@@ -48,25 +59,10 @@
---
-### GENERAL INFORMATION:
-
-- All Plugins get placed in the created Plugins folder inside the Game's Install Folder.
-- All Mods get placed in the created Mods folder inside the Game's Install Folder.
-- All Logs are made in the created Logs folder inside the Game's Install Folder.
-
----
-
-### CONSOLE DIFFERENCES:
-
-- Normal Console is for Debugging and Displaying the Output of Plugins and Mods.
-- Debug Mode/Console is for Debugging MelonLoader Internals.
-
----
-
### LAUNCH OPTIONS:
-| Argument | Description |
-| --------------------- | ---------------------------------------- |
+| Argument | Description |
+| - | - |
| --no-mods | Launch game without loading Mods |
| --quitfix | Fixes the Hanging Process Issue with some Games |
| --melonloader.hideconsole | Hides the Normal Console |
@@ -75,20 +71,32 @@
| --melonloader.magenta | Magenta Console Color |
| --melonloader.rainbow | Rainbow Console Color |
| --melonloader.randomrainbow | Random Rainbow Console Color |
-| --melonloader.maxlogs | Max Log Files [ Default: 10 ] [ Disable: 0 ] |
-| --melonloader.maxwarnings | Max Warnings per Log File [ Default: 100 ] [ Disable: 0 ] |
-| --melonloader.maxerrors | Max Errors per Log File [ Default: 100 ] [ Disable: 0 ] |
-| --melonloader.devpluginsonly | Loads only Plugins with the "-dev.dll" extension |
-| --melonloader.devmodsonly | Loads only Mods with the "-dev.dll" extension |
+| --melonloader.maxlogs | Max Log Files [ Default: 10 ] [ Disable: 0 ] |
+| --melonloader.maxwarnings | Max Warnings per Log File [ Default: 100 ] [ Disable: 0 ] |
+| --melonloader.maxerrors | Max Errors per Log File [ Default: 100 ] [ Disable: 0 ] |
+| --melonloader.loadmodeplugins | Load Mode for Plugins [ Default: 0 ] |
+| --melonloader.loadmodemods | Load Mode for Mods [ Default: 0 ] |
| --melonloader.agregenerate | Forces Assembly to be Regenerated on Il2Cpp Games |
-| --melonloader.agfvunhollower | Forces the Assembly Generator to use a Specified Version of Il2CppAssemblyUnhollower |
+| --melonloader.agfvunhollower | Forces use a Specified Version of Il2CppAssemblyUnhollower |
+| --melonloader.consoleontop | Forces the Console over all other Applications |
---
-### CREDITS+LICENSING:
+### LOAD MODES:
-MelonLoader is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/LICENSE.md) for the full License.
-[MelonLoader.dll](MelonLoader) was adapted from [AtiRoNya](https://github.com/AtiLion/AtiRoNya) by [AtiLion](https://github.com/AtiLion), licensed under [MIT License](https://github.com/AtiLion/AtiRoNya/blob/e20e4a8fc47b37834c8284f9e6e937f04a84c510/LICENSE)
+- Load Mode launch options are a way to dictate how you want Mods or Plugins to Load.
+
+| Value | Action |
+| - | - |
+| 0 | NORMAL - Load them only if they don't have the "-dev" Name Extension |
+| 1 | DEV - Load them only if they have the "-dev" Name Extension |
+| 2 | BOTH - Load All |
+
+---
+
+### LICENSING & CREDITS:
+
+MelonLoader is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/LICENSE.md) for the full License.
Third-party libraries used as source code or bundled in binary form:
- [Research Detours Package](https://github.com/microsoft/Detours) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/Detours/LICENSE.md) for the full License.
@@ -98,6 +106,7 @@ Third-party libraries used as source code or bundled in binary form:
- [LightJson](https://github.com/MarcosLopezC/LightJson) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.Installer/LightJson/LICENSE.txt) for the full License.
- [Tomlyn](https://github.com/xoofx/Tomlyn) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.ModHandler/Tomlyn/license.txt) for the full License.
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.ModHandler/SharpZipLib/LICENSE.txt) for the full License.
+- [AtiRoNya](https://github.com/AtiLion/AtiRoNya) is licensed under the MIT License. See [LICENSE](https://github.com/AtiLion/AtiRoNya/blob/e20e4a8fc47b37834c8284f9e6e937f04a84c510/LICENSE) for the full License.
- Unity Runtime libraries (found in [Unity Dependencies](BaseLibs/Unity%20Dependencies)) are part of Unity Software.
Their usage is subject to [Unity Terms of Service](https://unity3d.com/legal/terms-of-service), including [Unity Software Additional Terms](https://unity3d.com/legal/terms-of-service/software).
@@ -105,9 +114,7 @@ External tools downloaded and used at runtime:
- [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) is licensed under the MIT License. See [LICENSE](https://github.com/Perfare/Il2CppDumper/blob/master/LICENSE) for the full License.
- [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower) is licensed under the GNU Lesser General Public License v3.0. See [LICENSE](https://github.com/knah/Il2CppAssemblyUnhollower/blob/master/LICENSE) for the full License.
----
-
See [MelonLoader Wiki](https://melonwiki.xyz/#/credits) for the full Credits.
MelonLoader is not sponsored by, affiliated with or endorsed by Unity Technologies or its affiliates.
-"Unity" is a trademark or a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere.
\ No newline at end of file
+"Unity" is a trademark or a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere.
diff --git a/TestMod/AssemblyInfo.cs b/TestMod/AssemblyInfo.cs
index 55fa18241..3ca89fe0b 100644
--- a/TestMod/AssemblyInfo.cs
+++ b/TestMod/AssemblyInfo.cs
@@ -1,25 +1,18 @@
-using System.Resources;
-using System.Reflection;
-using System.Runtime.InteropServices;
+using System.Reflection;
using MelonLoader;
[assembly: AssemblyTitle(TestMod.BuildInfo.Description)]
[assembly: AssemblyDescription(TestMod.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(TestMod.BuildInfo.Company)]
[assembly: AssemblyProduct(TestMod.BuildInfo.Name)]
[assembly: AssemblyCopyright("Created by " + TestMod.BuildInfo.Author)]
[assembly: AssemblyTrademark(TestMod.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
[assembly: AssemblyVersion(TestMod.BuildInfo.Version)]
[assembly: AssemblyFileVersion(TestMod.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
-[assembly: MelonModInfo(typeof(TestMod.TestMod), TestMod.BuildInfo.Name, TestMod.BuildInfo.Version, TestMod.BuildInfo.Author, TestMod.BuildInfo.DownloadLink)]
+[assembly: MelonInfo(typeof(TestMod.TestMod), TestMod.BuildInfo.Name, TestMod.BuildInfo.Version, TestMod.BuildInfo.Author, TestMod.BuildInfo.DownloadLink)]
-// Create and Setup a MelonModGame to mark a Mod as Universal or Compatible with specific Games.
-// If no MelonModGameAttribute is found or any of the Values for any MelonModGame on the Mod is null or empty it will be assumed the Mod is Universal.
-// Values for MelonModGame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version.
-[assembly: MelonModGame(null, null)]
\ No newline at end of file
+// Create and Setup a MelonGame to mark a Mod as Universal or Compatible with specific Games.
+// If no MelonGameAttribute is found or any of the Values for any MelonGame on the Mod is null or empty it will be assumed the Mod is Universal.
+// Values for MelonMame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version.
+[assembly: MelonGame(null, null)]
\ No newline at end of file
diff --git a/TestMod/Main.cs b/TestMod/Main.cs
index 7d8b0a033..ab27ebf5f 100644
--- a/TestMod/Main.cs
+++ b/TestMod/Main.cs
@@ -16,57 +16,57 @@ public class TestMod : MelonMod
{
public override void OnApplicationStart() // Runs after Game Initialization.
{
- MelonModLogger.Log("OnApplicationStart");
+ MelonLogger.Log("OnApplicationStart");
}
public override void OnLevelIsLoading() // Runs when a Scene is Loading or when a Loading Screen is Shown. Currently only runs if the Mod is used in BONEWORKS.
{
- MelonModLogger.Log("OnLevelIsLoading");
+ MelonLogger.Log("OnLevelIsLoading");
}
public override void OnLevelWasLoaded(int level) // Runs when a Scene has Loaded.
{
- MelonModLogger.Log("OnLevelWasLoaded: " + level.ToString());
+ MelonLogger.Log("OnLevelWasLoaded: " + level.ToString());
}
public override void OnLevelWasInitialized(int level) // Runs when a Scene has Initialized.
{
- MelonModLogger.Log("OnLevelWasInitialized: " + level.ToString());
+ MelonLogger.Log("OnLevelWasInitialized: " + level.ToString());
}
public override void OnUpdate() // Runs once per frame.
{
- MelonModLogger.Log("OnUpdate");
+ MelonLogger.Log("OnUpdate");
}
public override void OnFixedUpdate() // Can run multiple times per frame. Mostly used for Physics.
{
- MelonModLogger.Log("OnFixedUpdate");
+ MelonLogger.Log("OnFixedUpdate");
}
public override void OnLateUpdate() // Runs once per frame after OnUpdate and OnFixedUpdate have finished.
{
- MelonModLogger.Log("OnLateUpdate");
+ MelonLogger.Log("OnLateUpdate");
}
public override void OnGUI() // Can run multiple times per frame. Mostly used for Unity's IMGUI.
{
- MelonModLogger.Log("OnGUI");
+ MelonLogger.Log("OnGUI");
}
public override void OnApplicationQuit() // Runs when the Game is told to Close.
{
- MelonModLogger.Log("OnApplicationQuit");
+ MelonLogger.Log("OnApplicationQuit");
}
public override void OnModSettingsApplied() // Runs when Mod Preferences get saved to UserData/modprefs.ini.
{
- MelonModLogger.Log("OnModSettingsApplied");
+ MelonLogger.Log("OnModSettingsApplied");
}
public override void VRChat_OnUiManagerInit() // Runs upon VRChat's UiManager Initialization. Only runs if the Mod is used in VRChat.
{
- MelonModLogger.Log("VRChat_OnUiManagerInit");
+ MelonLogger.Log("VRChat_OnUiManagerInit");
}
}
}
\ No newline at end of file
diff --git a/TestPlugin/AssemblyInfo.cs b/TestPlugin/AssemblyInfo.cs
index 4d0d9814d..30282423e 100644
--- a/TestPlugin/AssemblyInfo.cs
+++ b/TestPlugin/AssemblyInfo.cs
@@ -1,25 +1,18 @@
-using System.Resources;
-using System.Reflection;
-using System.Runtime.InteropServices;
+using System.Reflection;
using MelonLoader;
[assembly: AssemblyTitle(TestPlugin.BuildInfo.Description)]
[assembly: AssemblyDescription(TestPlugin.BuildInfo.Description)]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(TestPlugin.BuildInfo.Company)]
[assembly: AssemblyProduct(TestPlugin.BuildInfo.Name)]
[assembly: AssemblyCopyright("Created by " + TestPlugin.BuildInfo.Author)]
[assembly: AssemblyTrademark(TestPlugin.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
[assembly: AssemblyVersion(TestPlugin.BuildInfo.Version)]
[assembly: AssemblyFileVersion(TestPlugin.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
-[assembly: MelonPluginInfo(typeof(TestPlugin.TestPlugin), TestPlugin.BuildInfo.Name, TestPlugin.BuildInfo.Version, TestPlugin.BuildInfo.Author, TestPlugin.BuildInfo.DownloadLink)]
+[assembly: MelonInfo(typeof(TestPlugin.TestPlugin), TestPlugin.BuildInfo.Name, TestPlugin.BuildInfo.Version, TestPlugin.BuildInfo.Author, TestPlugin.BuildInfo.DownloadLink)]
// Create and Setup a MelonPluginGame to mark a Plugin as Universal or Compatible with specific Games.
// If no MelonPluginGameAttribute is found or any of the Values for any MelonPluginGame on the Mod is null or empty it will be assumed the Plugin is Universal.
// Values for MelonPluginGame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version.
-[assembly: MelonPluginGame(null, null)]
\ No newline at end of file
+[assembly: MelonGame(null, null)]
\ No newline at end of file
diff --git a/TestPlugin/Main.cs b/TestPlugin/Main.cs
index 4bda4a739..8cbc2df49 100644
--- a/TestPlugin/Main.cs
+++ b/TestPlugin/Main.cs
@@ -16,22 +16,22 @@ public class TestPlugin : MelonPlugin
{
public override void OnPreInitialization() // Runs before Game Initialization.
{
- MelonModLogger.Log("OnPreInitialization");
+ MelonLogger.Log("OnPreInitialization");
}
public override void OnApplicationStart() // Runs after Game Initialization.
{
- MelonModLogger.Log("OnApplicationStart");
+ MelonLogger.Log("OnApplicationStart");
}
public override void OnApplicationQuit() // Runs when the Game is told to Close.
{
- MelonModLogger.Log("OnApplicationQuit");
+ MelonLogger.Log("OnApplicationQuit");
}
public override void OnModSettingsApplied() // Runs when Mod Preferences get saved to UserData/modprefs.ini.
{
- MelonModLogger.Log("OnModSettingsApplied");
+ MelonLogger.Log("OnModSettingsApplied");
}
}
}
\ No newline at end of file
diff --git a/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs b/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs
index bd3058814..3dd9153de 100644
--- a/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs
+++ b/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs
@@ -1,17 +1,12 @@
-using System.Resources;
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("AssetBundle extension for Il2Cpp Games")]
[assembly: AssemblyDescription("AssetBundle extension for Il2Cpp Games")]
-[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)]
[assembly: AssemblyProduct("UnityEngine.Il2CppAssetBundleManager")]
[assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)]
[assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-//[assembly: Guid("")]
+[assembly: Guid("338a5234-8f1d-491a-8fde-a3d27ba00f44")]
[assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)]
-[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
-[assembly: NeutralResourcesLanguage("en")]
\ No newline at end of file
+[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)]
\ No newline at end of file