Skip to content

Commit

Permalink
Merge pull request #100 from BepInEx/5.0-rc2
Browse files Browse the repository at this point in the history
5.0 RC2 changes
  • Loading branch information
bbepis committed Nov 10, 2019
2 parents 7a5ee6f + ef3dae4 commit cb78681
Show file tree
Hide file tree
Showing 20 changed files with 409 additions and 163 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
@@ -1,6 +1,3 @@
[submodule "submodules/BepInEx.Harmony"]
path = submodules/BepInEx.Harmony
url = https://github.com/BepInEx/BepInEx.Harmony
[submodule "submodules/MonoMod"]
path = submodules/MonoMod
url = https://github.com/MonoMod/MonoMod.git
27 changes: 17 additions & 10 deletions BepInEx.Preloader/BepInEx.Preloader.csproj
Expand Up @@ -23,8 +23,23 @@
<DocumentationFile>..\bin\BepInEx.Preloader.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Cecil, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.dll</HintPath>
<Reference Include="Mono.Cecil, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Mdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Pdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Rocks.dll</HintPath>
</Reference>
<Reference Include="MonoMod.RuntimeDetour, Version=19.11.5.1, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MonoMod.RuntimeDetour.19.11.5.1\lib\net35\MonoMod.RuntimeDetour.dll</HintPath>
</Reference>
<Reference Include="MonoMod.Utils, Version=19.11.5.1, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MonoMod.Utils.19.11.5.1\lib\net35\MonoMod.Utils.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand Down Expand Up @@ -53,14 +68,6 @@
<Project>{a15d6ee6-f954-415b-8605-8a8470cc87dc}</Project>
<Name>Harmony</Name>
</ProjectReference>
<ProjectReference Include="..\submodules\MonoMod\MonoMod.RuntimeDetour\MonoMod.RuntimeDetour.csproj">
<Project>{d0c584c0-81d7-486e-b70e-d7f9256e0909}</Project>
<Name>MonoMod.RuntimeDetour</Name>
</ProjectReference>
<ProjectReference Include="..\submodules\MonoMod\MonoMod.Utils\MonoMod.Utils.csproj">
<Project>{1839cfe2-3db0-45a8-b03d-9aa797479a3a}</Project>
<Name>MonoMod.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
38 changes: 28 additions & 10 deletions BepInEx.Preloader/Entrypoint.cs
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -48,20 +49,32 @@ internal static class Entrypoint
/// </param>
public static void Main(string[] args)
{
EnvVars.LoadVars();
// We set it to the current directory first as a fallback, but try to use the same location as the .exe file.
string silentExceptionLog = $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log";

// 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));
try
{
EnvVars.LoadVars();

silentExceptionLog = Path.Combine(GetCurrentProcessDirectory(), silentExceptionLog);

AppDomain.CurrentDomain.AssemblyResolve += ResolveCurrentDirectory;
// 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));

// 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(Entrypoint).Assembly.GetType($"BepInEx.Preloader.{nameof(PreloaderRunner)}")
?.GetMethod(nameof(PreloaderRunner.PreloaderMain))
?.Invoke(null, new object[] { args });
AppDomain.CurrentDomain.AssemblyResolve += ResolveCurrentDirectory;

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(Entrypoint).Assembly.GetType($"BepInEx.Preloader.{nameof(PreloaderRunner)}")
?.GetMethod(nameof(PreloaderRunner.PreloaderMain))
?.Invoke(null, new object[] { args });

AppDomain.CurrentDomain.AssemblyResolve -= ResolveCurrentDirectory;
}
catch (Exception ex)
{
File.WriteAllText(silentExceptionLog, ex.ToString());
}
}

private static Assembly ResolveCurrentDirectory(object sender, ResolveEventArgs args)
Expand All @@ -77,5 +90,10 @@ private static Assembly ResolveCurrentDirectory(object sender, ResolveEventArgs
return null;
}
}

private static string GetCurrentProcessDirectory()
{
return Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
}
}
}
2 changes: 1 addition & 1 deletion BepInEx.Preloader/Logger/PreloaderLogWriter.cs
Expand Up @@ -9,7 +9,7 @@ namespace BepInEx.Preloader
{
public class PreloaderConsoleListener : ILogListener
{
public List<LogEventArgs> LogEvents { get; } = new List<LogEventArgs>();
public static List<LogEventArgs> LogEvents { get; } = new List<LogEventArgs>();
protected StringBuilder LogBuilder = new StringBuilder();

public static TextWriter StandardOut { get; set; }
Expand Down
6 changes: 3 additions & 3 deletions BepInEx.Preloader/Patching/AssemblyPatcher.cs
Expand Up @@ -283,17 +283,17 @@ public static void Load(AssemblyDefinition assembly, string filename)

#region Config

private static readonly ConfigEntry<bool> ConfigDumpAssemblies = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigDumpAssemblies = ConfigFile.CoreConfig.Bind(
"Preloader", "DumpAssemblies",
false,
"If enabled, BepInEx will save patched assemblies into BepInEx/DumpedAssemblies.\nThis can be used by developers to inspect and debug preloader patchers.");

private static readonly ConfigEntry<bool> ConfigLoadDumpedAssemblies = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigLoadDumpedAssemblies = ConfigFile.CoreConfig.Bind(
"Preloader", "LoadDumpedAssemblies",
false,
"If enabled, BepInEx will load patched assemblies from BepInEx/DumpedAssemblies instead of memory.\nThis can be used to be able to load patched assemblies into debuggers like dnSpy.\nIf set to true, will override DumpAssemblies.");

private static readonly ConfigEntry<bool> ConfigBreakBeforeLoadAssemblies = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigBreakBeforeLoadAssemblies = ConfigFile.CoreConfig.Bind(
"Preloader", "BreakBeforeLoadAssemblies",
false,
"If enabled, BepInEx will call Debugger.Break() once before loading patched assemblies.\nThis can be used with debuggers like dnSpy to install breakpoints into patched assemblies before they are loaded.");
Expand Down
20 changes: 14 additions & 6 deletions BepInEx.Preloader/Preloader.cs
Expand Up @@ -8,6 +8,7 @@
using BepInEx.Logging;
using BepInEx.Preloader.Patching;
using BepInEx.Preloader.RuntimeFixes;
using HarmonyLib;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.RuntimeDetour;
Expand Down Expand Up @@ -198,10 +199,17 @@ public static void PatchEntrypoint(ref AssemblyDefinition assembly)

il.InsertBefore(ins,
il.Create(OpCodes.Ldnull)); // gameExePath (always null, we initialize the Paths class in Entrypoint

il.InsertBefore(ins,
il.Create(OpCodes.Ldc_I4_0)); //startConsole (always false, we already load the console in Preloader)

il.InsertBefore(ins,
il.Create(OpCodes.Call, assembly.MainModule.ImportReference(
AccessTools.PropertyGetter(typeof(PreloaderConsoleListener), nameof(PreloaderConsoleListener.LogEvents))))); // preloaderLogEvents (load from Preloader.PreloaderLog.LogEvents)

il.InsertBefore(ins,
il.Create(OpCodes.Call, initMethod)); // Chainloader.Initialize(string gamePath, string managedPath = null, bool startConsole = true)

il.InsertBefore(ins,
il.Create(OpCodes.Call, startMethod));
}
Expand Down Expand Up @@ -237,32 +245,32 @@ public static void AllocateConsole()

#region Config

private static readonly ConfigEntry<string> ConfigEntrypointAssembly = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<string> ConfigEntrypointAssembly = ConfigFile.CoreConfig.Bind(
"Preloader.Entrypoint", "Assembly",
IsPostUnity2017 ? "UnityEngine.CoreModule.dll" : "UnityEngine.dll",
"The local filename of the assembly to target.");

private static readonly ConfigEntry<string> ConfigEntrypointType = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<string> ConfigEntrypointType = ConfigFile.CoreConfig.Bind(
"Preloader.Entrypoint", "Type",
"Application",
"The name of the type in the entrypoint assembly to search for the entrypoint method.");

private static readonly ConfigEntry<string> ConfigEntrypointMethod = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<string> ConfigEntrypointMethod = ConfigFile.CoreConfig.Bind(
"Preloader.Entrypoint", "Method",
".cctor",
"The name of the method in the specified entrypoint assembly and type to hook and load Chainloader from.");

private static readonly ConfigEntry<bool> ConfigApplyRuntimePatches = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigApplyRuntimePatches = ConfigFile.CoreConfig.Bind(
"Preloader", "ApplyRuntimePatches",
true,
"Enables or disables runtime patches.\nThis should always be true, unless you cannot start the game due to a Harmony related issue (such as running .NET Standard runtime) or you know what you're doing.");

private static readonly ConfigEntry<bool> ConfigShimHarmony = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigShimHarmony = ConfigFile.CoreConfig.Bind(
"Preloader", "ShimHarmonySupport",
!Utility.CLRSupportsDynamicAssemblies,
"If enabled, basic Harmony functionality is patched to use MonoMod's RuntimeDetour instead.\nTry using this if Harmony does not work in a game.");

private static readonly ConfigEntry<bool> ConfigPreloaderCOutLogging = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigPreloaderCOutLogging = ConfigFile.CoreConfig.Bind(
"Logging", "PreloaderConsoleOutRedirection",
true,
"Redirects text from Console.Out during preloader patch loading to the BepInEx logging system.");
Expand Down
5 changes: 3 additions & 2 deletions BepInEx.Preloader/packages.config
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>

<packages>
<package id="Mono.Cecil" version="0.10.3" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
<package id="MonoMod.RuntimeDetour" version="19.11.5.1" targetFramework="net35" />
<package id="MonoMod.Utils" version="19.11.5.1" targetFramework="net35" />
</packages>
14 changes: 0 additions & 14 deletions BepInEx.sln
Expand Up @@ -23,10 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Patcher", "Patcher", "{A907
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.Preloader", "BepInEx.Preloader\BepInEx.Preloader.csproj", "{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour", "submodules\MonoMod\MonoMod.RuntimeDetour\MonoMod.RuntimeDetour.csproj", "{D0C584C0-81D7-486E-B70E-D7F9256E0909}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Utils", "submodules\MonoMod\MonoMod.Utils\MonoMod.Utils.csproj", "{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInExTests", "BepInExTests\BepInExTests.csproj", "{E7CD429A-D057-48E3-8C51-E5C934E8E07B}"
ProjectSection(ProjectDependencies) = postProject
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} = {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}
Expand Down Expand Up @@ -62,14 +58,6 @@ Global
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.Build.0 = Release|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.ActiveCfg = Debug|x86
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.Build.0 = Debug|x86
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.ActiveCfg = Release|x86
Expand All @@ -82,8 +70,6 @@ Global
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB} = {A9071994-3533-4C1B-89DC-D817B676AB41}
{54161CFE-FF42-4DDE-B161-3A49545DB5CD} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
{A15D6EE6-F954-415B-8605-8A8470CC87DC} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
{D0C584C0-81D7-486E-B70E-D7F9256E0909} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {55AC11EF-F568-4C79-A356-7ED9510145B1}
Expand Down
13 changes: 11 additions & 2 deletions BepInEx/BepInEx.csproj
Expand Up @@ -39,8 +39,17 @@
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Cecil, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.dll</HintPath>
<Reference Include="Mono.Cecil, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Mdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Pdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.10.4.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.4\lib\net35\Mono.Cecil.Rocks.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="UnityEngine">
Expand Down
38 changes: 30 additions & 8 deletions BepInEx/Bootstrap/Chainloader.cs
Expand Up @@ -33,7 +33,7 @@ public static List<BaseUnityPlugin> Plugins
lock (_plugins)
{
_plugins.RemoveAll(x => x == null);
return _plugins;
return _plugins.ToList();
}
}
}
Expand All @@ -52,7 +52,7 @@ public static List<BaseUnityPlugin> Plugins
/// <summary>
/// Initializes BepInEx to be able to start the chainloader.
/// </summary>
public static void Initialize(string gameExePath, bool startConsole = true)
public static void Initialize(string gameExePath, bool startConsole = true, ICollection<LogEventArgs> preloaderLogEvents = null)
{
if (_initialized)
return;
Expand Down Expand Up @@ -96,6 +96,27 @@ public static void Initialize(string gameExePath, bool startConsole = true)
Logger.Sources.Add(new UnityLogSource());


// Temporarily disable the console log listener as we replay the preloader logs

var logListener = Logger.Listeners.FirstOrDefault(logger => logger is ConsoleLogListener);

if (logListener != null)
Logger.Listeners.Remove(logListener);

var preloaderLogSource = Logger.CreateLogSource("Preloader");

foreach (var preloaderLogEvent in preloaderLogEvents)
{
preloaderLogSource.Log(preloaderLogEvent.Level, preloaderLogEvent.Data);
}

Logger.Sources.Remove(preloaderLogSource);

if (logListener != null)
Logger.Listeners.Add(logListener);



Logger.LogMessage("Chainloader ready");

_initialized = true;
Expand Down Expand Up @@ -210,7 +231,8 @@ public static void Start()

Logger.LogInfo($"{pluginInfos.Count} plugins to load");

var dependencyDict = new Dictionary<string, IEnumerable<string>>();
// We use a sorted dictionary to ensure consistent load order
var dependencyDict = new SortedDictionary<string, IEnumerable<string>>(StringComparer.InvariantCultureIgnoreCase);
var pluginsByGUID = new Dictionary<string, PluginInfo>();

foreach (var pluginInfoGroup in pluginInfos.GroupBy(info => info.Metadata.GUID))
Expand Down Expand Up @@ -357,27 +379,27 @@ public static void Start()
#region Config


private static readonly ConfigEntry<bool> ConfigUnityLogging = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigUnityLogging = ConfigFile.CoreConfig.Bind(
"Logging", "UnityLogListening",
true,
"Enables showing unity log messages in the BepInEx logging system.");

private static readonly ConfigEntry<bool> ConfigDiskWriteUnityLog = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigDiskWriteUnityLog = ConfigFile.CoreConfig.Bind(
"Logging.Disk", "WriteUnityLog",
false,
"Include unity log messages in log file output.");

private static readonly ConfigEntry<bool> ConfigDiskAppend = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigDiskAppend = ConfigFile.CoreConfig.Bind(
"Logging.Disk", "AppendLog",
false,
"Appends to the log file instead of overwriting, on game startup.");

private static readonly ConfigEntry<bool> ConfigDiskLogging = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> ConfigDiskLogging = ConfigFile.CoreConfig.Bind(
"Logging.Disk", "Enabled",
true,
"Enables writing log messages to disk.");

private static readonly ConfigEntry<LogLevel> ConfigDiskConsoleDisplayedLevel = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<LogLevel> ConfigDiskConsoleDisplayedLevel = ConfigFile.CoreConfig.Bind(
"Logging.Disk", "DisplayedLogLevel",
LogLevel.Info,
"Only displays the specified log level and above in the console output.");
Expand Down
2 changes: 1 addition & 1 deletion BepInEx/Bootstrap/TypeLoader.cs
Expand Up @@ -251,7 +251,7 @@ public static string TypeLoadExceptionToString(ReflectionTypeLoadException ex)

#region Config

private static readonly ConfigEntry<bool> EnableAssemblyCache = ConfigFile.CoreConfig.AddSetting(
private static readonly ConfigEntry<bool> EnableAssemblyCache = ConfigFile.CoreConfig.Bind(
"Caching", "EnableAssemblyCache",
true,
"Enable/disable assembly metadata cache\nEnabling this will speed up discovery of plugins and patchers by caching the metadata of all types BepInEx discovers.");
Expand Down

0 comments on commit cb78681

Please sign in to comment.