diff --git a/Source/Client/Desyncs/ClientSyncOpinion.cs b/Source/Client/Desyncs/ClientSyncOpinion.cs index 0ab3482f3..cbc052ccf 100644 --- a/Source/Client/Desyncs/ClientSyncOpinion.cs +++ b/Source/Client/Desyncs/ClientSyncOpinion.cs @@ -53,7 +53,7 @@ join map2 in other.mapStates on map1.mapId equals map2.mapId if (!commandRandomStates.SequenceEqual(other.commandRandomStates)) return "Random state from commands doesn't match"; - if (!simulating && !other.simulating && desyncStackTraceHashes.Any() && other.desyncStackTraceHashes.Any() && !desyncStackTraceHashes.SequenceEqual(other.desyncStackTraceHashes)) + if (Native.IsMonoRuntime && !simulating && !other.simulating && desyncStackTraceHashes.Any() && other.desyncStackTraceHashes.Any() && !desyncStackTraceHashes.SequenceEqual(other.desyncStackTraceHashes)) return "Trace hashes don't match"; return null; diff --git a/Source/Client/Desyncs/DeferredStackTracing.cs b/Source/Client/Desyncs/DeferredStackTracing.cs index db53da2df..4b9ad7464 100644 --- a/Source/Client/Desyncs/DeferredStackTracing.cs +++ b/Source/Client/Desyncs/DeferredStackTracing.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Diagnostics; +using System.Text; using System.Reflection; using System.Runtime.InteropServices; using HarmonyLib; @@ -31,10 +33,31 @@ static IEnumerable TargetMethods() public static void Postfix() { - if (Native.LmfPtr == 0) return; + if (Native.IsMonoRuntime && Native.LmfPtr == 0) return; if (!ShouldAddStackTraceForDesyncLog()) return; var logItem = StackTraceLogItemRaw.GetFromPool(); + if (!Native.IsMonoRuntime) + { + // Managed fallback: never interpret CoreCLR addresses as Mono frames. + var frames = new StackTrace(1, false).GetFrames(); + var text = new StringBuilder(); + int managedHash = 17; + int managedDepth = System.Math.Min(frames?.Length ?? 0, StackTraceLogItemRaw.MaxDepth); + for (int i = 0; i < managedDepth; i++) + { + var method = frames[i].GetMethod(); + var name = $"{method?.DeclaringType?.FullName}.{method}"; + text.AppendLine(name); + // Explicit stable hashing; no process-randomized string hashes. + foreach (char c in name) + managedHash = unchecked(managedHash * 31 + c); + } + logItem.managedTrace = text.ToString(); + Multiplayer.game.sync.TryAddStackTraceForDesyncLogRaw(logItem, managedDepth, managedHash); + acc++; + return; + } var trace = logItem.raw; int hash = 0; // Skip this (DeferredStackTracing.Postfix) frame. Keeping it in the trace doesn't provide any value. diff --git a/Source/Client/Desyncs/JittedMethods.cs b/Source/Client/Desyncs/JittedMethods.cs index 7928b04b4..17976caf7 100644 --- a/Source/Client/Desyncs/JittedMethods.cs +++ b/Source/Client/Desyncs/JittedMethods.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -38,6 +38,7 @@ public static class JittedMethods public static void Init() { + if (!Native.IsMonoRuntime) return; profiler = Native.mono_profiler_create(IntPtr.Zero); Native.mono_profiler_set_jit_done_callback(profiler, (_, method, _) => { @@ -73,11 +74,14 @@ public static void Init() public static void OnApplicationQuit() { + if (!Native.IsMonoRuntime || profiler == IntPtr.Zero) return; Native.mono_profiler_set_jit_done_callback(profiler, null); } public static string GetJittedMethodsString() { + if (!Native.IsMonoRuntime) + return "Mono JIT history is unavailable on this runtime."; // Two ToArrays to prevent a compilation in between from causing a "Collection was modified" exception var jittedMethods = methodQueue.ToArray().Select((j, i) => (j, i)).ToArray(); diff --git a/Source/Client/Desyncs/SaveableDesyncInfo.cs b/Source/Client/Desyncs/SaveableDesyncInfo.cs index 200c3dfee..7379d9b65 100644 --- a/Source/Client/Desyncs/SaveableDesyncInfo.cs +++ b/Source/Client/Desyncs/SaveableDesyncInfo.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.IO.Compression; @@ -128,7 +128,9 @@ private string GetDesyncDetails() .AppendLine($"Processor Speed (MHz)|||{SystemInfo.processorFrequency}") .AppendLine($"Thread Count|||{SystemInfo.processorCount}") .AppendLine("\n###GPU Info###") +#if !RIMWORLD_1_7 .AppendLine($"GPU Family|||{SystemInfo.graphicsDeviceVendor}") +#endif .AppendLine($"GPU Type|||{SystemInfo.graphicsDeviceType}") .AppendLine($"GPU Name|||{SystemInfo.graphicsDeviceName}") .AppendLine($"GPU VRAM|||{SystemInfo.graphicsMemorySize}") diff --git a/Source/Client/Desyncs/StackTraceLogItem.cs b/Source/Client/Desyncs/StackTraceLogItem.cs index b7da27da5..43d60ef0f 100644 --- a/Source/Client/Desyncs/StackTraceLogItem.cs +++ b/Source/Client/Desyncs/StackTraceLogItem.cs @@ -40,6 +40,7 @@ public class StackTraceLogItemRaw : StackTraceLogItem public const int MaxDepth = 32; public long[] raw = new long[MaxDepth]; public int depth; + public string managedTrace; public int ticksGame; public ulong rngState; @@ -56,6 +57,7 @@ public override string StackTraceString { get { + if (managedTrace != null) return managedTrace; var builder = new StringBuilder(); for (int i = 0; i < depth; i++) { @@ -78,6 +80,7 @@ public static StackTraceLogItemRaw GetFromPool() public override void Dispose() { + managedTrace = null; depth = 0; ticksGame = 0; rngState = 0; diff --git a/Source/Client/Factions/MultifactionPatches.cs b/Source/Client/Factions/MultifactionPatches.cs index 9eea55518..2d5ef474b 100644 --- a/Source/Client/Factions/MultifactionPatches.cs +++ b/Source/Client/Factions/MultifactionPatches.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.Remoting.Messaging; + using UnityEngine; using Verse; using Verse.AI; diff --git a/Source/Client/Multiplayer.cs b/Source/Client/Multiplayer.cs index 5ea4b1760..c4ccd2794 100644 --- a/Source/Client/Multiplayer.cs +++ b/Source/Client/Multiplayer.cs @@ -151,6 +151,7 @@ public static void InitMultiplayer(ModContentPack content) [MethodImpl(MethodImplOptions.NoInlining)] private static void DisableOmitFramePointer() { + if (!Native.IsMonoRuntime) return; Native.mini_parse_debug_option("disable_omit_fp"); } diff --git a/Source/Client/Multiplayer.csproj b/Source/Client/Multiplayer.csproj index 26cdeb7b1..8f38f6a00 100644 --- a/Source/Client/Multiplayer.csproj +++ b/Source/Client/Multiplayer.csproj @@ -1,7 +1,8 @@ - + - net48 + net48 + net10.0 true 12 false diff --git a/Source/Client/Patches/GravshipTravelSessionPatches.cs b/Source/Client/Patches/GravshipTravelSessionPatches.cs index 4939f821c..82cabdb10 100644 --- a/Source/Client/Patches/GravshipTravelSessionPatches.cs +++ b/Source/Client/Patches/GravshipTravelSessionPatches.cs @@ -33,7 +33,7 @@ static MethodBase TargetMethod() return MpMethodUtil.GetLambda(typeof(GravshipUtility), nameof(GravshipUtility.PreLaunchConfirmation), lambdaOrdinal: 4); } - static void Postfix(Dialog_MessageBox __instance) + static void Postfix() { if (Multiplayer.Client == null) return; if (!Multiplayer.ExecutingCmds) return; diff --git a/Source/Client/Patches/VanillaTweaks.cs b/Source/Client/Patches/VanillaTweaks.cs index a83a1a147..d8716df64 100644 --- a/Source/Client/Patches/VanillaTweaks.cs +++ b/Source/Client/Patches/VanillaTweaks.cs @@ -19,7 +19,9 @@ static void Prefix() { if (done) return; GUI.skin.font = Text.fontStyles[1].font; +#if !RIMWORLD_1_7 Text.fontStyles[1].font.fontNames = new[] { "arial", "arialbd", "ariali", "arialbi" }; +#endif done = true; } } diff --git a/Source/Client/Syncing/SyncTemplates.cs b/Source/Client/Syncing/SyncTemplates.cs index 88a379f93..079ae13fc 100644 --- a/Source/Client/Syncing/SyncTemplates.cs +++ b/Source/Client/Syncing/SyncTemplates.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -39,19 +39,7 @@ static IEnumerable Transpiler(MethodBase original, IEnumerable< if (pInfo.IsOut) { pType = pType.GetElementType(); yield return new CodeInstruction(OpCodes.Ldarg, argIndex); - yield return CreateDefaultCodes(gen, pType).Last(); - if (AccessTools.IsClass(pType)) - yield return new CodeInstruction(OpCodes.Stind_Ref); - if (AccessTools.IsValue(pType)) { - if (pType == typeof(float)) - yield return new CodeInstruction(OpCodes.Stind_R4, (float) 0); - else if (pType == typeof(double)) - yield return new CodeInstruction(OpCodes.Stind_R8, (double) 0); - else if (pType == typeof(long)) - yield return new CodeInstruction(OpCodes.Stind_I8, (long) 0); - else - yield return new CodeInstruction(OpCodes.Stind_I4, 0); - } + yield return new CodeInstruction(OpCodes.Initobj, pType); } } @@ -94,29 +82,21 @@ static IEnumerable Transpiler(MethodBase original, IEnumerable< static IEnumerable CreateDefaultCodes(ILGenerator generator, Type type) { + if (type == typeof(void)) yield break; if (type.IsByRef) type = type.GetElementType(); - if (AccessTools.IsClass(type)) { + if (!type.IsValueType) { yield return new CodeInstruction(OpCodes.Ldnull); yield break; } - if (AccessTools.IsStruct(type)) { + if (type.IsValueType) { var v = generator.DeclareLocal(type); yield return new CodeInstruction(OpCodes.Ldloca, v); yield return new CodeInstruction(OpCodes.Initobj, type); + yield return new CodeInstruction(OpCodes.Ldloc, v); yield break; } - if (AccessTools.IsValue(type)) { - if (type == typeof(float)) - yield return new CodeInstruction(OpCodes.Ldc_R4, (float) 0); - else if (type == typeof(double)) - yield return new CodeInstruction(OpCodes.Ldc_R8, (double) 0); - else if (type == typeof(long)) - yield return new CodeInstruction(OpCodes.Ldc_I8, (long) 0); - else - yield return new CodeInstruction(OpCodes.Ldc_I4, 0); - } } } diff --git a/Source/Client/UI/MainMenuAnimation.cs b/Source/Client/UI/MainMenuAnimation.cs index c1810c71f..c5c14873f 100644 --- a/Source/Client/UI/MainMenuAnimation.cs +++ b/Source/Client/UI/MainMenuAnimation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using HarmonyLib; @@ -24,7 +24,8 @@ static class MainMenuAnimation // The background might be drawn at the same time as the loading of a map on a different thread calls Verse.Rand // Verse.Rand can't be used here because it isn't thread-safe - private static Random rand = new(SystemInfo.deviceUniqueIdentifier.GetHashCode()); + // Cosmetic only: no dependency on Unity's hardware identifier. + private static Random rand = new(); private static float newPulseTimer; diff --git a/Source/Client/Util/MpUI.cs b/Source/Client/Util/MpUI.cs index b80ea128d..590b34d02 100644 --- a/Source/Client/Util/MpUI.cs +++ b/Source/Client/Util/MpUI.cs @@ -263,8 +263,10 @@ public static void DoPasswordField(Rect rect, string controlName, ref string pas { te.scrollOffset = scrollOffset; te.text = new string(PassChar, password.Length); +#if !RIMWORLD_1_7 AccessTools.Field(typeof(TextEditor), "m_RevealCursor").SetValue(te, true); te.UpdateScrollOffsetIfNeeded(new Event()); +#endif } } } diff --git a/Source/Client/Util/MpUtil.cs b/Source/Client/Util/MpUtil.cs index 13264989b..447ae078c 100644 --- a/Source/Client/Util/MpUtil.cs +++ b/Source/Client/Util/MpUtil.cs @@ -18,6 +18,9 @@ public static class MpUtil { public static unsafe void MarkNoInlining(MethodBase method) { + // This offset is a MonoMethod layout, not a CoreCLR MethodDesc. + // Harmony handles detouring on CoreCLR; writing here corrupts memory. + if (!Native.IsMonoRuntime) return; ushort* iflags = (ushort*)(method.MethodHandle.Value) + 1; *iflags |= (ushort)MethodImplOptions.NoInlining; } diff --git a/Source/Common/Common.csproj b/Source/Common/Common.csproj index 9e94cb170..e90e1c879 100644 --- a/Source/Common/Common.csproj +++ b/Source/Common/Common.csproj @@ -1,7 +1,8 @@ - + - net48 + net48 + net10.0 true enable 12 diff --git a/Source/Common/Native.cs b/Source/Common/Native.cs index 12abbd423..e9c993bc1 100644 --- a/Source/Common/Native.cs +++ b/Source/Common/Native.cs @@ -9,6 +9,10 @@ namespace Multiplayer.Client { public static class Native { + // Waylith uses CoreCLR. Never load Mono libraries or dereference Mono + // thread structures on that runtime, even when running old mod assemblies. + public static bool IsMonoRuntime => Type.GetType("Mono.Runtime") != null; + public enum NativeOS { Windows, OSX, Linux, Dummy @@ -24,6 +28,7 @@ public enum NativeOS public static void EarlyInit(NativeOS os) { + if (!IsMonoRuntime) return; if (os == NativeOS.Linux) TheLinuxWay(); if (os == NativeOS.OSX) @@ -46,6 +51,7 @@ private static void EarlyInitInternal() public static unsafe void InitLmfPtr(NativeOS os) { + if (!IsMonoRuntime) return; // Don't bother on 32 bit runtimes if (IntPtr.Size == 4) return; diff --git a/Source/Directory.Build.targets b/Source/Directory.Build.targets new file mode 100644 index 000000000..91306404b --- /dev/null +++ b/Source/Directory.Build.targets @@ -0,0 +1,26 @@ + + + + net10.0 + $(DefineConstants);RIMWORLD_1_7 + true + + + + + + + + false + + + + + + + + + + + + diff --git a/Source/MultiplayerLoader/MultiplayerLoader.csproj b/Source/MultiplayerLoader/MultiplayerLoader.csproj index 018083c71..e2eb6b50d 100644 --- a/Source/MultiplayerLoader/MultiplayerLoader.csproj +++ b/Source/MultiplayerLoader/MultiplayerLoader.csproj @@ -1,7 +1,8 @@ - + - net48 + net48 + net10.0 true 12 false diff --git a/Source/MultiplayerLoader/Prepatches.cs b/Source/MultiplayerLoader/Prepatches.cs index 3261b5497..348d69b89 100644 --- a/Source/MultiplayerLoader/Prepatches.cs +++ b/Source/MultiplayerLoader/Prepatches.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -29,7 +29,9 @@ static void DontSetResolution(ModuleDefinition module) [FreePatch] static void ApplySyncPrepatches(ModuleDefinition module) { +#if !RIMWORLD_1_7 SyncRituals.ApplyPrepatches(module); +#endif } internal static void AddPrepatcherPrefix(ModuleDefinition module, int id, MethodInfo target, MethodInfo prefix) diff --git a/Source/MultiplayerLoader/SyncRituals.cs b/Source/MultiplayerLoader/SyncRituals.cs index 24a1c4af6..02aa5aa2a 100644 --- a/Source/MultiplayerLoader/SyncRituals.cs +++ b/Source/MultiplayerLoader/SyncRituals.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -13,6 +13,17 @@ namespace MultiplayerLoader; public static class SyncRituals { private static List syncMethodsWithPrepatches = new(); +#if RIMWORLD_1_7 + private static readonly Dictionary<(Type, string), ISyncMethodForPrepatcher> runtimeSyncMethods = new(); + private static readonly HashSet runtimePatchedMethods = new(); + private static readonly Harmony runtimeHarmony = new("rwmt.Multiplayer.WaylithRituals"); + + public static bool RuntimePrefix(object __instance, object[] __args, MethodBase __originalMethod) + { + return !runtimeSyncMethods.TryGetValue((__instance.GetType(), __originalMethod.Name), out var sync) + || !sync.DoSync(__instance, __args); + } +#endif public static bool PrepatcherPrefix(int index, object __instance, object[] __args) { @@ -50,6 +61,13 @@ void Register(Type baseType, string method, Type derivedType, Action - net8.0 + net8.0 + net10.0 enable enable 12 diff --git a/Source/Tests/packet-serializations/ClientInitDataPacket.verified.txt b/Source/Tests/packet-serializations/ClientInitDataPacket.verified.txt index b87199a17..c5901528d 100644 --- a/Source/Tests/packet-serializations/ClientInitDataPacket.verified.txt +++ b/Source/Tests/packet-serializations/ClientInitDataPacket.verified.txt @@ -1,3 +1,3 @@ -This packet is not byte-stable while serialized, meaning it can be serialized differently due to various factors, but it does deserialize into the same object +This packet is not byte-stable while serialized, meaning it can be serialized differently due to various factors, but it does deserialize into the same object -05-00-00-00-31-2E-30-2E-30-04-00-00-00-01-00-00-00-02-00-00-00-03-00-00-00-04-00-00-00-01-00-00-00-01-00-00-00-00-00-00-03-02-00-00-00-03-00-00-00-6B-65-79-01-00-00-00-7B-00-00-00-04-00-00-00-6B-65-79-32-00-00-00-00-00-00-00-00-00-24-00-00-00-63-64-60-60-10-00-E2-A2-F2-DC-12-BD-DC-D2-9C-92-CC-82-9C-C4-CA-D4-22-6E-A0-98-2F-82-0B-E4-81-01-33-98-04-00 (117 bytes) +05-00-00-00-31-2E-30-2E-30-04-00-00-00-01-00-00-00-02-00-00-00-03-00-00-00-04-00-00-00-01-00-00-00-01-00-00-00-00-00-00-03-02-00-00-00-03-00-00-00-6B-65-79-01-00-00-00-7B-00-00-00-04-00-00-00-6B-65-79-32-00-00-00-00-00-00-00-00-00-26-00-00-00-63-64-60-60-10-60-60-60-28-2A-CF-2D-D1-CB-2D-CD-29-C9-2C-C8-49-AC-4C-2D-E2-66-60-60-F0-45-70-19-A0-80-19-4C-01-00 (119 bytes) diff --git a/Source/Tests/packet-serializations/ServerJoinDataPacket.verified.txt b/Source/Tests/packet-serializations/ServerJoinDataPacket.verified.txt index 98e950920..17e380770 100644 --- a/Source/Tests/packet-serializations/ServerJoinDataPacket.verified.txt +++ b/Source/Tests/packet-serializations/ServerJoinDataPacket.verified.txt @@ -1,3 +1,3 @@ -This packet is not byte-stable while serialized, meaning it can be serialized differently due to various factors, but it does deserialize into the same object +This packet is not byte-stable while serialized, meaning it can be serialized differently due to various factors, but it does deserialize into the same object -08-00-00-00-47-61-6D-65-4E-61-6D-65-01-00-00-00-08-00-00-00-31-2E-36-2E-34-35-36-36-0D-00-00-00-30-2E-31-31-2E-30-2B-31-32-33-34-35-36-05-00-00-00-00-00-02-03-01-00-24-00-00-00-63-64-60-60-10-00-E2-A2-F2-DC-12-BD-DC-D2-9C-92-CC-82-9C-C4-CA-D4-22-6E-A0-98-2F-82-0B-E4-81-01-33-98-04-00 (95 bytes) +08-00-00-00-47-61-6D-65-4E-61-6D-65-01-00-00-00-08-00-00-00-31-2E-36-2E-34-35-36-36-0D-00-00-00-30-2E-31-31-2E-30-2B-31-32-33-34-35-36-05-00-00-00-00-00-02-03-01-00-26-00-00-00-63-64-60-60-10-60-60-60-28-2A-CF-2D-D1-CB-2D-CD-29-C9-2C-C8-49-AC-4C-2D-E2-66-60-60-F0-45-70-19-A0-80-19-4C-01-00 (97 bytes)