Skip to content

Commit 42b8b96

Browse files
committed
Fix NetCore for games started with dotnet executable
1 parent 9c2b17f commit 42b8b96

File tree

3 files changed

+121
-24
lines changed

3 files changed

+121
-24
lines changed

BepInEx.NetCore/BepInEx.NetCore.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFrameworks>netcoreapp3.1;net6</TargetFrameworks>
55
</PropertyGroup>
66

77
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

BepInEx.NetCore/HookEntrypoint.cs

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ internal class StartupHook
1111
{
1212
public static List<string> ResolveDirectories = new();
1313

14+
public static string DoesNotExistPath = "_doesnotexist_.exe";
15+
1416
public static void Initialize()
1517
{
16-
var silentExceptionLog = $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log";
18+
var silentExceptionLog = $"bepinex_preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log";
1719

1820
try
1921
{
20-
string filename, gameDirectory;
21-
2222
//#if DEBUG
2323
// filename =
2424
// Path.Combine(Directory.GetCurrentDirectory(),
@@ -28,25 +28,115 @@ public static void Initialize()
2828
// // for debugging within VS
2929
// ResolveDirectories.Add(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
3030
//#else
31-
filename = Process.GetCurrentProcess().MainModule.FileName;
32-
gameDirectory = Path.GetDirectoryName(filename);
33-
ResolveDirectories.Add(Path.Combine(gameDirectory, "BepInEx", "core"));
34-
//#endif
31+
32+
var executableFilename = Process.GetCurrentProcess().MainModule.FileName;
33+
34+
var assemblyFilename = TryDetermineAssemblyNameFromDotnet(executableFilename)
35+
?? TryDetermineAssemblyNameFromStubExecutable(executableFilename)
36+
?? TryDetermineAssemblyNameFromCurrentAssembly(executableFilename);
37+
38+
string gameDirectory = null;
39+
40+
if (assemblyFilename != null)
41+
gameDirectory = Path.GetDirectoryName(assemblyFilename);
3542

43+
string bepinexCoreDirectory = null;
44+
45+
if (gameDirectory != null)
46+
bepinexCoreDirectory = Path.Combine(gameDirectory, "BepInEx", "core");
47+
48+
if (assemblyFilename == null || gameDirectory == null || !Directory.Exists(bepinexCoreDirectory))
49+
{
50+
throw new Exception("Could not determine game location, or BepInEx install location");
51+
}
52+
3653
silentExceptionLog = Path.Combine(gameDirectory, silentExceptionLog);
54+
55+
ResolveDirectories.Add(bepinexCoreDirectory);
56+
//#endif
3757

3858
AppDomain.CurrentDomain.AssemblyResolve += SharedEntrypoint.RemoteResolve(ResolveDirectories);
3959

40-
NetCorePreloaderRunner.OuterMain(filename);
60+
NetCorePreloaderRunner.OuterMain(assemblyFilename);
4161
}
4262
catch (Exception ex)
4363
{
44-
File.WriteAllText(silentExceptionLog, ex.ToString());
64+
string executableLocation = null;
65+
string arguments = null;
66+
67+
try
68+
{
69+
executableLocation = Process.GetCurrentProcess().MainModule?.FileName;
70+
arguments = string.Join(' ', Environment.GetCommandLineArgs());
71+
}
72+
catch { }
73+
74+
string exceptionString = $"Unhandled fatal exception\r\n" +
75+
$"Executable location: {executableLocation ?? "<null>"}\r\n" +
76+
$"Arguments: {arguments ?? "<null>"}\r\n" +
77+
$"{ex}";
78+
79+
File.WriteAllText(silentExceptionLog, exceptionString);
4580

4681
Console.WriteLine("Unhandled exception");
82+
Console.WriteLine($"Executable location: {executableLocation ?? "<null>"}");
83+
Console.WriteLine($"Arguments: {arguments ?? "<null>"}");
4784
Console.WriteLine(ex);
4885
}
4986
}
87+
88+
private static string TryDetermineAssemblyNameFromDotnet(string executableFilename)
89+
{
90+
if (Path.GetFileNameWithoutExtension(executableFilename) == "dotnet")
91+
{
92+
// We're in a special setup that uses dotnet directly to start a .dll, instead of a .exe that launches dotnet implicitly
93+
94+
var args = Environment.GetCommandLineArgs();
95+
96+
foreach (var arg in args)
97+
{
98+
if (!arg.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
99+
&& !arg.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
100+
{
101+
continue;
102+
}
103+
104+
if (File.Exists(arg))
105+
{
106+
return Path.GetFullPath(arg);
107+
}
108+
}
109+
}
110+
111+
return null;
112+
}
113+
114+
private static string TryDetermineAssemblyNameFromStubExecutable(string executableFilename)
115+
{
116+
string dllFilename = Path.ChangeExtension(executableFilename, ".dll");
117+
118+
if (File.Exists(dllFilename))
119+
return dllFilename;
120+
121+
return null;
122+
}
123+
124+
private static string TryDetermineAssemblyNameFromCurrentAssembly(string executableFilename)
125+
{
126+
string assemblyLocation = typeof(StartupHook).Assembly.Location.Replace('/', Path.DirectorySeparatorChar);
127+
128+
string coreFolderPath = Path.GetDirectoryName(assemblyLocation);
129+
130+
if (coreFolderPath == null)
131+
return null; // throw new Exception("Could not find a valid path to the BepInEx directory");
132+
133+
string gameDirectory = Path.GetDirectoryName(Path.GetDirectoryName(coreFolderPath));
134+
135+
if (gameDirectory == null)
136+
return null; // throw new Exception("Could not find a valid path to the game directory");
137+
138+
return Path.Combine(gameDirectory, DoesNotExistPath);
139+
}
50140
}
51141

52142
namespace BepInEx.NetCore

BepInEx.NetCore/NetCorePreloader.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Diagnostics;
3-
using System.IO;
43
using BepInEx.Bootstrap;
54
using BepInEx.Logging;
65
using BepInEx.NetLauncher.Common;
@@ -20,38 +19,46 @@ public static void Start()
2019
var preloaderListener = new PreloaderConsoleListener();
2120
Logger.Listeners.Add(preloaderListener);
2221

23-
var entrypointAssemblyPath = Path.ChangeExtension(Process.GetCurrentProcess().MainModule.FileName, "dll");
22+
string entrypointAssemblyPath = !Paths.ExecutablePath.EndsWith(StartupHook.DoesNotExistPath) ? Paths.ExecutablePath : null;
2423

25-
Paths.SetExecutablePath(entrypointAssemblyPath);
26-
27-
TypeLoader.SearchDirectories.Add(Path.GetDirectoryName(entrypointAssemblyPath));
24+
TypeLoader.SearchDirectories.Add(Paths.GameRootPath);
2825

2926
Logger.Sources.Add(TraceLogSource.CreateSource());
3027

3128
ChainloaderLogHelper.PrintLogInfo(Log);
3229

33-
Log.Log(LogLevel.Info, $"CLR runtime version: {Environment.Version}");
30+
Log.LogInfo($"CLR runtime version: {Environment.Version}");
31+
32+
Log.LogInfo($"Current executable: {Process.GetCurrentProcess().MainModule.FileName}");
33+
Log.LogInfo($"Entrypoint assembly: {entrypointAssemblyPath ?? "<does not exist>"}");
34+
Log.LogInfo($"Launch arguments: {string.Join(' ', Environment.GetCommandLineArgs())}");
3435

3536

3637
AssemblyBuildInfo executableInfo;
3738

38-
using (var entrypointAssembly = AssemblyDefinition.ReadAssembly(entrypointAssemblyPath))
39-
executableInfo = AssemblyBuildInfo.DetermineInfo(entrypointAssembly);
40-
41-
Log.LogInfo($"Game executable build architecture: {executableInfo}");
39+
if (entrypointAssemblyPath != null)
40+
{
41+
using (var entrypointAssembly = AssemblyDefinition.ReadAssembly(entrypointAssemblyPath))
42+
executableInfo = AssemblyBuildInfo.DetermineInfo(entrypointAssembly);
43+
44+
Log.LogInfo($"Game executable build architecture: {executableInfo}");
45+
}
46+
else
47+
{
48+
Log.LogWarning("Game assembly is unknown, can't determine build architecture");
49+
}
4250

43-
Log.Log(LogLevel.Message, "Preloader started");
51+
Log.LogMessage("Preloader started");
4452

4553
using (var assemblyPatcher = new AssemblyPatcher())
4654
{
4755
assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath);
4856

49-
Log.Log(LogLevel.Info,
50-
$"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded");
57+
Log.LogInfo($"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded");
5158

5259
assemblyPatcher.LoadAssemblyDirectories(new[] { Paths.GameRootPath }, new[] { "dll", "exe" });
5360

54-
Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.AvailableAssemblies.Count} assemblies discovered");
61+
Log.LogInfo($"{assemblyPatcher.PatcherContext.AvailableAssemblies.Count} assemblies discovered");
5562

5663
assemblyPatcher.PatchAndLoad();
5764
}

0 commit comments

Comments
 (0)