diff --git a/BepInEx.IL2CPP/DoorstopEntrypoint.cs b/BepInEx.IL2CPP/DoorstopEntrypoint.cs index e05b1aa..18b2d93 100644 --- a/BepInEx.IL2CPP/DoorstopEntrypoint.cs +++ b/BepInEx.IL2CPP/DoorstopEntrypoint.cs @@ -1,66 +1,18 @@ -using System; +using System; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; +using BepInEx.IL2CPP; using BepInEx.Preloader.Core; -namespace BepInEx.IL2CPP; +namespace Doorstop; -internal static class UnityPreloaderRunner +internal static class Entrypoint { - public static void PreloaderMain(string[] args) - { - var bepinPath = - Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH))); - - PlatformUtils.SetPlatform(); - - Paths.SetExecutablePath(EnvVars.DOORSTOP_PROCESS_PATH, bepinPath, EnvVars.DOORSTOP_MANAGED_FOLDER_DIR, - EnvVars.DOORSTOP_DLL_SEARCH_DIRS); - - // Cecil 0.11 requires one to manually set up list of trusted assemblies for assembly resolving - AppDomain.CurrentDomain.AddCecilPlatformAssemblies(Paths.ManagedPath); - - AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; - AppDomain.CurrentDomain.AssemblyResolve -= DoorstopEntrypoint.ResolveCurrentDirectory; - - Preloader.Run(); - } - - internal static Assembly LocalResolve(object sender, ResolveEventArgs args) - { - var assemblyName = new AssemblyName(args.Name); - - var foundAssembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(x => x.GetName().Name == assemblyName.Name); - - if (foundAssembly != null) - return foundAssembly; - - if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Preloader.IL2CPPUnhollowedPath, out foundAssembly)) - return foundAssembly; - - return null; - } -} - -internal static class DoorstopEntrypoint -{ - private static string preloaderPath; - /// /// The main entrypoint of BepInEx, called from Doorstop. /// - /// - /// The arguments passed in from Doorstop. First argument is the path of the currently executing - /// process. - /// - public static void Main(string[] args) + public static void Start() { // We set it to the current directory first as a fallback, but try to use the same location as the .exe file. var silentExceptionLog = $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"; @@ -73,17 +25,12 @@ internal static class DoorstopEntrypoint silentExceptionLog = Path.Combine(Path.GetDirectoryName(EnvVars.DOORSTOP_PROCESS_PATH), silentExceptionLog); - // Get the path of this DLL via Doorstop env var because Assembly.Location mangles non-ASCII characters on some versions of Mono for unknown reasons - preloaderPath = Path.GetDirectoryName(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH)); - mutex = new Mutex(false, Process.GetCurrentProcess().ProcessName + EnvVars.DOORSTOP_PROCESS_PATH + - typeof(DoorstopEntrypoint).FullName); + typeof(Doorstop.Entrypoint).FullName); mutex.WaitOne(); - AppDomain.CurrentDomain.AssemblyResolve += ResolveCurrentDirectory; - - UnityPreloaderRunner.PreloaderMain(args); + UnityPreloaderRunner.PreloaderMain(); } catch (Exception ex) { @@ -94,18 +41,4 @@ internal static class DoorstopEntrypoint mutex?.ReleaseMutex(); } } - - public static Assembly ResolveCurrentDirectory(object sender, ResolveEventArgs args) - { - var name = new AssemblyName(args.Name); - - try - { - return Assembly.LoadFile(Path.Combine(preloaderPath, $"{name.Name}.dll")); - } - catch (Exception) - { - return null; - } - } } diff --git a/BepInEx.IL2CPP/UnityPreloadRunner.cs b/BepInEx.IL2CPP/UnityPreloadRunner.cs new file mode 100644 index 0000000..aaf821b --- /dev/null +++ b/BepInEx.IL2CPP/UnityPreloadRunner.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using BepInEx.Preloader.Core; + +namespace BepInEx.IL2CPP; + +internal static class UnityPreloaderRunner +{ + public static void PreloaderMain() + { + var bepinPath = + Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH))); + + PlatformUtils.SetPlatform(); + + Paths.SetExecutablePath(EnvVars.DOORSTOP_PROCESS_PATH, bepinPath, EnvVars.DOORSTOP_MANAGED_FOLDER_DIR, + EnvVars.DOORSTOP_DLL_SEARCH_DIRS); + + // Cecil 0.11 requires one to manually set up list of trusted assemblies for assembly resolving + AppDomain.CurrentDomain.AddCecilPlatformAssemblies(Paths.ManagedPath); + + Preloader.IL2CPPUnhollowedPath = Path.Combine(Paths.BepInExRootPath, "unhollowed"); + + AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; + + + Preloader.Run(); + } + + internal static Assembly LocalResolve(object sender, ResolveEventArgs args) + { + var assemblyName = new AssemblyName(args.Name); + + var foundAssembly = AppDomain.CurrentDomain.GetAssemblies() + .FirstOrDefault(x => x.GetName().Name == assemblyName.Name); + + if (foundAssembly != null) + return foundAssembly; + + if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly) + || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly) + || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly) + || Utility.TryResolveDllAssembly(assemblyName, Preloader.IL2CPPUnhollowedPath, out foundAssembly)) + return foundAssembly; + + return null; + } +} diff --git a/BepInEx.Preloader.Unity/DoorstopEntrypoint.cs b/BepInEx.Preloader.Unity/DoorstopEntrypoint.cs index 30cfe11..57b96e5 100644 --- a/BepInEx.Preloader.Unity/DoorstopEntrypoint.cs +++ b/BepInEx.Preloader.Unity/DoorstopEntrypoint.cs @@ -1,117 +1,16 @@ -using System; +using System; using System.IO; -using System.Linq; -using System.Reflection; using BepInEx.Preloader.Core; -using BepInEx.Preloader.RuntimeFixes; +using BepInEx.Preloader.Unity; -namespace BepInEx.Preloader.Unity; +namespace Doorstop; -internal static class UnityPreloaderRunner +internal static class Entrypoint { - // This is a list of important assemblies in BepInEx core folder that should be force-loaded - // Some games can ship these assemblies in Managed folder, in which case assembly resolving bypasses our LocalResolve - // On the other hand, renaming these assemblies is not viable because 3rd party assemblies - // that we don't build (e.g. MonoMod, Harmony, many plugins) depend on them - // As such, we load them early so that the game uses our version instead - // These assemblies should be known to be rarely edited and are known to be shipped as-is with Unity assets - private static readonly string[] CriticalAssemblies = - { - "Mono.Cecil.dll", - "Mono.Cecil.Mdb.dll", - "Mono.Cecil.Pdb.dll", - "Mono.Cecil.Rocks.dll" - }; - - private static void LoadCriticalAssemblies() - { - foreach (var criticalAssembly in CriticalAssemblies) - try - { - Assembly.LoadFile(Path.Combine(Paths.BepInExAssemblyDirectory, criticalAssembly)); - } - catch (Exception) - { - // Suppress error for now - // TODO: Should we crash here if load fails? Can't use logging at this point - } - } - - public static void PreloaderPreMain() - { - PlatformUtils.SetPlatform(); - - var bepinPath = Utility.ParentDirectory(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH), 2); - - Paths.SetExecutablePath(EnvVars.DOORSTOP_PROCESS_PATH, bepinPath, - EnvVars.DOORSTOP_MANAGED_FOLDER_DIR, - EnvVars.DOORSTOP_DLL_SEARCH_DIRS); - - LoadCriticalAssemblies(); - AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; - // Remove temporary resolver early so it won't override local resolver - AppDomain.CurrentDomain.AssemblyResolve -= DoorstopEntrypoint.ResolveCurrentDirectory; - PreloaderMain(); - } - - private static void PreloaderMain() - { - if (UnityPreloader.ConfigApplyRuntimePatches.Value) - { - XTermFix.Apply(); - ConsoleSetOutFix.Apply(); - } - - UnityPreloader.Run(); - } - - private static Assembly LocalResolve(object sender, ResolveEventArgs args) - { - if (!Utility.TryParseAssemblyName(args.Name, out var assemblyName)) - return null; - - // Use parse assembly name on managed side because native GetName() can fail on some locales - // if the game path has "exotic" characters - - var validAssemblies = AppDomain.CurrentDomain - .GetAssemblies() - .Select(a => new - { - assembly = a, - name = Utility.TryParseAssemblyName(a.FullName, out var name) - ? name - : null - }) - .Where(a => a.name != null && a.name.Name == assemblyName.Name) - .OrderByDescending(a => a.name.Version) - .ToList(); - - // First try to match by version, then just pick the best match (generally highest) - // This should mainly affect cases where the game itself loads some assembly (like Mono.Cecil) - var foundMatch = validAssemblies.FirstOrDefault(a => a.name.Version == assemblyName.Version) ?? - validAssemblies.FirstOrDefault(); - var foundAssembly = foundMatch?.assembly; - - if (foundAssembly != null) - return foundAssembly; - - if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly) - || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly)) - return foundAssembly; - - return null; - } -} - -internal static class DoorstopEntrypoint -{ - private static string preloaderPath; - /// /// The main entrypoint of BepInEx, called from Doorstop. /// - public static void Main() + public static void Start() { // We set it to the current directory first as a fallback, but try to use the same location as the .exe file. var silentExceptionLog = $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"; @@ -123,39 +22,15 @@ internal static class DoorstopEntrypoint var gamePath = Path.GetDirectoryName(EnvVars.DOORSTOP_PROCESS_PATH) ?? "."; silentExceptionLog = Path.Combine(gamePath, silentExceptionLog); - // Get the path of this DLL via Doorstop env var because Assembly.Location mangles non-ASCII characters on some versions of Mono for unknown reasons - preloaderPath = Path.GetDirectoryName(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH)); - - AppDomain.CurrentDomain.AssemblyResolve += ResolveCurrentDirectory; - // In some versions of Unity 4, Mono tries to resolve BepInEx.dll prematurely because of the call to Paths.SetExecutablePath // To prevent that, we have to use reflection and a separate startup class so that we can install required assembly resolvers before the main code - typeof(DoorstopEntrypoint).Assembly.GetType($"BepInEx.Preloader.Unity.{nameof(UnityPreloaderRunner)}") - ?.GetMethod(nameof(UnityPreloaderRunner.PreloaderPreMain)) - ?.Invoke(null, null); + typeof(Doorstop.Entrypoint).Assembly.GetType($"BepInEx.Preloader.Unity.{nameof(UnityPreloaderRunner)}") + ?.GetMethod(nameof(UnityPreloaderRunner.PreloaderPreMain)) + ?.Invoke(null, null); } catch (Exception ex) { File.WriteAllText(silentExceptionLog, ex.ToString()); } - finally - { - AppDomain.CurrentDomain.AssemblyResolve -= ResolveCurrentDirectory; - } - } - - internal static Assembly ResolveCurrentDirectory(object sender, ResolveEventArgs args) - { - // Can't use Utils here because it's not yet resolved - var name = new AssemblyName(args.Name); - - try - { - return Assembly.LoadFile(Path.Combine(preloaderPath, $"{name.Name}.dll")); - } - catch (Exception) - { - return null; - } } } diff --git a/BepInEx.Preloader.Unity/UnityPreloaderRunner.cs b/BepInEx.Preloader.Unity/UnityPreloaderRunner.cs new file mode 100644 index 0000000..ff5caa2 --- /dev/null +++ b/BepInEx.Preloader.Unity/UnityPreloaderRunner.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using BepInEx.Preloader.Core; +using BepInEx.Preloader.RuntimeFixes; + +namespace BepInEx.Preloader.Unity; + +internal static class UnityPreloaderRunner +{ + // This is a list of important assemblies in BepInEx core folder that should be force-loaded + // Some games can ship these assemblies in Managed folder, in which case assembly resolving bypasses our LocalResolve + // On the other hand, renaming these assemblies is not viable because 3rd party assemblies + // that we don't build (e.g. MonoMod, Harmony, many plugins) depend on them + // As such, we load them early so that the game uses our version instead + // These assemblies should be known to be rarely edited and are known to be shipped as-is with Unity assets + private static readonly string[] CriticalAssemblies = + { + "Mono.Cecil.dll", + "Mono.Cecil.Mdb.dll", + "Mono.Cecil.Pdb.dll", + "Mono.Cecil.Rocks.dll" + }; + + private static void LoadCriticalAssemblies() + { + foreach (var criticalAssembly in CriticalAssemblies) + try + { + Assembly.LoadFile(Path.Combine(Paths.BepInExAssemblyDirectory, criticalAssembly)); + } + catch (Exception) + { + // Suppress error for now + // TODO: Should we crash here if load fails? Can't use logging at this point + } + } + + public static void PreloaderPreMain() + { + PlatformUtils.SetPlatform(); + + var bepinPath = Utility.ParentDirectory(Path.GetFullPath(EnvVars.DOORSTOP_INVOKE_DLL_PATH), 2); + + Paths.SetExecutablePath(EnvVars.DOORSTOP_PROCESS_PATH, bepinPath, + EnvVars.DOORSTOP_MANAGED_FOLDER_DIR, + EnvVars.DOORSTOP_DLL_SEARCH_DIRS); + + LoadCriticalAssemblies(); + AppDomain.CurrentDomain.AssemblyResolve += LocalResolve; + PreloaderMain(); + } + + private static void PreloaderMain() + { + if (UnityPreloader.ConfigApplyRuntimePatches.Value) + { + XTermFix.Apply(); + ConsoleSetOutFix.Apply(); + } + + UnityPreloader.Run(); + } + + private static Assembly LocalResolve(object sender, ResolveEventArgs args) + { + if (!Utility.TryParseAssemblyName(args.Name, out var assemblyName)) + return null; + + // Use parse assembly name on managed side because native GetName() can fail on some locales + // if the game path has "exotic" characters + + var validAssemblies = AppDomain.CurrentDomain + .GetAssemblies() + .Select(a => new + { + assembly = a, + name = Utility.TryParseAssemblyName(a.FullName, out var name) + ? name + : null + }) + .Where(a => a.name != null && a.name.Name == assemblyName.Name) + .OrderByDescending(a => a.name.Version) + .ToList(); + + // First try to match by version, then just pick the best match (generally highest) + // This should mainly affect cases where the game itself loads some assembly (like Mono.Cecil) + var foundMatch = validAssemblies.FirstOrDefault(a => a.name.Version == assemblyName.Version) ?? + validAssemblies.FirstOrDefault(); + var foundAssembly = foundMatch?.assembly; + + if (foundAssembly != null) + return foundAssembly; + + if (Utility.TryResolveDllAssembly(assemblyName, Paths.BepInExAssemblyDirectory, out foundAssembly) + || Utility.TryResolveDllAssembly(assemblyName, Paths.PatcherPluginPath, out foundAssembly) + || Utility.TryResolveDllAssembly(assemblyName, Paths.PluginPath, out foundAssembly)) + return foundAssembly; + + return null; + } +} diff --git a/build.cake b/build.cake index 5e1e053..b26b9d1 100644 --- a/build.cake +++ b/build.cake @@ -82,10 +82,11 @@ Task("Build") } }); -const string DOORSTOP_VER_WIN = "3.4.1.0"; +const string DOORSTOP_VER_WIN = "4.0.0-alpha.1"; const string DOORSTOP_VER_UNIX = "1.5.1.0"; const string MONO_VER = "2021.6.24"; -const string DOORSTOP_DLL = "winhttp.dll"; +const string DOORSTOP_DLL = "doorstop.dll"; +const string DOORSTOP_PROXY_DLL = "winhttp.dll"; var depCachePath = Directory($"./bin/{DEP_CACHE_NAME}"); var doorstopPath = depCachePath + Directory("doorstop"); @@ -112,15 +113,15 @@ Task("DownloadDependencies") if (NeedsRedownload("NeighTools/UnityDoorstop", DOORSTOP_VER_WIN)) { - Information("Updating Doorstop (Windows)"); - var doorstopX64Path = doorstopPath + File("doorstop_x64.zip"); - var doorstopX86Path = doorstopPath + File("doorstop_x86.zip"); + Information("Updating Doorstop"); + // var doorstopX64Path = doorstopPath + File("doorstop_x64.zip"); + // var doorstopX86Path = doorstopPath + File("doorstop_x86.zip"); - DownloadFile($"https://github.com/NeighTools/UnityDoorstop/releases/download/v{DOORSTOP_VER_WIN}/Doorstop_x64_{DOORSTOP_VER_WIN}.zip", doorstopX64Path); - DownloadFile($"https://github.com/NeighTools/UnityDoorstop/releases/download/v{DOORSTOP_VER_WIN}/Doorstop_x86_{DOORSTOP_VER_WIN}.zip", doorstopX86Path); + // DownloadFile($"https://github.com/NeighTools/UnityDoorstop/releases/download/v{DOORSTOP_VER_WIN}/Doorstop_x64_{DOORSTOP_VER_WIN}.zip", doorstopX64Path); + // DownloadFile($"https://github.com/NeighTools/UnityDoorstop/releases/download/v{DOORSTOP_VER_WIN}/Doorstop_x86_{DOORSTOP_VER_WIN}.zip", doorstopX86Path); - ZipUncompress(doorstopX86Path, doorstopPath + Directory("x86")); - ZipUncompress(doorstopX64Path, doorstopPath + Directory("x64")); + // ZipUncompress(doorstopX86Path, doorstopPath + Directory("x86")); + // ZipUncompress(doorstopX64Path, doorstopPath + Directory("x64")); } if (NeedsRedownload("NeighTools/UnityDoorstop.Unix", DOORSTOP_VER_UNIX)) @@ -222,7 +223,14 @@ Task("MakeDist") if (doorstopArchPath != null) { CopyFile("./doorstop/" + doorstopConfigFile, Directory(distArchDir) + File(isUnix ? "run_bepinex.sh" : "doorstop_config.ini")); - CopyFiles(doorstopArchPath, doorstopDir); + if (isUnix) + { + CopyFiles(doorstopArchPath, doorstopDir); + } + else + { + CopyFile(doorstopArchPath, Directory(doorstopDir) + File(DOORSTOP_PROXY_DLL)); + } if (isUnix) { diff --git a/doorstop/doorstop_config_il2cpp.ini b/doorstop/doorstop_config_il2cpp.ini index 2d92cca..ec3e4ce 100644 --- a/doorstop/doorstop_config_il2cpp.ini +++ b/doorstop/doorstop_config_il2cpp.ini @@ -1,18 +1,53 @@ -[UnityDoorstop] -# Specifies whether assembly executing is enabled -enabled=true -# Specifies the path (absolute, or relative to the game's exe) to the DLL/EXE that should be executed by Doorstop -targetAssembly=BepInEx\core\BepInEx.IL2CPP.dll -# Specifies whether Unity's output log should be redirected to \output_log.txt -redirectOutputLog=false +# General options for Unity Doorstop +[General] -[MonoBackend] -runtimeLib=mono\MonoBleedingEdge\EmbedRuntime\mono-2.0-sgen.dll -configDir=mono\MonoBleedingEdge\etc -corlibDir=mono\Managed -# Specifies whether the mono soft debugger is enabled -debugEnabled=false -# Specifies whether the mono soft debugger should suspend the process and wait for the remote debugger -debugSuspend=false -# Specifies the listening address the soft debugger -debugAddress=127.0.0.1:10000 \ No newline at end of file +# Enable Doorstop? +enabled=true + +# Path to the assembly to load and execute +# NOTE: The entrypoint must be of format `static void Doorstop.Entrypoint.Start()` +target_assembly=BepInEx\core\BepInEx.IL2CPP.dll + +# If true, Unity's output log is redirected to \output_log.txt +redirect_output_log=false + +# If enabled, DOORSTOP_DISABLE env var value is ignored +# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS +ignore_disable_switch=false + + +# Options specific to running under Unity Mono runtime +[UnityMono] + +# Overrides default Mono DLL search path +# Sometimes it is needed to instruct Mono to seek its assemblies from a different path +# (e.g. mscorlib is stripped in original game) +# This option causes Mono to seek mscorlib and core libraries from a different folder before Managed +# Original Managed folder is added as a secondary folder in the search path +dll_search_path_override= + +# If true, Mono debugger server will be enabled +debug_enabled=false + +# When debug_enabled is true, this option specifies whether Doorstop should initialize the debugger server +# If you experience crashes when starting the debugger on debug UnityPlayer builds, try setting this to false +debug_start_server=true + +# When debug_enabled is true, specifies the address to use for the debugger server +debug_address=127.0.0.1:10000 + +# If true and debug_enabled is true, Mono debugger server will suspend the game execution until a debugger is attached +debug_suspend=false + +# Options sepcific to running under Il2Cpp runtime +[Il2Cpp] + +# Path to coreclr.dll that contains the CoreCLR runtime +# [MonoBackend] +# runtimeLib=mono\MonoBleedingEdge\EmbedRuntime\mono-2.0-sgen.dll +coreclr_path= + +# Path to the directory containing the managed core libraries for CoreCLR (mscorlib, System, etc.) +# [MonoBackend] +# corlibDir=mono\Managed +corlib_dir= diff --git a/doorstop/doorstop_config_mono.ini b/doorstop/doorstop_config_mono.ini index 36d29a0..94390cd 100644 --- a/doorstop/doorstop_config_mono.ini +++ b/doorstop/doorstop_config_mono.ini @@ -1,7 +1,39 @@ -[UnityDoorstop] -# Specifies whether assembly executing is enabled +# General options for Unity Doorstop +[General] + +# Enable Doorstop? enabled=true -# Specifies the path (absolute, or relative to the game's exe) to the DLL/EXE that should be executed by Doorstop -targetAssembly=BepInEx\core\BepInEx.Preloader.Unity.dll -# Specifies whether Unity's output log should be redirected to \output_log.txt -redirectOutputLog=false \ No newline at end of file + +# Path to the assembly to load and execute +# NOTE: The entrypoint must be of format `static void Doorstop.Entrypoint.Start()` +target_assembly=BepInEx\core\BepInEx.Preloader.Unity.dll + +# If true, Unity's output log is redirected to \output_log.txt +redirect_output_log=false + +# If enabled, DOORSTOP_DISABLE env var value is ignored +# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS +ignore_disable_switch=false + +# Options specific to running under Unity Mono runtime +[UnityMono] + +# Overrides default Mono DLL search path +# Sometimes it is needed to instruct Mono to seek its assemblies from a different path +# (e.g. mscorlib is stripped in original game) +# This option causes Mono to seek mscorlib and core libraries from a different folder before Managed +# Original Managed folder is added as a secondary folder in the search path +dll_search_path_override= + +# If true, Mono debugger server will be enabled +debug_enabled=false + +# When debug_enabled is true, this option specifies whether Doorstop should initialize the debugger server +# If you experience crashes when starting the debugger on debug UnityPlayer builds, try setting this to false +debug_start_server=true + +# When debug_enabled is true, specifies the address to use for the debugger server +debug_address=127.0.0.1:10000 + +# If true and debug_enabled is true, Mono debugger server will suspend the game execution until a debugger is attached +debug_suspend=false