diff --git a/.github/workflows/build.todo b/.github/workflows/build.todo new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 63f7500..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -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 diff --git a/ClientPlugin.cs b/ClientPlugin.cs deleted file mode 100644 index 5d031c8..0000000 --- a/ClientPlugin.cs +++ /dev/null @@ -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; - } - } -} diff --git a/TimeoutFix.cs b/TimeoutFix.cs index 5285b94..06f766f 100644 --- a/TimeoutFix.cs +++ b/TimeoutFix.cs @@ -1,8 +1,7 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Reflection; using System.Text; -using ClientPlugin; +using HarmonyLib; using Sandbox; using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Networking; @@ -17,189 +16,160 @@ using VRage.GameServices; using VRage.Plugins; 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); + } + + public void Dispose() + { + } + + public void Init(object gameInstance) + { + var harmony = new Harmony(nameof(TimeoutFix)); + harmony.Patch(Target, new HarmonyMethod(Patch)); + harmony.Patch(ReceiveTarget, new HarmonyMethod(ReceivePatch)); + } + + public void Update() + { + } + + private static bool DownloadWorld(MyGuiScreenProgress progress, MyMultiplayerBase multiplayer) + { + _worldReceived = false; + if (progress.Text != null) { - get + progress.Text.Clear(); + progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateConnectingToServer)); + } + MyLog.Default.WriteLine($"World requested: Timeout fix {Version}"); + + var worldRequestTime = Stopwatch.StartNew(); + var serverId = multiplayer.GetOwner(); + var connected = false; + + progress.Tick += delegate + { + var myP2PSessionState = default(MyP2PSessionState); + MyGameService.Peer2Peer.GetSessionState(multiplayer.ServerId, ref myP2PSessionState); + + if (!connected && myP2PSessionState.ConnectionActive) { - return (MyGuiScreenProgressBase)((MyGuiScreenProgressBase)TimeoutFix._progressField.GetValue(null)); + MyLog.Default.WriteLine("World requested - connection alive"); + connected = true; + if (progress.Text != null) + { + progress.Text.Clear(); + progress.Text.AppendLine("Using Rexxar's fixed join code v2.2! :D"); + progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateWaitingForServer)); + } } - set + + if (connected && !myP2PSessionState.ConnectionActive) { - TimeoutFix._progressField.SetValue(null, value); + MyLog.Default.WriteLine("World request - connection dropped"); + progress.Cancel(); + MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default, MyMessageBoxStyleEnum.Error); + MySessionLoader.UnloadAndExitToMenu(); } - } - public void Dispose() - { - } - - public void Init(object gameInstance) - { - MethodUtil.ReplaceMethod(TimeoutFix._patch, TimeoutFix._target); - MethodUtil.ReplaceMethod(TimeoutFix._receivePatch, TimeoutFix._receiveTarget); - } - - public void Update() - { - } - - private static void DownloadWorld(MyGuiScreenProgress progress, MyMultiplayerBase multiplayer) - { - TimeoutFix._worldReceived = false; - bool flag = progress.Text != null; - if (flag) + var progressScreenOnTop = MyScreenManager.IsScreenOnTop(progress); + if (serverId != multiplayer.GetOwner()) { - progress.Text.Clear(); - progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateConnectingToServer)); - } - MyLog.Default.WriteLine("World requested: Timeout fix v2.2"); - Stopwatch worldRequestTime = Stopwatch.StartNew(); - ulong serverId = multiplayer.GetOwner(); - bool connected = false; - progress.Tick += delegate - { - MyP2PSessionState myP2PSessionState = default(MyP2PSessionState); - MyGameService.Peer2Peer.GetSessionState(multiplayer.ServerId, ref myP2PSessionState); - bool flag2 = !connected && myP2PSessionState.ConnectionActive; - if (flag2) - { - MyLog.Default.WriteLine("World requested - connection alive"); - connected = true; - bool flag3 = progress.Text != null; - if (flag3) - { - progress.Text.Clear(); - progress.Text.AppendLine("Using Rexxar's fixed join code v2.2! :D"); - progress.Text.Append(MyTexts.Get(MyCommonTexts.MultiplayerStateWaitingForServer)); - } - } - bool flag4 = connected && !myP2PSessionState.ConnectionActive; - if (flag4) - { - MyLog.Default.WriteLine("World request - connection dropped"); - progress.Cancel(); - MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default(MyStringId), MyMessageBoxStyleEnum.Error); - MySessionLoader.UnloadAndExitToMenu(); - } - bool flag5 = MyScreenManager.IsScreenOnTop((MyGuiScreenBase)progress); - bool flag6 = serverId != multiplayer.GetOwner(); - if (flag6) - { - MyLog.Default.WriteLine(string.Format("World requested - failed, server changed: Expected {0} got {1}", serverId, multiplayer.GetOwner())); - progress.Cancel(); - MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorServerHasLeft, default(MyStringId), MyMessageBoxStyleEnum.Error); - multiplayer.Dispose(); - } - bool flag7 = MyScreenManager.IsScreenOfTypeOpen(typeof(MyGuiScreenDownloadMods)); - bool flag8 = !flag7 && flag5 && !worldRequestTime.IsRunning; - if (flag8) - { - worldRequestTime.Start(); - } - else - { - bool flag9 = flag7 || (!flag5 && worldRequestTime.IsRunning); - if (flag9) - { - worldRequestTime.Stop(); - } - } - bool flag10 = flag7 && progress.Visible; - if (flag10) - { - progress.HideScreen(); - } - else - { - bool flag11 = !flag7 && !progress.Visible; - if (flag11) - { - progress.UnhideScreen(); - } - } - 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(string.Format("Elapsed : {0:N2}", worldRequestTime.ElapsedTicks / Stopwatch.Frequency)); - progress.Cancel(); - MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default(MyStringId), MyMessageBoxStyleEnum.Error); - MySessionLoader.UnloadAndExitToMenu(); - } - }; - multiplayer.DownloadWorld(MyFinalBuildConstants.APP_VERSION.Version); - } - - private static void CheckDx11AndJoin(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) - { - bool scenario = multiplayer.Scenario; - if (scenario) - { - MySessionLoader.LoadMultiplayerScenarioWorld(world, multiplayer); - } - else - { - MySessionLoader.LoadMultiplayerSession(world, multiplayer); - } - } - - private static void WorldReceived(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) - { - bool flag = world == null; - if (flag) - { - MyLog.Default.WriteLine("World requested - failed, version mismatch"); - TimeoutFix.m_progress.Cancel(); - TimeoutFix.m_progress = null; - MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorAppVersionMismatch, default(MyStringId), MyMessageBoxStyleEnum.Error); + MyLog.Default.WriteLine(string.Format("World requested - failed, server changed: Expected {0} got {1}", serverId, multiplayer.GetOwner())); + progress.Cancel(); + MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorServerHasLeft, default, MyMessageBoxStyleEnum.Error); multiplayer.Dispose(); } + + var downloadScreenOpen = MyScreenManager.IsScreenOfTypeOpen(typeof(MyGuiScreenDownloadMods)); + if (!downloadScreenOpen && progressScreenOnTop && !worldRequestTime.IsRunning) + { + worldRequestTime.Start(); + } else { - TimeoutFix._worldReceived = true; - MyLog.Default.WriteLine("World requested - world data received"); - bool flag2; - if (world == null) - { - 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(); - StringBuilder stringBuilder = new StringBuilder(); - 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)); - } - else - { - TimeoutFix.m_progress = null; - TimeoutFix.CheckDx11AndJoin(world, multiplayer); - } + if (downloadScreenOpen || !progressScreenOnTop && worldRequestTime.IsRunning) + worldRequestTime.Stop(); + } + + if (downloadScreenOpen && progress.Visible) + { + progress.HideScreen(); + } + else + { + if (!downloadScreenOpen && !progress.Visible) + progress.UnhideScreen(); + } + if (!_worldReceived && worldRequestTime.IsRunning && worldRequestTime.ElapsedTicks / Stopwatch.Frequency > 120f) + { + MyLog.Default.WriteLine("World requested - failed, timeout reached"); + MyLog.Default.WriteLine(string.Format("Elapsed : {0:N2}", worldRequestTime.ElapsedTicks / Stopwatch.Frequency)); + progress.Cancel(); + MyGuiSandbox.Show(MyCommonTexts.MultiplaterJoin_ServerIsNotResponding, default, MyMessageBoxStyleEnum.Error); + MySessionLoader.UnloadAndExitToMenu(); + } + }; + multiplayer.DownloadWorld(MyFinalBuildConstants.APP_VERSION.Version); + + return false; + } + + private static void CheckDx11AndJoin(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) + { + if (multiplayer.Scenario) + MySessionLoader.LoadMultiplayerScenarioWorld(world, multiplayer); + else + MySessionLoader.LoadMultiplayerSession(world, multiplayer); + } + + private static bool WorldReceived(MyObjectBuilder_World world, MyMultiplayerBase multiplayer) + { + if (world == null) + { + MyLog.Default.WriteLine("World requested - failed, version mismatch"); + Progress.Cancel(); + Progress = null; + MyGuiSandbox.Show(MyCommonTexts.MultiplayerErrorAppVersionMismatch, default, MyMessageBoxStyleEnum.Error); + multiplayer.Dispose(); + } + else + { + _worldReceived = true; + MyLog.Default.WriteLine("World requested - world data received"); + + if (world.Checkpoint?.Settings != null && !MySandboxGame.Config.ExperimentalMode) + { + MySessionLoader.UnloadAndExitToMenu(); + var stringBuilder = new StringBuilder(); + stringBuilder.AppendFormat(MyCommonTexts.DialogTextJoinWorldFailed, MyTexts.GetString(MyCommonTexts.MultiplayerErrorExperimental)); + 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 + { + Progress = null; + CheckDx11AndJoin(world, multiplayer); } } - private static MethodInfo _target = typeof(MyJoinGameHelper).GetMethod("DownloadWorld", BindingFlags.Static | BindingFlags.NonPublic); - - 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"; + return false; } } diff --git a/TimeoutFix.csproj b/TimeoutFix.csproj index c8b3dca..0f40d3d 100644 --- a/TimeoutFix.csproj +++ b/TimeoutFix.csproj @@ -1,6 +1,6 @@ - net48 + net8.0-windows10.0.19041.0 Release Library true @@ -16,29 +16,6 @@ none - - .GameBinaries\Sandbox.Game.dll - - - .GameBinaries\Sandbox.Graphics.dll - - - - - .GameBinaries\VRage.dll - False - - - .GameBinaries\VRage.Game.dll - False - - - .GameBinaries\VRage.Library.dll - False - - - .GameBinaries\VRage.Math.dll - False - + \ No newline at end of file diff --git a/TimeoutFix.sln b/TimeoutFix.sln new file mode 100644 index 0000000..af8e2a4 --- /dev/null +++ b/TimeoutFix.sln @@ -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