Use harmony instead of custom patcher

Cleanup code from decompiler
This commit is contained in:
2024-11-06 10:15:39 -05:00
parent c05d904954
commit c249c8b660
6 changed files with 168 additions and 412 deletions

0
.github/workflows/build.todo vendored Normal file
View File

View File

@@ -1,43 +0,0 @@
name: build
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
schedule:
- cron: '0 6 * * 1' # weekly (every monday at 6 am UTC)
jobs:
build:
runs-on: windows-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup DepotDownloader
shell: bash
run: |
mkdir depotdownloader
cd depotdownloader
curl -L 'https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_2.7.2/DepotDownloader-windows-x64.zip' > depotdownloader.zip
unzip depotdownloader.zip
- name: setup Space Engineers
shell: bash
run: |
echo 'regex:^DedicatedServer64/' > filelist.txt
# https://steamdb.info/app/298740/depots/
depotdownloader/DepotDownloader.exe -app 298740 -depot 298741 -filelist filelist.txt -dir .
mv DedicatedServer64 .GameBinaries
- name: setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: build
run: dotnet build --configuration Release

View File

@@ -1,173 +0,0 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
namespace ClientPlugin
{
public static class MethodUtil
{
public static void SwapMethod(MethodBase source, MethodBase dest)
{
bool flag = !MethodUtil.MethodSignaturesEqual(source, dest);
if (flag)
{
throw new ArgumentException("The method signatures are not the same.", "source");
}
MethodUtil.SwapMethod(MethodUtil.GetMethodAddress(source), dest);
}
public unsafe static void SwapMethod(IntPtr srcAdr, MethodBase dest)
{
IntPtr methodAddress = MethodUtil.GetMethodAddress(dest);
bool flag = IntPtr.Size == 8;
if (flag)
{
ulong* ptr = (ulong*)srcAdr.ToPointer();
ulong* ptr2 = (ulong*)methodAddress.ToPointer();
ulong num = *ptr;
*ptr = *ptr2;
*ptr2 = num;
}
else
{
uint* ptr3 = (uint*)methodAddress.ToPointer();
uint* ptr4 = (uint*)srcAdr.ToPointer();
uint num2 = *ptr4;
*ptr4 = *ptr3;
*ptr3 = num2;
}
}
public static void ReplaceMethod(MethodBase source, MethodBase dest)
{
bool flag = !MethodUtil.MethodSignaturesEqual(source, dest);
if (flag)
{
throw new ArgumentException("The method signatures are not the same.", "source");
}
MethodUtil.ReplaceMethod(MethodUtil.GetMethodAddress(source), dest);
}
public unsafe static void ReplaceMethod(IntPtr srcAdr, MethodBase dest)
{
IntPtr methodAddress = MethodUtil.GetMethodAddress(dest);
bool flag = IntPtr.Size == 8;
if (flag)
{
ulong* ptr = (ulong*)methodAddress.ToPointer();
*ptr = (ulong)(*(long*)srcAdr.ToPointer());
}
else
{
uint* ptr2 = (uint*)methodAddress.ToPointer();
*ptr2 = *(uint*)srcAdr.ToPointer();
}
}
public unsafe static IntPtr GetMethodAddress(MethodBase method)
{
bool flag = method is DynamicMethod;
IntPtr intPtr;
if (flag)
{
intPtr = MethodUtil.GetDynamicMethodAddress(method);
}
else
{
RuntimeHelpers.PrepareMethod(method.MethodHandle);
intPtr = new IntPtr((void*)((byte*)method.MethodHandle.Value.ToPointer() + 8L));
}
return intPtr;
}
private unsafe static IntPtr GetDynamicMethodAddress(MethodBase method)
{
byte* ptr = (byte*)MethodUtil.GetDynamicMethodRuntimeHandle(method).Value.ToPointer();
bool flag = IntPtr.Size == 8;
IntPtr intPtr;
if (flag)
{
ulong* ptr2 = (ulong*)ptr;
ptr2 += 6;
intPtr = new IntPtr((void*)ptr2);
}
else
{
uint* ptr3 = (uint*)ptr;
ptr3 += 6;
intPtr = new IntPtr((void*)ptr3);
}
return intPtr;
}
private static RuntimeMethodHandle GetDynamicMethodRuntimeHandle(MethodBase method)
{
bool flag = method is DynamicMethod;
if (flag)
{
FieldInfo field = typeof(DynamicMethod).GetField("m_method", BindingFlags.Instance | BindingFlags.NonPublic);
bool flag2 = field != null;
if (flag2)
{
return (RuntimeMethodHandle)field.GetValue(method);
}
}
return method.MethodHandle;
}
private static bool MethodSignaturesEqual(MethodBase x, MethodBase y)
{
bool flag = x.CallingConvention != y.CallingConvention;
bool flag2;
if (flag)
{
flag2 = false;
}
else
{
Type methodReturnType = MethodUtil.GetMethodReturnType(x);
Type methodReturnType2 = MethodUtil.GetMethodReturnType(y);
bool flag3 = methodReturnType != methodReturnType2;
if (flag3)
{
flag2 = false;
}
else
{
ParameterInfo[] parameters = x.GetParameters();
ParameterInfo[] parameters2 = y.GetParameters();
bool flag4 = parameters.Length != parameters2.Length;
if (flag4)
{
flag2 = false;
}
else
{
for (int i = 0; i < parameters.Length; i++)
{
bool flag5 = parameters[i].ParameterType != parameters2[i].ParameterType;
if (flag5)
{
return false;
}
}
flag2 = true;
}
}
}
return flag2;
}
private static Type GetMethodReturnType(MethodBase method)
{
MethodInfo methodInfo = method as MethodInfo;
bool flag = methodInfo == null;
if (flag)
{
throw new ArgumentException("Unsupported MethodBase : " + method.GetType().Name, "method");
}
return methodInfo.ReturnType;
}
}
}

View File

@@ -1,8 +1,7 @@
using System; using System.Diagnostics;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using ClientPlugin; using HarmonyLib;
using Sandbox; using Sandbox;
using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
@@ -17,20 +16,24 @@ using VRage.GameServices;
using VRage.Plugins; using VRage.Plugins;
using VRage.Utils; using VRage.Utils;
namespace TimeoutFixPlugin namespace TimeoutFix;
public class TimeoutFix : IPlugin
{ {
public class TimeoutFix : IPlugin, IDisposable private const string Version = "v2.2";
private static readonly MethodInfo Target = typeof(MyJoinGameHelper).GetMethod(nameof(DownloadWorld), BindingFlags.Static | BindingFlags.NonPublic);
private static readonly MethodInfo Patch = typeof(TimeoutFix).GetMethod(nameof(DownloadWorld), BindingFlags.Static | BindingFlags.NonPublic);
private static readonly MethodInfo ReceiveTarget = typeof(MyJoinGameHelper).GetMethod(nameof(WorldReceived), BindingFlags.Static | BindingFlags.Public);
private static readonly MethodInfo ReceivePatch = typeof(TimeoutFix).GetMethod(nameof(WorldReceived), BindingFlags.Static | BindingFlags.NonPublic);
private static readonly FieldInfo ProgressField = typeof(MyJoinGameHelper).GetField("m_progress", BindingFlags.Static | BindingFlags.NonPublic);
private static bool _worldReceived;
private static MyGuiScreenProgressBase Progress
{ {
private static MyGuiScreenProgressBase m_progress get => (MyGuiScreenProgressBase)ProgressField.GetValue(null);
{ set => ProgressField.SetValue(null, value);
get
{
return (MyGuiScreenProgressBase)((MyGuiScreenProgressBase)TimeoutFix._progressField.GetValue(null));
}
set
{
TimeoutFix._progressField.SetValue(null, value);
}
} }
public void Dispose() public void Dispose()
@@ -39,167 +42,134 @@ namespace TimeoutFixPlugin
public void Init(object gameInstance) public void Init(object gameInstance)
{ {
MethodUtil.ReplaceMethod(TimeoutFix._patch, TimeoutFix._target); var harmony = new Harmony(nameof(TimeoutFix));
MethodUtil.ReplaceMethod(TimeoutFix._receivePatch, TimeoutFix._receiveTarget); harmony.Patch(Target, new HarmonyMethod(Patch));
harmony.Patch(ReceiveTarget, new HarmonyMethod(ReceivePatch));
} }
public void Update() public void Update()
{ {
} }
private static void DownloadWorld(MyGuiScreenProgress progress, MyMultiplayerBase multiplayer) private static bool DownloadWorld(MyGuiScreenProgress progress, MyMultiplayerBase multiplayer)
{ {
TimeoutFix._worldReceived = false; _worldReceived = false;
bool flag = progress.Text != null; if (progress.Text != null)
if (flag)
{ {
progress.Text.Clear(); progress.Text.Clear();
progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateConnectingToServer)); progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateConnectingToServer));
} }
MyLog.Default.WriteLine("World requested: Timeout fix v2.2"); MyLog.Default.WriteLine($"World requested: Timeout fix {Version}");
Stopwatch worldRequestTime = Stopwatch.StartNew();
ulong serverId = multiplayer.GetOwner(); var worldRequestTime = Stopwatch.StartNew();
bool connected = false; var serverId = multiplayer.GetOwner();
var connected = false;
progress.Tick += delegate progress.Tick += delegate
{ {
MyP2PSessionState myP2PSessionState = default(MyP2PSessionState); var myP2PSessionState = default(MyP2PSessionState);
MyGameService.Peer2Peer.GetSessionState(multiplayer.ServerId, ref myP2PSessionState); MyGameService.Peer2Peer.GetSessionState(multiplayer.ServerId, ref myP2PSessionState);
bool flag2 = !connected && myP2PSessionState.ConnectionActive;
if (flag2) if (!connected && myP2PSessionState.ConnectionActive)
{ {
MyLog.Default.WriteLine("World requested - connection alive"); MyLog.Default.WriteLine("World requested - connection alive");
connected = true; connected = true;
bool flag3 = progress.Text != null; if (progress.Text != null)
if (flag3)
{ {
progress.Text.Clear(); progress.Text.Clear();
progress.Text.AppendLine("Using Rexxar's fixed join code v2.2! :D"); progress.Text.AppendLine("Using Rexxar's fixed join code v2.2! :D");
progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateWaitingForServer)); progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateWaitingForServer));
} }
} }
bool flag4 = connected && !myP2PSessionState.ConnectionActive;
if (flag4) if (connected && !myP2PSessionState.ConnectionActive)
{ {
MyLog.Default.WriteLine("World request - connection dropped"); MyLog.Default.WriteLine("World request - connection dropped");
progress.Cancel(); progress.Cancel();
MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default(MyStringId), MyMessageBoxStyleEnum.Error); MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default, MyMessageBoxStyleEnum.Error);
MySessionLoader.UnloadAndExitToMenu(); MySessionLoader.UnloadAndExitToMenu();
} }
bool flag5 = MyScreenManager.IsScreenOnTop((MyGuiScreenBase)progress);
bool flag6 = serverId != multiplayer.GetOwner(); var progressScreenOnTop = MyScreenManager.IsScreenOnTop(progress);
if (flag6) if (serverId != multiplayer.GetOwner())
{ {
MyLog.Default.WriteLine(string.Format("World requested - failed, server changed: Expected {0} got {1}", serverId, multiplayer.GetOwner())); MyLog.Default.WriteLine(string.Format("World requested - failed, server changed: Expected {0} got {1}", serverId, multiplayer.GetOwner()));
progress.Cancel(); progress.Cancel();
MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorServerHasLeft, default(MyStringId), MyMessageBoxStyleEnum.Error); MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorServerHasLeft, default, MyMessageBoxStyleEnum.Error);
multiplayer.Dispose(); multiplayer.Dispose();
} }
bool flag7 = MyScreenManager.IsScreenOfTypeOpen(typeof(MyGuiScreenDownloadMods));
bool flag8 = !flag7 && flag5 && !worldRequestTime.IsRunning; var downloadScreenOpen = MyScreenManager.IsScreenOfTypeOpen(typeof(MyGuiScreenDownloadMods));
if (flag8) if (!downloadScreenOpen && progressScreenOnTop && !worldRequestTime.IsRunning)
{ {
worldRequestTime.Start(); worldRequestTime.Start();
} }
else else
{ {
bool flag9 = flag7 || (!flag5 && worldRequestTime.IsRunning); if (downloadScreenOpen || !progressScreenOnTop && worldRequestTime.IsRunning)
if (flag9)
{
worldRequestTime.Stop(); worldRequestTime.Stop();
} }
}
bool flag10 = flag7 && progress.Visible; if (downloadScreenOpen && progress.Visible)
if (flag10)
{ {
progress.HideScreen(); progress.HideScreen();
} }
else else
{ {
bool flag11 = !flag7 && !progress.Visible; if (!downloadScreenOpen && !progress.Visible)
if (flag11)
{
progress.UnhideScreen(); progress.UnhideScreen();
} }
} if (!_worldReceived && worldRequestTime.IsRunning && worldRequestTime.ElapsedTicks / Stopwatch.Frequency > 120f)
bool flag12 = !TimeoutFix._worldReceived && worldRequestTime.IsRunning && (float)(worldRequestTime.ElapsedTicks / Stopwatch.Frequency) > 120f;
if (flag12)
{ {
MyLog.Default.WriteLine("World requested - failed, timeout reached"); MyLog.Default.WriteLine("World requested - failed, timeout reached");
MyLog.Default.WriteLine(string.Format("Elapsed : {0:N2}", worldRequestTime.ElapsedTicks / Stopwatch.Frequency)); MyLog.Default.WriteLine(string.Format("Elapsed : {0:N2}", worldRequestTime.ElapsedTicks / Stopwatch.Frequency));
progress.Cancel(); progress.Cancel();
MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default(MyStringId), MyMessageBoxStyleEnum.Error); MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default, MyMessageBoxStyleEnum.Error);
MySessionLoader.UnloadAndExitToMenu(); MySessionLoader.UnloadAndExitToMenu();
} }
}; };
multiplayer.DownloadWorld(MyFinalBuildConstants.APP_VERSION.Version); multiplayer.DownloadWorld(MyFinalBuildConstants.APP_VERSION.Version);
return false;
} }
private static void CheckDx11AndJoin(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) private static void CheckDx11AndJoin(MyObjectBuilder_World world, MyMultiplayerBase multiplayer)
{ {
bool scenario = multiplayer.Scenario; if (multiplayer.Scenario)
if (scenario)
{
MySessionLoader.LoadMultiplayerScenarioWorld(world, multiplayer); MySessionLoader.LoadMultiplayerScenarioWorld(world, multiplayer);
}
else else
{
MySessionLoader.LoadMultiplayerSession(world, multiplayer); MySessionLoader.LoadMultiplayerSession(world, multiplayer);
} }
}
private static void WorldReceived(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) private static bool WorldReceived(MyObjectBuilder_World world, MyMultiplayerBase multiplayer)
{ {
bool flag = world == null; if (world == null)
if (flag)
{ {
MyLog.Default.WriteLine("World requested - failed, version mismatch"); MyLog.Default.WriteLine("World requested - failed, version mismatch");
TimeoutFix.m_progress.Cancel(); Progress.Cancel();
TimeoutFix.m_progress = null; Progress = null;
MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorAppVersionMismatch, default(MyStringId), MyMessageBoxStyleEnum.Error); MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorAppVersionMismatch, default, MyMessageBoxStyleEnum.Error);
multiplayer.Dispose(); multiplayer.Dispose();
} }
else else
{ {
TimeoutFix._worldReceived = true; _worldReceived = true;
MyLog.Default.WriteLine("World requested - world data received"); MyLog.Default.WriteLine("World requested - world data received");
bool flag2;
if (world == null) if (world.Checkpoint?.Settings != null && !MySandboxGame.Config.ExperimentalMode)
{
flag2 = null != null;
}
else
{
MyObjectBuilder_Checkpoint checkpoint = world.Checkpoint;
flag2 = ((checkpoint != null) ? checkpoint.Settings : null) != null;
}
bool flag3 = flag2 && !MySandboxGame.Config.ExperimentalMode;
if (flag3)
{ {
MySessionLoader.UnloadAndExitToMenu(); MySessionLoader.UnloadAndExitToMenu();
StringBuilder stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
stringBuilder.AppendFormat(MyCommonTexts.DialogTextJoinWorldFailed, MyTexts.GetString(MyCommonTexts.MultiplayerErrorExperimental)); stringBuilder.AppendFormat(MyCommonTexts.DialogTextJoinWorldFailed, MyTexts.GetString(MyCommonTexts.MultiplayerErrorExperimental));
MyGuiSandbox.AddScreen((MyGuiScreenBase)MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, stringBuilder, MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), null, null, null, null, null, 0, MyGuiScreenMessageBox.ResultEnum.YES, true, null, true, null, true, false, null)); MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, stringBuilder, MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), null, null, null, null, null, 0, MyGuiScreenMessageBox.ResultEnum.YES, true, null, true, null, true, false, null));
} }
else else
{ {
TimeoutFix.m_progress = null; Progress = null;
TimeoutFix.CheckDx11AndJoin(world, multiplayer); CheckDx11AndJoin(world, multiplayer);
}
} }
} }
private static MethodInfo _target = typeof(MyJoinGameHelper).GetMethod("DownloadWorld", BindingFlags.Static | BindingFlags.NonPublic); return false;
private static MethodInfo _patch = typeof(TimeoutFix).GetMethod("DownloadWorld", BindingFlags.Static | BindingFlags.NonPublic);
private static MethodInfo _receiveTarget = typeof(MyJoinGameHelper).GetMethod("WorldReceived", BindingFlags.Static | BindingFlags.Public);
private static MethodInfo _receivePatch = typeof(TimeoutFix).GetMethod("WorldReceived", BindingFlags.Static | BindingFlags.NonPublic);
private static bool _worldReceived;
private static readonly FieldInfo _progressField = typeof(MyJoinGameHelper).GetField("m_progress", BindingFlags.Static | BindingFlags.NonPublic);
private const string VERSION = "v2.2";
} }
} }

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net48</TargetFramework> <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -16,29 +16,6 @@
<PdbPath>none</PdbPath> <PdbPath>none</PdbPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Sandbox.Game"> <PackageReference Include="CringePlugins" Version="0.1.32" />
<HintPath>.GameBinaries\Sandbox.Game.dll</HintPath>
</Reference>
<Reference Include="Sandbox.Graphics">
<HintPath>.GameBinaries\Sandbox.Graphics.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="VRage">
<HintPath>.GameBinaries\VRage.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Game">
<HintPath>.GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Library">
<HintPath>.GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Math">
<HintPath>.GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

25
TimeoutFix.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimeoutFix", "TimeoutFix.csproj", "{3A616A98-0D6E-466D-8885-8D3A2E494753}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3A616A98-0D6E-466D-8885-8D3A2E494753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A616A98-0D6E-466D-8885-8D3A2E494753}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A616A98-0D6E-466D-8885-8D3A2E494753}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A616A98-0D6E-466D-8885-8D3A2E494753}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5CAAAB50-CF4A-4F5C-BF13-14F1F84F1753}
EndGlobalSection
EndGlobal