using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using Mirror.FizzySteam; using UnityEngine; using UnityEngine.SceneManagement; using kcp2k; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TechtonicaDirectConnect { public static class InventorySyncPatches { private static bool _patchesApplied; private static object _lastHitDestructibleAction; private static object _lastMOLEAction; private static object _lastTakeAllAction; private static ManualLogSource Log => Plugin.Log; public static void ApplyPatches(Harmony harmony) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Expected O, but got Unknown //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Expected O, but got Unknown //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Expected O, but got Unknown //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Expected O, but got Unknown if (_patchesApplied) { return; } try { MethodInfo methodInfo = AccessTools.Method("TakeAllInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(InventorySyncPatches), "TakeAllInfo_ProcessOnClient_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched TakeAllInfo.ProcessOnClient"); } MethodInfo methodInfo2 = AccessTools.Method("HitDestructibleInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo2 != null) { HarmonyMethod val2 = new HarmonyMethod(typeof(InventorySyncPatches), "HitDestructibleInfo_ProcessOnClient_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched HitDestructibleInfo.ProcessOnClient"); } MethodInfo methodInfo3 = AccessTools.Method("MOLEActionInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo3 != null) { HarmonyMethod val3 = new HarmonyMethod(typeof(InventorySyncPatches), "MOLEActionInfo_ProcessOnClient_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo3, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched MOLEActionInfo.ProcessOnClient (prefix)"); } MethodInfo methodInfo4 = AccessTools.Method("CraftActionInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo4 == null) { methodInfo4 = AccessTools.Method("CraftInfo:ProcessOnClient", (Type[])null, (Type[])null); } if (methodInfo4 != null) { HarmonyMethod val4 = new HarmonyMethod(typeof(InventorySyncPatches), "CraftInfo_ProcessOnClient_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo4, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("[InventorySync] Patched " + methodInfo4.DeclaringType.Name + ".ProcessOnClient")); } else { Log.LogWarning((object)"[InventorySync] CraftActionInfo/CraftInfo.ProcessOnClient not found"); } MethodInfo methodInfo5 = AccessTools.Method("NetworkBuildInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo5 != null) { HarmonyMethod val5 = new HarmonyMethod(typeof(InventorySyncPatches), "SimpleBuildInfo_ProcessOnClient_Postfix", (Type[])null); harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, val5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("[InventorySync] Patched " + methodInfo5.DeclaringType.Name + ".ProcessOnClient")); } else { Log.LogWarning((object)"[InventorySync] NetworkBuildInfo.ProcessOnClient not found"); } MethodInfo methodInfo6 = AccessTools.Method("ExchangeMachineActionInfo:ProcessOnClient", (Type[])null, (Type[])null); if (methodInfo6 == null) { methodInfo6 = AccessTools.Method("ExchangeMachineInfo:ProcessOnClient", (Type[])null, (Type[])null); } if (methodInfo6 != null) { HarmonyMethod val6 = new HarmonyMethod(typeof(InventorySyncPatches), "ExchangeMachineInfo_ProcessOnClient_Postfix", (Type[])null); harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, val6, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("[InventorySync] Patched " + methodInfo6.DeclaringType.Name + ".ProcessOnClient")); } else { Log.LogWarning((object)"[InventorySync] ExchangeMachineActionInfo/ExchangeMachineInfo.ProcessOnClient not found"); } MethodInfo methodInfo7 = AccessTools.Method("NetworkMessageRelay:UserCode_HitDestructableResponse", (Type[])null, (Type[])null); if (methodInfo7 != null) { HarmonyMethod val7 = new HarmonyMethod(typeof(InventorySyncPatches), "HitDestructableResponse_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo7, val7, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_HitDestructableResponse"); } else { Log.LogWarning((object)"[InventorySync] UserCode_HitDestructableResponse not found"); } MethodInfo methodInfo8 = AccessTools.Method("NetworkMessageRelay:UserCode_HitVoxelResponse", (Type[])null, (Type[])null); if (methodInfo8 != null) { HarmonyMethod val8 = new HarmonyMethod(typeof(InventorySyncPatches), "HitVoxelResponse_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo8, val8, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_HitVoxelResponse"); } MethodInfo methodInfo9 = AccessTools.Method("NetworkMessageRelay:UserCode_MOLEResponse", (Type[])null, (Type[])null); if (methodInfo9 != null) { HarmonyMethod val9 = new HarmonyMethod(typeof(InventorySyncPatches), "MOLEResponse_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo9, val9, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_MOLEResponse"); } MethodInfo methodInfo10 = AccessTools.Method("NetworkMessageRelay:UserCode_TakeAllResponse", (Type[])null, (Type[])null); if (methodInfo10 != null) { HarmonyMethod val10 = new HarmonyMethod(typeof(InventorySyncPatches), "TakeAllResponse_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo10, val10, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_TakeAllResponse"); } PatchIncomingActionRPCs(harmony); _patchesApplied = true; Log.LogInfo((object)"[InventorySync] All inventory sync patches applied successfully"); } catch (Exception ex) { Log.LogError((object)("[InventorySync] Error applying patches: " + ex.Message)); } } private static void PatchIncomingActionRPCs(Harmony harmony) { //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown Type type = AccessTools.TypeByName("NetworkMessageRelay"); if (type == null) { Log.LogWarning((object)"[InventorySync] NetworkMessageRelay type not found, cannot patch incoming RPCs"); return; } string[] obj = new string[20] { "HitDestructable", "ActivateMachine", "MOLEAction", "TakeAllFromMachine", "Craft", "RpcBuildSimpleMachine", "RpcBuildConveyor", "EraseMachine", "RotateMachine", "SetFilter", "HarvestPlant", "ExchangeMachineInventory", "SwapVariant", "Replace", "Shoot", "Ping", "ActivatePower", "DeactivateTech", "TempConveyor", "HitVoxel" }; int num = 0; string[] array = obj; foreach (string text in array) { MethodInfo method = type.GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { method = type.GetMethod("UserCode_" + text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (method != null) { try { HarmonyMethod val = new HarmonyMethod(typeof(InventorySyncPatches), "FixIncomingActionTick_Prefix", (Type[])null); harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; Log.LogInfo((object)("[InventorySync] Patched incoming RPC: " + method.Name)); } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] Failed to patch " + text + ": " + ex.Message)); } } } Log.LogInfo((object)$"[InventorySync] Patched {num} incoming action RPCs for tick fix"); } public static void FixIncomingActionTick_Prefix(object __0, MethodBase __originalMethod) { try { Log.LogInfo((object)("[InventorySync] RPC PREFIX CALLED: " + (__originalMethod?.Name ?? "null") + ", arg0=" + (__0?.GetType()?.Name ?? "null"))); if (!NetworkClient.active || NetworkServer.active) { Log.LogInfo((object)"[InventorySync] Skipping - not a client"); return; } if (__0 == null) { Log.LogInfo((object)"[InventorySync] No info object in first parameter"); return; } string name = __0.GetType().Name; Log.LogInfo((object)("[InventorySync] Processing info object of type: " + name)); Type type = AccessTools.TypeByName("MachineManager"); if (type == null) { Log.LogWarning((object)"[InventorySync] MachineManager type not found"); return; } object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { Log.LogWarning((object)"[InventorySync] MachineManager.instance is null"); return; } FieldInfo field = type.GetField("curTick", BindingFlags.Instance | BindingFlags.Public); if (field == null) { Log.LogWarning((object)"[InventorySync] curTick field not found"); return; } int num = (int)field.GetValue(obj); FieldInfo field2 = __0.GetType().GetField("tick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 != null) { int num2 = (int)field2.GetValue(__0); Log.LogInfo((object)$"[InventorySync] {__originalMethod?.Name}: oldTick={num2}, currentTick={num}"); if (num2 != num) { field2.SetValue(__0, num); Log.LogInfo((object)$"[InventorySync] FIXED incoming {__originalMethod?.Name} tick: {num2} -> {num}"); } } else { Log.LogWarning((object)("[InventorySync] tick field not found on " + name)); } } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] FixIncomingActionTick error: " + ex.Message)); } } public static void StorePendingAction(object action) { if (action != null) { switch (action.GetType().Name) { case "HitDestructibleAction": _lastHitDestructibleAction = action; break; case "MOLEAction": _lastMOLEAction = action; break; case "TakeAllAction": _lastTakeAllAction = action; break; } } } public static bool HitDestructableResponse_Prefix(object[] __args) { try { if (!NetworkClient.active || NetworkServer.active) { return true; } Array array = ((__args.Length > 1) ? (__args[1] as Array) : null); if (array != null && array.Length > 0) { Log.LogInfo((object)$"[InventorySync] HitDestructableResponse: Got {array.Length} items from server, using original handler"); return true; } Log.LogInfo((object)"[InventorySync] HitDestructableResponse: Empty response from headless server, calculating items locally"); Type type = AccessTools.TypeByName("Player"); object obj = null; obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null && (Object)(object)NetworkClient.localPlayer != (Object)null) { NetworkIdentity localPlayer = NetworkClient.localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)((Component)localPlayer).gameObject != (Object)null) { MethodInfo method = typeof(GameObject).GetMethod("GetComponent", new Type[0]); if (method != null && type != null) { obj = method.MakeGenericMethod(type).Invoke(((Component)localPlayer).gameObject, null); if (obj != null) { Log.LogInfo((object)"[InventorySync] Found Player via NetworkClient.localPlayer"); } } } } if (obj == null && type != null) { MethodInfo method2 = typeof(Object).GetMethod("FindObjectOfType", new Type[0]); if (method2 != null) { obj = method2.MakeGenericMethod(type).Invoke(null, null); if (obj != null) { Log.LogInfo((object)"[InventorySync] Found Player via FindObjectOfType"); } } } if (obj == null) { Log.LogWarning((object)"[InventorySync] HitDestructableResponse: Could not find Player through any method"); return false; } object obj2 = type.GetProperty("networkedPlayer")?.GetValue(obj); if (obj2 == null) { Log.LogWarning((object)"[InventorySync] HitDestructableResponse: networkedPlayer is null"); return false; } object obj3 = obj2.GetType().GetField("pendingActions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2); object obj4 = null; if (obj3 != null && obj3 is IEnumerable enumerable) { foreach (object item in enumerable) { if (item?.GetType()?.Name == "HitDestructibleAction") { obj4 = item.GetType().GetMethod("GetInfo")?.Invoke(item, null); break; } } } if (obj4 == null && _lastHitDestructibleAction != null) { obj4 = _lastHitDestructibleAction.GetType().GetMethod("GetInfo")?.Invoke(_lastHitDestructibleAction, null); } if (obj4 == null) { Log.LogWarning((object)"[InventorySync] HitDestructableResponse: Could not find pending HitDestructible action"); ClearPendingHitDestructible(obj2); return false; } CalculateAndAddHitDestructibleRewards(obj4, obj); ClearPendingHitDestructible(obj2); return false; } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] HitDestructableResponse_Prefix error: " + ex.Message)); return true; } } private static void ClearPendingHitDestructible(object networkedPlayer) { try { networkedPlayer.GetType().GetMethod("ClearPendingAction", BindingFlags.Instance | BindingFlags.Public); _lastHitDestructibleAction = null; } catch { } } private static void CalculateAndAddHitDestructibleRewards(object hitDestructibleInfo, object playerInstance) { try { if (!(hitDestructibleInfo.GetType().GetField("propLookups")?.GetValue(hitDestructibleInfo) is Array array) || array.Length == 0) { Log.LogInfo((object)"[InventorySync] CalculateRewards: No propLookups"); return; } FieldInfo field = hitDestructibleInfo.GetType().GetField("hitStrength"); float num = ((field != null) ? ((float)field.GetValue(hitDestructibleInfo)) : 100f); Type type = AccessTools.TypeByName("PropManager"); object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { Log.LogWarning((object)"[InventorySync] CalculateRewards: PropManager.instance is null"); return; } object obj2 = AccessTools.TypeByName("Player").GetProperty("inventory")?.GetValue(playerInstance); if (obj2 == null) { Log.LogWarning((object)"[InventorySync] CalculateRewards: Player.inventory is null"); return; } Type type2 = AccessTools.TypeByName("DestructibleData"); MethodInfo method = type.GetMethod("GetDestructibleData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { typeof(int), type2.MakeByRefType() }, null); Type type3 = AccessTools.TypeByName("UIManager"); object obj3 = type3?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); object obj4 = (type3?.GetField("hud", BindingFlags.Instance | BindingFlags.Public))?.GetValue(obj3); int num2 = 0; bool flag = default(bool); for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); FieldInfo field2 = value.GetType().GetField("typeID"); if (field2 == null) { continue; } int num3 = (int)field2.GetValue(value); object[] array2 = new object[2] { num3, null }; object obj5 = method?.Invoke(obj, array2); int num4; if (obj5 is bool) { flag = (bool)obj5; num4 = 1; } else { num4 = 0; } if (((uint)num4 & (flag ? 1u : 0u)) == 0) { continue; } object obj6 = array2[1]; if (obj6 == null) { continue; } MethodInfo method2 = obj6.GetType().GetMethod("WouldBeDestroyed"); bool flag2 = true; if (method2 != null) { flag2 = (bool)method2.Invoke(obj6, new object[2] { value, num }); } if (!flag2 || !(obj6.GetType().GetField("rewards")?.GetValue(obj6) is IList list) || list.Count <= 0) { continue; } foreach (object item in list) { FieldInfo? field3 = item.GetType().GetField("resource"); FieldInfo field4 = item.GetType().GetField("quantity"); object obj7 = field3?.GetValue(item); object obj8 = field4?.GetValue(item); if (obj7 != null && obj8 != null) { MethodInfo method3 = obj2.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { obj7.GetType(), typeof(int) }, null); if (method3 != null) { method3.Invoke(obj2, new object[2] { obj7, obj8 }); num2++; obj4?.GetType().GetMethod("OnCollectResources")?.Invoke(obj4, new object[2] { obj7, obj8 }); Log.LogInfo((object)$"[InventorySync] Added reward: {obj7} x{obj8}"); } } } } Log.LogInfo((object)$"[InventorySync] CalculateRewards: Added {num2} reward items"); } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] CalculateRewards error: " + ex.Message)); } } public static bool HitVoxelResponse_Prefix(object[] __args) { try { if (!NetworkClient.active || NetworkServer.active) { return true; } Array array = ((__args.Length > 3) ? (__args[3] as Array) : null); if (array != null && array.Length > 0) { return true; } Log.LogInfo((object)"[InventorySync] HitVoxelResponse: Empty response, client will calculate ore locally"); return false; } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] HitVoxelResponse_Prefix error: " + ex.Message)); return true; } } public static bool MOLEResponse_Prefix(object[] __args) { try { if (!NetworkClient.active || NetworkServer.active) { return true; } Array array = ((__args.Length > 1) ? (__args[1] as Array) : null); if (array != null && array.Length > 0) { return true; } Log.LogInfo((object)"[InventorySync] MOLEResponse: Empty response, client handles ore locally"); return false; } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] MOLEResponse_Prefix error: " + ex.Message)); return true; } } public static bool TakeAllResponse_Prefix(object[] __args) { try { if (!NetworkClient.active || NetworkServer.active) { return true; } Array array = ((__args.Length > 1) ? (__args[1] as Array) : null); if (array != null && array.Length > 0) { return true; } Log.LogInfo((object)"[InventorySync] TakeAllResponse: Empty response from headless server"); return false; } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] TakeAllResponse_Prefix error: " + ex.Message)); return true; } } public static bool TakeAllInfo_ProcessOnClient_Prefix(object __instance) { Log.LogInfo((object)("[InventorySync] TakeAllInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name)); try { if (!NetworkClient.active || NetworkServer.active) { Log.LogInfo((object)$"[InventorySync] TakeAll: Skipping - NetworkClient.active={NetworkClient.active}, NetworkServer.active={NetworkServer.active}"); return true; } MethodInfo method = __instance.GetType().GetMethod("TakeAll"); if (method == null) { Log.LogWarning((object)"[InventorySync] TakeAll: TakeAll method not found!"); return true; } Log.LogInfo((object)"[InventorySync] TakeAll: Calling TakeAll() method..."); object obj = method.Invoke(__instance, null); Log.LogInfo((object)("[InventorySync] TakeAll: TakeAll() returned: " + (obj?.GetType()?.Name ?? "null"))); IList list = obj as IList; if (list == null || list.Count == 0) { Log.LogInfo((object)$"[InventorySync] TakeAll: No items returned (items={list != null}, count={list?.Count ?? 0})"); return false; } Log.LogInfo((object)$"[InventorySync] TakeAll: Got {list.Count} item stacks to add"); Type type = AccessTools.TypeByName("Player"); Log.LogInfo((object)("[InventorySync] TakeAll: playerType=" + (type?.Name ?? "null"))); object obj2 = null; obj2 = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj2 != null) { Log.LogInfo((object)"[InventorySync] TakeAll: Got player via Player.instance"); } if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null) { obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type); if (obj2 != null) { Log.LogInfo((object)"[InventorySync] TakeAll: Got player via NetworkClient.localPlayer"); } } if (obj2 == null) { obj2 = Object.FindObjectOfType(type); if (obj2 != null) { Log.LogInfo((object)"[InventorySync] TakeAll: Got player via FindObjectOfType"); } } if (obj2 == null) { Log.LogWarning((object)"[InventorySync] TakeAll: Could not find Player instance via any method!"); return false; } object obj3 = type.GetProperty("inventory")?.GetValue(obj2); if (obj3 == null) { Log.LogWarning((object)"[InventorySync] TakeAll: Player.inventory is NULL!"); return false; } Log.LogInfo((object)("[InventorySync] TakeAll: Got inventory of type " + obj3.GetType().Name)); MethodInfo method2 = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { AccessTools.TypeByName("ResourceInfo"), typeof(int) }, null); if (method2 == null) { method2 = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public); Log.LogInfo((object)$"[InventorySync] TakeAll: AddResources fallback lookup = {method2 != null}"); } Log.LogInfo((object)("[InventorySync] TakeAll: AddResources method = " + (method2?.Name ?? "null"))); int num = 0; foreach (object item in list) { PropertyInfo? property = item.GetType().GetProperty("info"); FieldInfo field = item.GetType().GetField("count"); object obj4 = property?.GetValue(item); object obj5 = field?.GetValue(item); Log.LogInfo((object)string.Format("[InventorySync] TakeAll: Item - info={0}, count={1}, infoType={2}", obj4 != null, obj5, obj4?.GetType()?.Name ?? "null")); if (obj4 != null && obj5 != null) { try { method2?.Invoke(obj3, new object[2] { obj4, obj5 }); num++; Log.LogInfo((object)$"[InventorySync] TakeAll: Added item #{num}"); } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] Failed to add resource: " + ex.Message)); } } } Log.LogInfo((object)$"[InventorySync] TakeAll: Added {num} item stacks to inventory"); return false; } catch (Exception ex2) { Log.LogWarning((object)("[InventorySync] TakeAllInfo_Prefix error: " + ex2.Message)); return true; } } public static void HitDestructibleInfo_ProcessOnClient_Prefix(object __instance) { Log.LogInfo((object)("[InventorySync] HitDestructibleInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name)); try { if (!NetworkClient.active || NetworkServer.active) { Log.LogInfo((object)$"[InventorySync] HitDestructible: Skipping - NetworkClient.active={NetworkClient.active}, NetworkServer.active={NetworkServer.active}"); return; } FieldInfo field = __instance.GetType().GetField("propLookups"); if (field == null) { Log.LogWarning((object)"[InventorySync] HitDestructible: propLookups field not found!"); return; } Array array = field.GetValue(__instance) as Array; if (array == null || array.Length == 0) { Log.LogInfo((object)$"[InventorySync] HitDestructible: No propLookups (null={array == null})"); return; } Log.LogInfo((object)$"[InventorySync] HitDestructible: Processing {array.Length} prop lookups"); Type type = AccessTools.TypeByName("PropManager"); object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { Log.LogWarning((object)"[InventorySync] HitDestructible: PropManager.instance is NULL!"); return; } Type type2 = AccessTools.TypeByName("Player"); object obj2 = null; obj2 = type2?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null) { obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type2); } if (obj2 == null) { obj2 = Object.FindObjectOfType(type2); } if (obj2 == null) { Log.LogWarning((object)"[InventorySync] HitDestructible: Could not find Player!"); return; } Log.LogInfo((object)"[InventorySync] HitDestructible: Got player instance"); object obj3 = type2.GetProperty("inventory")?.GetValue(obj2); if (obj3 == null) { Log.LogWarning((object)"[InventorySync] HitDestructible: Player.inventory is NULL!"); return; } Type type3 = AccessTools.TypeByName("DestructibleData"); MethodInfo methodInfo = null; if (type3 != null) { methodInfo = type.GetMethod("GetDestructibleData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { typeof(int), type3.MakeByRefType() }, null); Log.LogInfo((object)$"[InventorySync] HitDestructible: GetDestructibleData(int, out) method found: {methodInfo != null}"); } else { Log.LogWarning((object)"[InventorySync] HitDestructible: DestructibleData type not found"); } int num = 0; bool flag = default(bool); for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); try { FieldInfo field2 = value.GetType().GetField("typeID"); if (field2 == null) { Log.LogWarning((object)"[InventorySync] HitDestructible: typeID field not found in InstanceLookup"); continue; } int num2 = (int)field2.GetValue(value); object[] array2 = new object[2] { num2, null }; object obj4 = methodInfo?.Invoke(obj, array2); int num3; if (obj4 is bool) { flag = (bool)obj4; num3 = 1; } else { num3 = 0; } if (((uint)num3 & (flag ? 1u : 0u)) == 0) { continue; } object obj5 = array2[1]; if (obj5 == null || !(obj5.GetType().GetField("rewards")?.GetValue(obj5) is IList list) || list.Count <= 0) { continue; } foreach (object item in list) { FieldInfo? field3 = item.GetType().GetField("resource"); FieldInfo field4 = item.GetType().GetField("quantity"); object obj6 = field3?.GetValue(item); object obj7 = field4?.GetValue(item); if (obj6 == null || obj7 == null) { continue; } try { MethodInfo method = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { obj6.GetType(), typeof(int) }, null); if (method != null) { method.Invoke(obj3, new object[2] { obj6, obj7 }); num++; } else { Log.LogWarning((object)("[InventorySync] HitDestructible: AddResources method not found for type " + obj6.GetType().Name)); } } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] HitDestructible: AddResources failed: " + ex.Message)); } } } catch { } } if (num > 0) { Log.LogInfo((object)$"[InventorySync] HitDestructible: Added {num} item stacks to inventory"); } } catch (Exception ex2) { Log.LogWarning((object)("[InventorySync] HitDestructibleInfo_Prefix error: " + ex2.Message)); } } public static void MOLEActionInfo_ProcessOnClient_Prefix(object __instance) { try { if (NetworkClient.active && !NetworkServer.active) { FieldInfo field = __instance.GetType().GetField("canMineOre"); if (field != null) { bool flag = (bool)field.GetValue(__instance); field.SetValue(__instance, true); Log.LogInfo((object)$"[InventorySync] MOLE: Set canMineOre={flag}->true (client will check locally)"); } else { Log.LogWarning((object)"[InventorySync] MOLE: canMineOre field not found!"); } FieldInfo field2 = __instance.GetType().GetField("digPos"); if (field2 != null) { object value = field2.GetValue(__instance); Log.LogInfo((object)$"[InventorySync] MOLE: digPos={value}"); } } } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] MOLEActionInfo_Prefix error: " + ex.Message)); } } public static bool CraftInfo_ProcessOnClient_Prefix(object __instance) { Log.LogInfo((object)("[InventorySync] CraftInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name)); try { if (!NetworkClient.active || NetworkServer.active) { Log.LogInfo((object)"[InventorySync] Craft: Skipping - not remote client"); return true; } FieldInfo[] fields = __instance.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); string text = string.Join(", ", Array.ConvertAll(fields, (FieldInfo f) => $"{f.Name}={f.GetValue(__instance)}")); Log.LogInfo((object)("[InventorySync] CraftInfo fields: " + text)); Type type = AccessTools.TypeByName("Player"); object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null && (Object)(object)NetworkClient.localPlayer != (Object)null) { obj = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type); } if (obj == null) { obj = Object.FindObjectOfType(type); } if (obj == null) { Log.LogWarning((object)"[InventorySync] Craft: Could not find Player!"); return true; } object obj2 = type.GetProperty("crafting")?.GetValue(obj); if (obj2 == null) { Log.LogWarning((object)"[InventorySync] Craft: Player.crafting is null!"); return true; } object obj3 = __instance.GetType().GetField("recipe")?.GetValue(__instance); if (obj3 != null) { Log.LogInfo((object)("[InventorySync] Craft: Found recipe of type " + obj3.GetType().Name)); MethodInfo method = obj2.GetType().GetMethod("Craft", BindingFlags.Instance | BindingFlags.Public); if (method != null) { Log.LogInfo((object)"[InventorySync] Craft: Calling Craft method..."); try { method.Invoke(obj2, new object[1] { obj3 }); Log.LogInfo((object)"[InventorySync] Craft: Craft method executed!"); } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] Craft method failed: " + (ex.InnerException?.Message ?? ex.Message))); } } else { MethodInfo[] methods = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public); string text2 = string.Join(", ", Array.ConvertAll(methods, (MethodInfo m) => m.Name)); Log.LogInfo((object)("[InventorySync] Craft: Available methods: " + text2)); } } return true; } catch (Exception ex2) { Log.LogWarning((object)("[InventorySync] CraftInfo_Prefix error: " + ex2.Message)); return true; } } public static void SimpleBuildInfo_ProcessOnClient_Postfix(object __instance) { try { if (NetworkClient.active && !NetworkServer.active) { Log.LogInfo((object)"[InventorySync] SimpleBuildInfo postfix - build action processed"); FieldInfo field = __instance.GetType().GetField("machineType"); if (!(field == null)) { field.GetValue(__instance); } } } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] SimpleBuildInfo_Postfix error: " + ex.Message)); } } public static void ExchangeMachineInfo_ProcessOnClient_Postfix(object __instance) { try { if (!NetworkClient.active || NetworkServer.active) { return; } FieldInfo field = __instance.GetType().GetField("addingToMachine"); FieldInfo field2 = __instance.GetType().GetField("quantity"); FieldInfo field3 = __instance.GetType().GetField("resID"); if (field == null || field2 == null || field3 == null) { Log.LogWarning((object)"[InventorySync] ExchangeMachine: Required fields not found"); return; } bool num = (bool)field.GetValue(__instance); int num2 = (int)field2.GetValue(__instance); int num3 = (int)field3.GetValue(__instance); if (num) { return; } Log.LogInfo((object)$"[InventorySync] ExchangeMachine: Taking {num2} of resID {num3} from machine"); MethodInfo methodInfo = AccessTools.TypeByName("SaveState")?.GetMethod("GetResInfoFromId", BindingFlags.Static | BindingFlags.Public); if (methodInfo == null) { Log.LogWarning((object)"[InventorySync] ExchangeMachine: GetResInfoFromId not found"); return; } object obj = methodInfo.Invoke(null, new object[1] { num3 }); if (obj == null) { Log.LogWarning((object)$"[InventorySync] ExchangeMachine: No ResourceInfo for resID {num3}"); return; } Type type = AccessTools.TypeByName("Player"); object obj2 = null; obj2 = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null) { obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type); } if (obj2 == null) { obj2 = Object.FindObjectOfType(type); } if (obj2 == null) { Log.LogWarning((object)"[InventorySync] ExchangeMachine: Could not find Player!"); return; } object obj3 = type.GetProperty("inventory")?.GetValue(obj2); if (obj3 == null) { Log.LogWarning((object)"[InventorySync] ExchangeMachine: Player.inventory is null!"); return; } MethodInfo method = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { obj.GetType(), typeof(int) }, null); if (method != null) { method.Invoke(obj3, new object[2] { obj, num2 }); Log.LogInfo((object)$"[InventorySync] ExchangeMachine: Added {num2} items to inventory"); } else { Log.LogWarning((object)("[InventorySync] ExchangeMachine: AddResources method not found for type " + obj.GetType().Name)); } } catch (Exception ex) { Log.LogWarning((object)("[InventorySync] ExchangeMachineInfo_Postfix error: " + ex.Message)); } } } [BepInPlugin("com.certifried.techtonicadirectconnect", "Techtonica Direct Connect", "1.0.103")] [BepInProcess("Techtonica.exe")] public class Plugin : BaseUnityPlugin { private static Plugin _instance; private Harmony _harmony; private bool _showConnectUI; private string _serverAddress = ""; private string _serverPort = "6968"; private string _statusMessage = ""; private bool _isConnecting; private GUIStyle _windowStyle; private GUIStyle _labelStyle; private GUIStyle _textFieldStyle; private GUIStyle _buttonStyle; private GUIStyle _statusStyle; private Rect _windowRect = new Rect((float)(Screen.width / 2 - 200), (float)(Screen.height / 2 - 100), 400f, 200f); private static KcpTransport _kcpTransport; private static Transport _originalTransport; private static bool _isDirectConnectActive; private const int VK_F11 = 122; private const int VK_ESCAPE = 27; private bool _f11WasPressed; private bool _escWasPressed; private float _debugTimer; private bool _updateRunning; private int _lastToggleFrame = -1; private string _pendingServerAddress = ""; private int _pendingServerPort = 6968; private static float _loadingStuckTimer = 0f; private static bool _loadingMonitorActive = false; private static string _lastLoadingState = ""; private static bool _finishLoadingCalled = false; private static int _lastLoggedSecond = -1; private static int _updateHeartbeat = 0; private static bool _loadingMonitorDebugLogged = false; private static int _monitorCheckCount = 0; public static ManualLogSource Log { get; private set; } public static ConfigEntry DefaultPort { get; private set; } public static ConfigEntry LastServerAddress { get; private set; } public static ConfigEntry ConnectHotkey { get; private set; } public static Plugin Instance => _instance; [DllImport("user32.dll")] private static extern short GetAsyncKeyState(int vKey); private void Awake() { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown _instance = this; Log = ((BaseUnityPlugin)this).Logger; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; DefaultPort = ((BaseUnityPlugin)this).Config.Bind("General", "DefaultPort", 6968, "Default server port"); LastServerAddress = ((BaseUnityPlugin)this).Config.Bind("General", "LastServerAddress", "", "Last connected server address"); ConnectHotkey = ((BaseUnityPlugin)this).Config.Bind("General", "ConnectHotkey", (KeyCode)292, "Hotkey to open connect dialog"); _serverAddress = LastServerAddress.Value; _serverPort = DefaultPort.Value.ToString(); _harmony = new Harmony("com.certifried.techtonicadirectconnect"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); NullSafetyPatches.ApplyPatches(_harmony); InventorySyncPatches.ApplyPatches(_harmony); TickFixPatches.ApplyPatches(_harmony); Log.LogInfo((object)"[Techtonica Direct Connect] v1.0.103 loaded!"); Log.LogInfo((object)"[Techtonica Direct Connect] Press F11 to open connect dialog"); } private void Update() { _updateHeartbeat++; if (!_updateRunning) { _updateRunning = true; Log.LogInfo((object)"[DirectConnect] Update() is running!"); } if (_updateHeartbeat % 300 == 0 && NetworkClient.active) { Log.LogInfo((object)$"[DirectConnect] HEARTBEAT: frame={_updateHeartbeat}, monitorActive={_loadingMonitorActive}, timer={_loadingStuckTimer:F1}s, finishCalled={_finishLoadingCalled}"); } bool keyDown = Input.GetKeyDown((KeyCode)292); bool keyDown2 = Input.GetKeyDown((KeyCode)27); bool flag = (GetAsyncKeyState(122) & 0x8000) != 0; bool flag2 = (GetAsyncKeyState(27) & 0x8000) != 0; _debugTimer += Time.deltaTime; if (_debugTimer > 5f) { _debugTimer = 0f; Log.LogInfo((object)$"[DirectConnect] F11: Unity={keyDown}, Win={flag}, UI={_showConnectUI}"); } if ((keyDown || (flag && !_f11WasPressed)) && Time.frameCount != _lastToggleFrame) { _lastToggleFrame = Time.frameCount; _showConnectUI = !_showConnectUI; if (_showConnectUI) { _statusMessage = ""; } Log.LogInfo((object)$"[DirectConnect] UI toggled via Update: {_showConnectUI}"); } _f11WasPressed = flag; bool flag3 = keyDown2 || (flag2 && !_escWasPressed); if (_showConnectUI && flag3) { _showConnectUI = false; Log.LogInfo((object)"[DirectConnect] UI closed via ESC"); } _escWasPressed = flag2; CheckLoadingMonitor(); } private void CheckLoadingMonitor() { _monitorCheckCount++; if (!NetworkClient.active) { if (_monitorCheckCount % 300 == 0 && _loadingMonitorActive) { Log.LogWarning((object)"[DirectConnect] Monitor: NetworkClient not active!"); } return; } if (_finishLoadingCalled) { if (_monitorCheckCount % 300 == 0) { Log.LogInfo((object)"[DirectConnect] Monitor: finish already called, skipping"); } return; } try { Type type = AccessTools.TypeByName("LoadingUI"); if (type == null) { if (!_loadingMonitorDebugLogged) { Log.LogWarning((object)"[DirectConnect] LoadingUI type not found!"); _loadingMonitorDebugLogged = true; } return; } object obj = null; FieldInfo fieldInfo = AccessTools.Field(type, "instance"); if (fieldInfo != null) { obj = fieldInfo.GetValue(null); } if (obj == null) { PropertyInfo propertyInfo = AccessTools.Property(type, "Instance"); if (propertyInfo != null) { obj = propertyInfo.GetValue(null); } } if (obj == null) { obj = Object.FindObjectOfType(type); } if (obj == null) { if (_loadingMonitorActive && _monitorCheckCount % 60 == 0) { Log.LogWarning((object)"[DirectConnect] Monitor: LoadingUI instance became null!"); } else if (!_loadingMonitorDebugLogged) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); Log.LogWarning((object)("[DirectConnect] LoadingUI.instance is null. Available fields: " + string.Join(", ", fields.Select((FieldInfo f) => f.Name)))); _loadingMonitorDebugLogged = true; } return; } Component val = (Component)((obj is Component) ? obj : null); bool flag = (Object)(object)val != (Object)null && val.gameObject.activeInHierarchy; bool flag2 = false; FieldInfo field = type.GetField("_loaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { flag2 = (bool)field.GetValue(obj); } catch { } } bool flag3 = flag && !flag2; if (_monitorCheckCount % 60 == 0 && _loadingMonitorActive) { Log.LogInfo((object)$"[DirectConnect] Monitor check: goActive={flag}, _loaded={flag2}, isActive={flag3}, timer={_loadingStuckTimer:F1}s"); } if (!flag3) { if (_loadingMonitorActive) { Log.LogWarning((object)$"[DirectConnect] Monitor: Loading done or inactive! goActive={flag}, _loaded={flag2}"); } _loadingMonitorActive = false; _loadingStuckTimer = 0f; return; } string text = ""; string[] array = new string[5] { "currentState", "loadingState", "state", "currentLoadingState", "loadState" }; foreach (string name in array) { FieldInfo field2 = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 != null) { text = field2.GetValue(obj)?.ToString() ?? ""; if (!string.IsNullOrEmpty(text)) { break; } } } if (!_loadingMonitorActive && !_loadingMonitorDebugLogged) { Log.LogInfo((object)$"[DirectConnect] LoadingUI found! Active={flag3}, State='{text}'"); FieldInfo[] fields2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Log.LogInfo((object)("[DirectConnect] LoadingUI fields: " + string.Join(", ", fields2.Select((FieldInfo f) => f.Name)))); _loadingMonitorDebugLogged = true; } if (!flag3) { return; } if (!_loadingMonitorActive) { _loadingMonitorActive = true; _loadingStuckTimer = 0f; _lastLoadingState = text; Log.LogInfo((object)("[DirectConnect] Loading monitor started at state: '" + text + "'")); } _loadingStuckTimer += Time.unscaledDeltaTime; int num = (int)_loadingStuckTimer; if (num > 0 && num != _lastLoggedSecond) { _lastLoggedSecond = num; Log.LogInfo((object)$"[DirectConnect] Loading monitor: {_loadingStuckTimer:F2}s at '{text}' (deltaTime={Time.deltaTime:F4})"); } if (((!NullSafetyPatches._saveLoaded || !(_loadingStuckTimer > 10f)) && !(_loadingStuckTimer > 30f)) || _finishLoadingCalled) { return; } _finishLoadingCalled = true; Log.LogWarning((object)$"[DirectConnect] Loading stuck for {_loadingStuckTimer:F1}s at '{text}' - forcing completion!"); MethodInfo methodInfo = AccessTools.Method(type, "OnFinishLoading", (Type[])null, (Type[])null); if (methodInfo != null) { try { methodInfo.Invoke(obj, null); Log.LogInfo((object)"[DirectConnect] Successfully called LoadingUI.OnFinishLoading()!"); if (Time.timeScale == 0f) { Time.timeScale = 1f; Log.LogInfo((object)"[DirectConnect] Restored Time.timeScale to 1"); } FieldInfo field3 = type.GetField("confirmedLoad", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field3 != null) { field3.SetValue(obj, true); Log.LogInfo((object)"[DirectConnect] Set confirmedLoad = true"); } if (field != null) { field.SetValue(obj, true); Log.LogInfo((object)"[DirectConnect] Set _loaded = true"); } FieldInfo field4 = type.GetField("cg", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field4 != null) { object value = field4.GetValue(obj); if (value != null) { Type type2 = value.GetType(); type2.GetProperty("alpha")?.SetValue(value, 0f); type2.GetProperty("blocksRaycasts")?.SetValue(value, false); Log.LogInfo((object)"[DirectConnect] Set CanvasGroup alpha=0, hidden (but gameObject still active)"); } } EnsureNetworkMessageRelayExists(); return; } catch (Exception ex) { Log.LogError((object)("[DirectConnect] Error calling OnFinishLoading: " + ex.Message)); return; } } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Log.LogInfo((object)("[DirectConnect] OnFinishLoading not found. Available methods: " + string.Join(", ", methods.Select((MethodInfo m) => m.Name)))); } catch (Exception ex2) { Log.LogError((object)("[DirectConnect] Loading monitor error: " + ex2.Message)); } } private void EnsureNetworkMessageRelayExists() { //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("NetworkMessageRelay"); if (type == null) { Log.LogWarning((object)"[DirectConnect] NetworkMessageRelay type not found"); return; } FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { Log.LogWarning((object)"[DirectConnect] Could not find NetworkMessageRelay.instance field"); return; } object value = field.GetValue(null); if (value != null) { object obj = ((value is MonoBehaviour) ? value : null); bool flag = (Object)(object)((obj != null) ? ((Component)obj).GetComponent() : null) != (Object)null; Log.LogInfo((object)$"[DirectConnect] NetworkMessageRelay.instance already exists (hasNetworkIdentity={flag})"); return; } Object[] array = Object.FindObjectsOfType(type); Log.LogInfo((object)$"[DirectConnect] Found {array.Length} NetworkMessageRelay objects in scene"); Object[] array2 = array; foreach (Object val in array2) { MonoBehaviour val2 = (MonoBehaviour)(object)((val is MonoBehaviour) ? val : null); if ((Object)(object)val2 != (Object)null) { NetworkIdentity component = ((Component)val2).GetComponent(); Log.LogInfo((object)$"[DirectConnect] Found relay: {((Object)val2).name}, hasNetworkIdentity={(Object)(object)component != (Object)null}, netId={((component != null) ? component.netId : 0u)}"); if ((Object)(object)component != (Object)null) { field.SetValue(null, val); Log.LogInfo((object)$"[DirectConnect] Using scene NetworkMessageRelay with netId {component.netId}"); return; } } } Log.LogWarning((object)"[DirectConnect] No networked scene relay found - creating dummy (commands may not work!)"); GameObject val3 = new GameObject("DirectConnect_DummyNetworkMessageRelay"); Object.DontDestroyOnLoad((Object)val3); Component value2 = val3.AddComponent(type); field.SetValue(null, value2); Log.LogInfo((object)"[DirectConnect] Created dummy NetworkMessageRelay instance"); } catch (Exception ex) { Log.LogError((object)("[DirectConnect] Failed to create NetworkMessageRelay: " + ex.Message)); } } private void OnGUI() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 Event current = Event.current; if (current != null && (int)current.type == 4 && (int)current.keyCode == 292) { if (Time.frameCount != _lastToggleFrame) { _lastToggleFrame = Time.frameCount; _showConnectUI = !_showConnectUI; if (_showConnectUI) { _statusMessage = ""; } Log.LogInfo((object)$"[DirectConnect] UI toggled via Event.current: {_showConnectUI}"); } current.Use(); } if (_showConnectUI) { InitStyles(); _windowRect = GUI.Window(12345, _windowRect, new WindowFunction(DrawConnectWindow), "Direct Connect", _windowStyle); } } private void InitStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Expected O, but got Unknown //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Expected O, but got Unknown if (_windowStyle == null) { _windowStyle = new GUIStyle(GUI.skin.window); _windowStyle.normal.background = MakeTexture(2, 2, new Color(0.1f, 0.1f, 0.15f, 0.95f)); _windowStyle.fontSize = 16; _windowStyle.fontStyle = (FontStyle)1; _windowStyle.normal.textColor = new Color(0.65f, 0.55f, 0.98f); _windowStyle.padding = new RectOffset(15, 15, 25, 15); _labelStyle = new GUIStyle(GUI.skin.label); _labelStyle.fontSize = 14; _labelStyle.normal.textColor = Color.white; _textFieldStyle = new GUIStyle(GUI.skin.textField); _textFieldStyle.fontSize = 14; _textFieldStyle.normal.background = MakeTexture(2, 2, new Color(0.15f, 0.15f, 0.2f, 1f)); _textFieldStyle.normal.textColor = Color.white; _textFieldStyle.padding = new RectOffset(10, 10, 8, 8); _buttonStyle = new GUIStyle(GUI.skin.button); _buttonStyle.fontSize = 14; _buttonStyle.fontStyle = (FontStyle)1; _buttonStyle.normal.background = MakeTexture(2, 2, new Color(0.49f, 0.23f, 0.93f, 1f)); _buttonStyle.hover.background = MakeTexture(2, 2, new Color(0.59f, 0.33f, 1f, 1f)); _buttonStyle.normal.textColor = Color.white; _buttonStyle.hover.textColor = Color.white; _buttonStyle.padding = new RectOffset(15, 15, 10, 10); _statusStyle = new GUIStyle(GUI.skin.label); _statusStyle.fontSize = 12; _statusStyle.alignment = (TextAnchor)4; _statusStyle.wordWrap = true; } } private Texture2D MakeTexture(int width, int height, Color color) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = color; } Texture2D val = new Texture2D(width, height); val.SetPixels(array); val.Apply(); return val; } private void DrawConnectWindow(int windowID) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Expected O, but got Unknown //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Server:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); _serverAddress = GUILayout.TextField(_serverAddress, _textFieldStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Port:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); _serverPort = GUILayout.TextField(_serverPort, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUILayout.EndHorizontal(); GUILayout.Space(15f); if (NetworkClient.active) { _statusStyle.normal.textColor = new Color(0.2f, 0.83f, 0.6f); GUILayout.Label("Connected to " + NetworkManager.singleton?.networkAddress, _statusStyle, Array.Empty()); GUILayout.Space(10f); if (GUILayout.Button("Disconnect", _buttonStyle, Array.Empty())) { Disconnect(); } } else { if (!string.IsNullOrEmpty(_statusMessage)) { _statusStyle.normal.textColor = ((_statusMessage.Contains("Error") || _statusMessage.Contains("Failed")) ? new Color(0.97f, 0.44f, 0.44f) : new Color(0.65f, 0.55f, 0.98f)); GUILayout.Label(_statusMessage, _statusStyle, Array.Empty()); GUILayout.Space(5f); } GUI.enabled = !_isConnecting && !string.IsNullOrWhiteSpace(_serverAddress); if (GUILayout.Button(_isConnecting ? "Connecting..." : "Connect", _buttonStyle, Array.Empty())) { Connect(); } GUI.enabled = true; } GUILayout.Space(10f); GUIStyle val = new GUIStyle(_buttonStyle); val.normal.background = MakeTexture(2, 2, new Color(0.3f, 0.3f, 0.35f, 1f)); if (GUILayout.Button("Close", val, Array.Empty())) { _showConnectUI = false; } GUI.DragWindow(new Rect(0f, 0f, 10000f, 30f)); } private void Connect() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(_serverAddress)) { _statusMessage = "Please enter a server address"; return; } if (!int.TryParse(_serverPort, out var result) || result < 1 || result > 65535) { _statusMessage = "Invalid port number"; return; } try { Scene val = SceneManager.GetActiveScene(); string name = ((Scene)(ref val)).name; Log.LogInfo((object)("[DirectConnect] Current scene: " + name)); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); Log.LogInfo((object)$"[DirectConnect] Loaded scene {i}: {((Scene)(ref sceneAt)).name} (active: {sceneAt == SceneManager.GetActiveScene()})"); } bool flag = false; for (int j = 0; j < SceneManager.sceneCount; j++) { val = SceneManager.GetSceneAt(j); string name2 = ((Scene)(ref val)).name; if (name2.Contains("Player") || name2.Contains("Voxeland")) { flag = true; break; } } if (!flag) { Log.LogInfo((object)"[DirectConnect] Not in game world - triggering JoinGameAsClient to load game scenes..."); _statusMessage = "Loading game..."; _pendingServerAddress = _serverAddress; _pendingServerPort = result; if (EnableDirectConnect(result)) { NetworkManager singleton = NetworkManager.singleton; if ((Object)(object)singleton != (Object)null) { singleton.networkAddress = _serverAddress; Log.LogInfo((object)("[DirectConnect] Set network address to: " + _serverAddress)); } try { FlowManager.JoinGameAsClient((Action)delegate { Log.LogInfo((object)"[DirectConnect] JoinGameAsClient callback - scene loaded!"); }); Log.LogInfo((object)"[DirectConnect] Called FlowManager.JoinGameAsClient successfully"); return; } catch (Exception arg) { Log.LogError((object)$"[DirectConnect] Failed to call JoinGameAsClient: {arg}"); _statusMessage = "Failed to load game - try loading a save first"; return; } } _statusMessage = "Failed to initialize connection"; } else { DoConnect(_serverAddress, result); } } catch (Exception ex) { _statusMessage = "Error: " + ex.Message; _isConnecting = false; Log.LogError((object)$"[DirectConnect] Connection error: {ex}"); } } private IEnumerator CheckConnection() { float timeout = 15f; float elapsed = 0f; while (elapsed < timeout) { if (NetworkClient.isConnected) { Log.LogInfo((object)"[DirectConnect] Connected! Sending ready signal..."); _statusMessage = "Connected! Joining game..."; if (!NetworkClient.ready) { try { NetworkClient.Ready(); Log.LogInfo((object)"[DirectConnect] Sent Ready signal"); } catch (Exception ex) { Log.LogWarning((object)("[DirectConnect] Ready failed: " + ex.Message)); } } yield return (object)new WaitForSeconds(0.5f); try { if (NetworkClient.ready && (Object)(object)NetworkClient.localPlayer == (Object)null) { NetworkClient.AddPlayer(); Log.LogInfo((object)"[DirectConnect] Requested player spawn"); } } catch (Exception ex2) { Log.LogWarning((object)("[DirectConnect] AddPlayer failed: " + ex2.Message)); } _statusMessage = "Connected!"; _isConnecting = false; yield return (object)new WaitForSeconds(1f); _showConnectUI = false; yield break; } if (!NetworkClient.active && !_isConnecting) { _statusMessage = "Connection failed"; yield break; } elapsed += 0.5f; yield return (object)new WaitForSeconds(0.5f); } if (!NetworkClient.isConnected) { _statusMessage = "Connection timed out"; _isConnecting = false; NetworkManager singleton = NetworkManager.singleton; if (singleton != null) { singleton.StopClient(); } } } private IEnumerator ConnectAfterSceneLoad() { Log.LogInfo((object)"[DirectConnect] ConnectAfterSceneLoad - waiting for game world..."); _statusMessage = "Loading game world..."; float maxWait = 30f; float elapsed = 0f; while (elapsed < maxWait) { bool flag = false; for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); string name = ((Scene)(ref sceneAt)).name; if (name.Contains("Player") || name.Contains("Voxeland")) { flag = true; break; } } if (flag) { Log.LogInfo((object)"[DirectConnect] Game world loaded! Starting connection..."); yield return (object)new WaitForSeconds(2f); DoConnect(_pendingServerAddress, _pendingServerPort); yield break; } elapsed += 0.5f; yield return (object)new WaitForSeconds(0.5f); } _statusMessage = "Timeout waiting for game to load"; Log.LogError((object)"[DirectConnect] Timeout waiting for game world to load"); } private IEnumerator WaitForGameWorldAndConnect(int port) { Log.LogInfo((object)"[DirectConnect] WaitForGameWorldAndConnect - polling for game world..."); _statusMessage = "Loading game world..."; _pendingServerAddress = _serverAddress; _pendingServerPort = port; float maxWait = 60f; float elapsed = 0f; while (elapsed < maxWait) { bool flag = false; for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); string name = ((Scene)(ref sceneAt)).name; Log.LogInfo((object)("[DirectConnect] Checking scene: " + name)); if (name.Contains("Player") || name.Contains("Voxeland")) { flag = true; break; } } if (flag) { Log.LogInfo((object)"[DirectConnect] Game world loaded! Starting connection..."); yield return (object)new WaitForSeconds(3f); DoConnect(_pendingServerAddress, _pendingServerPort); yield break; } elapsed += 1f; _statusMessage = $"Loading game world... ({elapsed:0}s)"; yield return (object)new WaitForSeconds(1f); } _statusMessage = "Timeout waiting for game to load"; Log.LogError((object)"[DirectConnect] Timeout waiting for game world to load"); } private void DoConnect(string address, int port) { try { _isConnecting = true; _statusMessage = "Connecting..."; LastServerAddress.Value = address; if (!EnableDirectConnect(port)) { _statusMessage = "Failed to initialize connection"; _isConnecting = false; return; } NetworkManager singleton = NetworkManager.singleton; if ((Object)(object)singleton == (Object)null) { _statusMessage = "Error: NetworkManager not found"; _isConnecting = false; return; } string text = address; if (!IPAddress.TryParse(address, out IPAddress _)) { try { _statusMessage = "Resolving " + address + "..."; Log.LogInfo((object)("[DirectConnect] Resolving hostname: " + address)); IPAddress[] hostAddresses = Dns.GetHostAddresses(address); foreach (IPAddress iPAddress in hostAddresses) { if (iPAddress.AddressFamily == AddressFamily.InterNetwork) { text = iPAddress.ToString(); Log.LogInfo((object)("[DirectConnect] Resolved " + address + " -> " + text)); break; } } } catch (Exception ex) { Log.LogWarning((object)("[DirectConnect] DNS resolution failed: " + ex.Message + ", trying direct connection...")); } } singleton.networkAddress = text; singleton.StartClient(); Log.LogInfo((object)($"[DirectConnect] Connecting to {text}:{port}..." + ((text != address) ? (" (resolved from " + address + ")") : ""))); ((MonoBehaviour)this).StartCoroutine(CheckConnection()); } catch (Exception ex2) { _statusMessage = "Error: " + ex2.Message; _isConnecting = false; Log.LogError((object)$"[DirectConnect] Connection error: {ex2}"); } } private void Disconnect() { try { NetworkManager singleton = NetworkManager.singleton; if (singleton != null) { singleton.StopClient(); } DisableDirectConnect(); NetworkRelayLinkingPatches.Reset(); _statusMessage = "Disconnected"; Log.LogInfo((object)"[DirectConnect] Disconnected"); } catch (Exception arg) { Log.LogError((object)$"[DirectConnect] Disconnect error: {arg}"); } } public void ShowConnectUI() { _showConnectUI = true; _statusMessage = ""; Log.LogInfo((object)"[DirectConnect] Opening connect UI from main menu"); } private static bool EnableDirectConnect(int port) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown try { NetworkManager singleton = NetworkManager.singleton; if ((Object)(object)singleton == (Object)null) { Log.LogError((object)"[DirectConnect] NetworkManager not found!"); return false; } FieldInfo field = typeof(NetworkManager).GetField("transport", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { Log.LogError((object)"[DirectConnect] Could not find transport field!"); return false; } object? value = field.GetValue(singleton); _originalTransport = (Transport)((value is Transport) ? value : null); if ((Object)(object)_kcpTransport == (Object)null) { GameObject val = new GameObject("DirectConnect_KcpTransport"); Object.DontDestroyOnLoad((Object)val); _kcpTransport = val.AddComponent(); } _kcpTransport.Port = (ushort)port; _kcpTransport.NoDelay = true; _kcpTransport.Interval = 10u; _kcpTransport.Timeout = 30000; _kcpTransport.DualMode = false; field.SetValue(singleton, _kcpTransport); typeof(Transport).GetField("activeTransport", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(null, _kcpTransport); _isDirectConnectActive = true; Log.LogInfo((object)$"[DirectConnect] Switched to KCP transport on port {port}"); return true; } catch (Exception arg) { Log.LogError((object)$"[DirectConnect] Failed to enable: {arg}"); return false; } } private static void DisableDirectConnect() { if ((Object)(object)_originalTransport != (Object)null && (Object)(object)NetworkManager.singleton != (Object)null) { try { typeof(NetworkManager).GetField("transport", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(NetworkManager.singleton, _originalTransport); typeof(Transport).GetField("activeTransport", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(null, _originalTransport); _isDirectConnectActive = false; Log.LogInfo((object)"[DirectConnect] Restored original transport"); } catch (Exception arg) { Log.LogError((object)$"[DirectConnect] Failed to restore transport: {arg}"); } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_kcpTransport != (Object)null) { Object.Destroy((Object)(object)((Component)_kcpTransport).gameObject); } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.certifried.techtonicadirectconnect"; public const string PLUGIN_NAME = "Techtonica Direct Connect"; public const string PLUGIN_VERSION = "1.0.103"; } [HarmonyPatch] public static class NetworkRelayLinkingPatches { private static bool _relayLinked; private static bool _loggedOnce; public static void Reset() { _relayLinked = false; _loggedOnce = false; NullSafetyPatches._sendActionLogged = false; NullSafetyPatches._serverRelayNetId = 2u; NullSafetyPatches._actionCount = 0; } [HarmonyPrefix] [HarmonyPatch(typeof(NetworkClient), "OnSpawn")] public static bool OnSpawn_Prefix(SpawnMessage message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (message.assetId == Guid.Empty && message.sceneId == 0L) { if (!_loggedOnce) { Plugin.Log.LogInfo((object)$"[DirectConnect] Intercepted spawn for netId {message.netId} with no assetId/sceneId - will try to link scene relay"); _loggedOnce = true; } if (_relayLinked) { return false; } NullSafetyPatches._serverRelayNetId = message.netId; Plugin.Log.LogInfo((object)$"[DirectConnect] Server relay netId detected: {message.netId}"); if (LinkSceneRelayToNetId(message.netId)) { _relayLinked = true; return false; } } return true; } private static bool LinkSceneRelayToNetId(uint targetNetId) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) try { Type type = AccessTools.TypeByName("NetworkMessageRelay"); if (type == null) { Plugin.Log.LogWarning((object)"[DirectConnect] NetworkMessageRelay type not found"); return false; } Object[] array = Object.FindObjectsOfType(type); Plugin.Log.LogInfo((object)$"[DirectConnect] Found {array.Length} NetworkMessageRelay objects in scene"); NetworkIdentity val = null; object obj = null; Object[] array2 = array; foreach (Object val2 in array2) { MonoBehaviour val3 = (MonoBehaviour)(object)((val2 is MonoBehaviour) ? val2 : null); if (!((Object)(object)val3 == (Object)null)) { val = ((Component)val3).GetComponent(); if ((Object)(object)val != (Object)null) { obj = val2; Plugin.Log.LogInfo((object)("[DirectConnect] Found scene relay '" + ((Object)val3).name + "' with identity")); break; } Plugin.Log.LogInfo((object)("[DirectConnect] Relay '" + ((Object)val3).name + "' has no NetworkIdentity")); } } if (obj == null || (Object)(object)val == (Object)null) { Plugin.Log.LogInfo((object)$"[DirectConnect] No scene relay found - creating networked relay for netId {targetNetId}"); GameObject val4 = new GameObject("DirectConnect_NetworkMessageRelay"); Object.DontDestroyOnLoad((Object)val4); val = val4.AddComponent(); obj = val4.AddComponent(type); FieldInfo field = typeof(NetworkBehaviour).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, val); Plugin.Log.LogInfo((object)"[DirectConnect] Set netIdentity backing field on relay"); } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not find netIdentity backing field!"); } PropertyInfo property = typeof(NetworkIdentity).GetProperty("NetworkBehaviours", BindingFlags.Instance | BindingFlags.Public); if (property != null) { MethodInfo setMethod = property.GetSetMethod(nonPublic: true); if (setMethod != null) { NetworkBehaviour[] array3 = (NetworkBehaviour[])(object)new NetworkBehaviour[1] { (NetworkBehaviour)((obj is NetworkBehaviour) ? obj : null) }; setMethod.Invoke(val, new object[1] { array3 }); Plugin.Log.LogInfo((object)"[DirectConnect] Set NetworkBehaviours array on identity"); } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not get NetworkBehaviours setter!"); } } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not find NetworkBehaviours property!"); } PropertyInfo property2 = typeof(NetworkBehaviour).GetProperty("ComponentIndex", BindingFlags.Instance | BindingFlags.Public); if (property2 != null) { MethodInfo setMethod2 = property2.GetSetMethod(nonPublic: true); if (setMethod2 != null) { setMethod2.Invoke(obj, new object[1] { (byte)0 }); Plugin.Log.LogInfo((object)"[DirectConnect] Set ComponentIndex = 0 on relay"); } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not get ComponentIndex setter!"); } } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not find ComponentIndex property!"); } Plugin.Log.LogInfo((object)"[DirectConnect] Created NetworkMessageRelay with NetworkIdentity"); } Plugin.Log.LogInfo((object)$"[DirectConnect] Linking relay to netId {targetNetId}"); bool flag = false; FieldInfo field2 = typeof(NetworkIdentity).GetField("_netId", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 == null) { field2 = typeof(NetworkIdentity).GetField("netId", BindingFlags.Instance | BindingFlags.NonPublic); } if (field2 != null) { field2.SetValue(val, targetNetId); Plugin.Log.LogInfo((object)$"[DirectConnect] Set identity._netId = {targetNetId} via field"); flag = true; } else { PropertyInfo property3 = typeof(NetworkIdentity).GetProperty("netId", BindingFlags.Instance | BindingFlags.Public); if (property3 != null) { MethodInfo setMethod3 = property3.GetSetMethod(nonPublic: true); if (setMethod3 != null) { setMethod3.Invoke(val, new object[1] { targetNetId }); Plugin.Log.LogInfo((object)$"[DirectConnect] Set identity.netId = {targetNetId} via property setter"); flag = true; } } } if (!flag) { Plugin.Log.LogError((object)"[DirectConnect] Could not set netId on identity!"); return false; } if (!NetworkClient.spawned.ContainsKey(targetNetId)) { NetworkClient.spawned[targetNetId] = val; Plugin.Log.LogInfo((object)$"[DirectConnect] Added identity to NetworkClient.spawned[{targetNetId}]"); } FieldInfo field3 = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field3 != null) { field3.SetValue(null, obj); Plugin.Log.LogInfo((object)"[DirectConnect] Set NetworkMessageRelay.instance"); } FieldInfo field4 = typeof(NetworkIdentity).GetField("isClient", BindingFlags.Instance | BindingFlags.NonPublic); if (field4 != null) { field4.SetValue(val, true); } FieldInfo field5 = typeof(NetworkIdentity).GetField("_hasAuthority", BindingFlags.Instance | BindingFlags.NonPublic); if (field5 != null) { field5.SetValue(val, true); Plugin.Log.LogInfo((object)"[DirectConnect] Set hasAuthority = true"); } PropertyInfo property4 = typeof(NetworkIdentity).GetProperty("connectionToServer", BindingFlags.Instance | BindingFlags.Public); if (property4 != null && NetworkClient.connection != null) { MethodInfo setMethod4 = property4.GetSetMethod(nonPublic: true); if (setMethod4 != null) { setMethod4.Invoke(val, new object[1] { NetworkClient.connection }); Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer = NetworkClient.connection (via property)"); } else { try { property4.SetValue(val, NetworkClient.connection); Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer = NetworkClient.connection (direct)"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DirectConnect] Could not set connectionToServer: " + ex.Message)); } } } else { Plugin.Log.LogWarning((object)$"[DirectConnect] Could not set connectionToServer: prop={property4 != null}, connection={NetworkClient.connection != null}"); } Plugin.Log.LogInfo((object)$"[DirectConnect] Successfully linked NetworkMessageRelay to server's netId {targetNetId}!"); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"[DirectConnect] Failed to link scene relay: {arg}"); return false; } } } [HarmonyPatch] public static class FizzyFacepunchPatches { [HarmonyPrefix] [HarmonyPatch(typeof(FizzyFacepunch), "ClientEarlyUpdate")] public static bool ClientEarlyUpdate_Prefix() { return false; } [HarmonyPrefix] [HarmonyPatch(typeof(FizzyFacepunch), "ClientLateUpdate")] public static bool ClientLateUpdate_Prefix() { return false; } [HarmonyPrefix] [HarmonyPatch(typeof(FizzyFacepunch), "ServerEarlyUpdate")] public static bool ServerEarlyUpdate_Prefix() { return false; } [HarmonyPrefix] [HarmonyPatch(typeof(FizzyFacepunch), "ServerLateUpdate")] public static bool ServerLateUpdate_Prefix() { return false; } } [HarmonyPatch] public static class MainMenuPatches { [HarmonyPostfix] [HarmonyPatch(typeof(MainMenuUI), "RefreshHiddenButtonState")] public static void RefreshHiddenButtonState_Postfix(MainMenuUI __instance) { try { if (AccessTools.Field(typeof(MainMenuUI), "menuSelections")?.GetValue(__instance) is MainMenuItem[] array && array.Length > 3) { ((Component)array[3]).gameObject.SetActive(true); Plugin.Log.LogInfo((object)"[DirectConnect] Enabled 'Join Multiplayer' button in main menu"); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[DirectConnect] Failed to patch main menu: {arg}"); } } [HarmonyPrefix] [HarmonyPatch(typeof(MainMenuUI), "JoinMultiplayerAsClient")] public static bool JoinMultiplayerAsClient_Prefix() { try { Plugin.Log.LogInfo((object)"[DirectConnect] 'Join Multiplayer' clicked - showing connect dialog"); Plugin.Instance.ShowConnectUI(); return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"[DirectConnect] Error in JoinMultiplayerAsClient: {arg}"); return true; } } } public static class NullSafetyPatches { private static bool _patchesApplied = false; public static uint _serverRelayNetId = 2u; public static bool _sendActionLogged = false; public static int _actionCount = 0; private static readonly Dictionary _actionTypeToCommand = new Dictionary { { "ConveyorBuildAction", "BuildConveyorCommand" }, { "HVCBuildAction", "BuildHvcCommand" }, { "MassTransitBuildAction", "BuildMassTransitCommand" }, { "MassTransitChainBuildAction", "BuildMassTransitChainCommand" }, { "ResearchCoreBuildAction", "BuildResearchCoreCommand" }, { "SimpleBuildAction", "BuildSimpleMachineCommand" }, { "ExchangeMachineAction", "ExchangeMachineInvCommand" }, { "RotateMachineAction", "RotateMachineCommand" }, { "SwapVariantAction", "SwapVariantCommand" }, { "EraseMachineAction", "EraseMachineCommand" }, { "ChangeFilterAction", "SetFilterCommand" }, { "HarvestPlantAction", "HarvestPlantCommand" }, { "TakeAllAction", "TakeAllCommand" }, { "HitDestructibleAction", "HitDestructableCommand" }, { "HitVoxelAction", "HitVoxelCommand" }, { "RepairAction", "RepairMachineCommand" }, { "CheatAction", "CheatCommand" }, { "CraftAction", "CraftCommand" }, { "ActivatePowerAction", "ActivatePowerCommand" }, { "ScanAction", "ScanCommand" }, { "UnlockTechAction", "UnlockTechCommand" }, { "DeactivateTechAction", "DeactivateTechCommand" }, { "ModifyMouseBufferAction", "ModifyMouseBufferCommand" }, { "InteractAction", "InteractCommand" }, { "ActivateMachineAction", "ActivateMachineCommand" }, { "MOLEAction", "MOLECommand" }, { "SortInventoryAction", "SortCommand" }, { "TempConveyorAction", "UpdateTempConveyorsCommand" }, { "UpgradeProductionTerminalAction", "UpgradeResearchCommand" }, { "UnlockDatabankEntryAction", "UnlockDatabankEntryCommand" }, { "QuestStatusAction", "CompleteQuestCommand" }, { "ExplosiveBuildAction", "BuildExplosiveCommand" }, { "ExplodeAction", "ExplodeCommand" }, { "CompleteResourceGateAction", "CompleteResourceGateCommand" }, { "CompleteRepairElevatorAction", "CompleteRepairElevatorCommand" }, { "CheatUpgradeAction", "CheatUpgradeCommand" }, { "MainReceiverAction", "MainReceiverCommand" }, { "BuildWithRecipeAction", "BuildMachineWithRecipeCommand" }, { "MultiPlaceBuildAction", "BuildMultiPlaceCommand" }, { "ReplaceAction", "ReplaceCommand" }, { "ResizeStorageLimitAction", "ResizeStorageLimitCommand" }, { "ShootAction", "ShootCommand" }, { "MultiplayerGameAction", "MultiplayerGameCommand" }, { "SetNexusTeamAction", "SetNexusTeamCommand" }, { "MultiplayerGameLootNexusAction", "MultiplayerGameLootNexusCommand" }, { "AsteroidsAction", "AsteroidsCommand" }, { "PingAction", "PingCommand" }, { "OmniseekablePingAction", "OmniseekablePingCommand" }, { "SandPumpBuildAction", "BuildSandPumpCommand" } }; private static int _sendCommandCount = 0; private static bool _requestSimTickHandled = false; public static bool _saveLoaded = false; public static void ApplyPatches(Harmony harmony) { //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Expected O, but got Unknown //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Expected O, but got Unknown //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Expected O, but got Unknown //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_0527: Expected O, but got Unknown //IL_05aa: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Expected O, but got Unknown //IL_0623: Unknown result type (might be due to invalid IL or missing references) //IL_062a: Expected O, but got Unknown //IL_069c: Unknown result type (might be due to invalid IL or missing references) //IL_06a3: Expected O, but got Unknown //IL_0715: Unknown result type (might be due to invalid IL or missing references) //IL_071c: Expected O, but got Unknown //IL_078e: Unknown result type (might be due to invalid IL or missing references) //IL_0795: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Expected O, but got Unknown //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Expected O, but got Unknown //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown if (_patchesApplied) { return; } try { Type type = AccessTools.TypeByName("ThirdPersonDisplayAnimator"); if (type != null) { MethodInfo methodInfo = AccessTools.Method(type, "Update", (Type[])null, (Type[])null); if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched ThirdPersonDisplayAnimator.Update to skip"); } MethodInfo methodInfo2 = AccessTools.Method(type, "UpdateSillyStuff", (Type[])null, (Type[])null); if (methodInfo2 != null) { HarmonyMethod val2 = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched ThirdPersonDisplayAnimator.UpdateSillyStuff to skip"); } } else { Plugin.Log.LogWarning((object)"[DirectConnect] ThirdPersonDisplayAnimator type not found"); } Type type2 = AccessTools.TypeByName("NetworkMessageRelay"); if (type2 != null) { MethodInfo methodInfo3 = AccessTools.Method(type2, "SendNetworkAction", (Type[])null, (Type[])null); if (methodInfo3 != null) { HarmonyMethod val3 = new HarmonyMethod(typeof(NullSafetyPatches), "SendNetworkAction_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo3, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.SendNetworkAction (complete replacement)"); } MethodInfo methodInfo4 = AccessTools.Method(type2, "CmdSendNetworkAction", (Type[])null, (Type[])null); if (methodInfo4 != null) { HarmonyMethod val4 = new HarmonyMethod(typeof(NullSafetyPatches), "CmdSendNetworkAction_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo4, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.CmdSendNetworkAction"); } else { Plugin.Log.LogWarning((object)"[DirectConnect] CmdSendNetworkAction method not found - checking for other command names"); MethodInfo[] methods = type2.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo5 in methods) { if (methodInfo5.Name.Contains("Cmd") || methodInfo5.Name.Contains("Command") || methodInfo5.Name.Contains("Send")) { Plugin.Log.LogInfo((object)("[DirectConnect] Found method: " + methodInfo5.Name)); } } } } try { MethodInfo method = typeof(NetworkBehaviour).GetMethod("SendCommandInternal", BindingFlags.Instance | BindingFlags.NonPublic); if (method != null) { HarmonyMethod val5 = new HarmonyMethod(typeof(NullSafetyPatches), "SendCommandInternal_Prefix", (Type[])null); harmony.Patch((MethodBase)method, val5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched Mirror.NetworkBehaviour.SendCommandInternal"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch SendCommandInternal (non-critical): " + ex.Message)); } if (type2 != null) { MethodInfo methodInfo6 = AccessTools.Method(type2, "RequestCurrentSimTick", (Type[])null, (Type[])null); if (methodInfo6 != null) { HarmonyMethod val6 = new HarmonyMethod(typeof(NullSafetyPatches), "RequestCurrentSimTick_Prefix", (Type[])null); HarmonyMethod val7 = new HarmonyMethod(typeof(NullSafetyPatches), "RequestCurrentSimTick_Finalizer", (Type[])null); harmony.Patch((MethodBase)methodInfo6, val6, (HarmonyMethod)null, (HarmonyMethod)null, val7, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.RequestCurrentSimTick with prefix and finalizer"); } MethodInfo methodInfo7 = AccessTools.Method(type2, "UserCode_ProcessCurrentSimTick", (Type[])null, (Type[])null); if (methodInfo7 != null) { HarmonyMethod val8 = new HarmonyMethod(typeof(NullSafetyPatches), "ProcessCurrentSimTick_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo7, val8, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.UserCode_ProcessCurrentSimTick with prefix"); } } Type type3 = AccessTools.TypeByName("NetworkedPlayer"); if (type3 != null) { MethodInfo methodInfo8 = AccessTools.Method(type3, "OnStopClient", (Type[])null, (Type[])null); if (methodInfo8 != null) { HarmonyMethod val9 = new HarmonyMethod(typeof(NullSafetyPatches), "OnStopClient_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo8, val9, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkedPlayer.OnStopClient for disconnect tracking"); } } MethodInfo methodInfo9 = AccessTools.Method(typeof(NetworkManager), "OnClientDisconnect", (Type[])null, (Type[])null); if (methodInfo9 != null) { HarmonyMethod val10 = new HarmonyMethod(typeof(NullSafetyPatches), "OnClientDisconnect_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo9, val10, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkManager.OnClientDisconnect for disconnect tracking"); } try { MethodInfo method2 = typeof(NetworkClient).GetMethod("OnObjectSpawn", BindingFlags.Static | BindingFlags.NonPublic); if (method2 != null) { HarmonyMethod val11 = new HarmonyMethod(typeof(NullSafetyPatches), "OnObjectSpawn_Prefix", (Type[])null); harmony.Patch((MethodBase)method2, val11, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.OnObjectSpawn for spawn tracking"); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch OnObjectSpawn: " + ex2.Message)); } try { MethodInfo method3 = typeof(NetworkClient).GetMethod("OnObjectDestroy", BindingFlags.Static | BindingFlags.NonPublic); if (method3 != null) { HarmonyMethod val12 = new HarmonyMethod(typeof(NullSafetyPatches), "OnObjectDestroy_Prefix", (Type[])null); harmony.Patch((MethodBase)method3, val12, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.OnObjectDestroy for destroy tracking"); } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch OnObjectDestroy: " + ex3.Message)); } try { MethodInfo method4 = typeof(NetworkIdentity).GetMethod("HandleRemoteCall", BindingFlags.Instance | BindingFlags.NonPublic); if (method4 != null) { HarmonyMethod val13 = new HarmonyMethod(typeof(NullSafetyPatches), "HandleRemoteCall_Prefix", (Type[])null); harmony.Patch((MethodBase)method4, val13, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkIdentity.HandleRemoteCall for RPC tracking"); } } catch (Exception ex4) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch HandleRemoteCall: " + ex4.Message)); } try { MethodInfo method5 = typeof(NetworkClient).GetMethod("Connect", new Type[1] { typeof(string) }); if (method5 != null) { HarmonyMethod val14 = new HarmonyMethod(typeof(NullSafetyPatches), "NetworkClient_Connect_Prefix", (Type[])null); harmony.Patch((MethodBase)method5, val14, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.Connect"); } } catch (Exception ex5) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch NetworkClient.Connect: " + ex5.Message)); } try { MethodInfo method6 = typeof(KcpTransport).GetMethod("OnClientConnected", BindingFlags.Instance | BindingFlags.NonPublic); if (method6 != null) { HarmonyMethod val15 = new HarmonyMethod(typeof(NullSafetyPatches), "KCP_OnClientConnected_Prefix", (Type[])null); harmony.Patch((MethodBase)method6, val15, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpTransport.OnClientConnected"); } } catch (Exception ex6) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpTransport.OnClientConnected: " + ex6.Message)); } try { MethodInfo method7 = typeof(KcpTransport).GetMethod("OnClientDisconnected", BindingFlags.Instance | BindingFlags.NonPublic); if (method7 != null) { HarmonyMethod val16 = new HarmonyMethod(typeof(NullSafetyPatches), "KCP_OnClientDisconnected_Prefix", (Type[])null); harmony.Patch((MethodBase)method7, val16, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpTransport.OnClientDisconnected"); } } catch (Exception ex7) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpTransport.OnClientDisconnected: " + ex7.Message)); } try { MethodInfo method8 = typeof(KcpClient).GetMethod("Disconnect", BindingFlags.Instance | BindingFlags.Public); if (method8 != null) { HarmonyMethod val17 = new HarmonyMethod(typeof(NullSafetyPatches), "KcpClient_Disconnect_Prefix", (Type[])null); harmony.Patch((MethodBase)method8, val17, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpClient.Disconnect"); } } catch (Exception ex8) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpClient.Disconnect: " + ex8.Message)); } try { MethodInfo method9 = typeof(KcpConnection).GetMethod("Disconnect", BindingFlags.Instance | BindingFlags.Public); if (method9 != null) { HarmonyMethod val18 = new HarmonyMethod(typeof(NullSafetyPatches), "KcpConnection_Disconnect_Prefix", (Type[])null); harmony.Patch((MethodBase)method9, val18, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpConnection.Disconnect"); } } catch (Exception ex9) { Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpConnection.Disconnect: " + ex9.Message)); } _patchesApplied = true; } catch (Exception arg) { Plugin.Log.LogError((object)$"[DirectConnect] Failed to apply null safety patches: {arg}"); } } public static bool Skip_Prefix() { return false; } public static bool SendNetworkAction_Prefix(object __instance, object action) { _actionCount++; string text = action?.GetType()?.Name ?? "null"; try { Plugin.Log.LogInfo((object)$"[DirectConnect] SendNetworkAction #{_actionCount}: {text}"); object obj = ((__instance is NetworkBehaviour) ? __instance : null); if ((Object)(object)((obj != null) ? ((NetworkBehaviour)obj).netIdentity : null) == (Object)null) { Plugin.Log.LogError((object)("[DirectConnect] SendNetworkAction: netIdentity is NULL! Cannot send " + text)); return false; } if (!_actionTypeToCommand.TryGetValue(text, out var value)) { Plugin.Log.LogError((object)("[DirectConnect] Unknown action type: " + text + ". Cannot send to server!")); return false; } if (SendCommandDirectly(__instance, action, value, text)) { Plugin.Log.LogInfo((object)("[DirectConnect] " + value + " sent successfully via SendCommandInternal!")); } else { Plugin.Log.LogError((object)("[DirectConnect] Failed to send " + value + "!")); } return false; } catch (Exception ex) { Plugin.Log.LogError((object)("[DirectConnect] SendNetworkAction_Prefix error for " + text + ": " + ex.Message)); Plugin.Log.LogError((object)("[DirectConnect] Stack: " + ex.StackTrace)); return false; } } private static bool SendCommandDirectly(object relayInstance, object action, string commandName, string actionTypeName) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) try { NetworkBehaviour val = (NetworkBehaviour)((relayInstance is NetworkBehaviour) ? relayInstance : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[DirectConnect] SendCommandDirectly: relay is not a NetworkBehaviour!"); return false; } if (!NetworkClient.active) { Plugin.Log.LogError((object)"[DirectConnect] SendCommandDirectly: NetworkClient is not active!"); return false; } if (!NetworkClient.ready) { Plugin.Log.LogError((object)"[DirectConnect] SendCommandDirectly: NetworkClient is not ready!"); return false; } if (NetworkClient.connection == null) { Plugin.Log.LogError((object)"[DirectConnect] SendCommandDirectly: NetworkClient.connection is null!"); return false; } MethodInfo method = typeof(NetworkWriterPool).GetMethod("GetWriter", BindingFlags.Static | BindingFlags.Public); if (method == null) { Plugin.Log.LogWarning((object)"[DirectConnect] NetworkWriterPool.GetWriter not found, using new NetworkWriter"); } object obj; try { obj = method?.Invoke(null, null) ?? ((object)new NetworkWriter()); } catch { obj = (object)new NetworkWriter(); } Type type = AccessTools.TypeByName("GeneratedNetworkCode"); if (type == null) { Plugin.Log.LogError((object)"[DirectConnect] GeneratedNetworkCode type not found!"); return false; } string text = "_Write_" + actionTypeName; MethodInfo method2 = type.GetMethod(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method2 == null) { Plugin.Log.LogError((object)("[DirectConnect] Serializer method '" + text + "' not found in GeneratedNetworkCode!")); foreach (MethodInfo item in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Take(20)) { if (item.Name.StartsWith("_Write")) { Plugin.Log.LogInfo((object)("[DirectConnect] Available: " + item.Name)); } } return false; } FixActionTick(action, actionTypeName); try { method2.Invoke(null, new object[2] { obj, action }); Plugin.Log.LogInfo((object)("[DirectConnect] Serialized " + actionTypeName + " using " + text)); } catch (Exception ex) { Plugin.Log.LogError((object)("[DirectConnect] Failed to serialize " + actionTypeName + ": " + (ex.InnerException?.Message ?? ex.Message))); return false; } Type type2 = AccessTools.TypeByName("NetworkMessageRelay"); if (type2 == null) { Plugin.Log.LogError((object)"[DirectConnect] NetworkMessageRelay type not found!"); return false; } Type type3 = AccessTools.TypeByName("Mirror.RemoteCallHelper") ?? AccessTools.TypeByName("RemoteCallHelper"); if (type3 == null) { Plugin.Log.LogError((object)"[DirectConnect] RemoteCallHelper type not found!"); return false; } MethodInfo method3 = type3.GetMethod("GetMethodHash", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method3 == null) { Plugin.Log.LogError((object)"[DirectConnect] RemoteCallHelper.GetMethodHash not found!"); return false; } int num; try { num = (int)method3.Invoke(null, new object[2] { type2, commandName }); Plugin.Log.LogInfo((object)$"[DirectConnect] Function hash for {commandName}: {num}"); } catch (Exception ex2) { Plugin.Log.LogError((object)("[DirectConnect] Failed to get function hash: " + ex2.Message)); return false; } NetworkIdentity netIdentity = val.netIdentity; int componentIndex = val.ComponentIndex; MethodInfo method4 = obj.GetType().GetMethod("ToArraySegment", BindingFlags.Instance | BindingFlags.Public); if (method4 == null) { Plugin.Log.LogError((object)"[DirectConnect] NetworkWriter.ToArraySegment not found!"); return false; } object value = method4.Invoke(obj, null); Type type4 = AccessTools.TypeByName("Mirror.CommandMessage"); if (type4 == null) { Plugin.Log.LogError((object)"[DirectConnect] Mirror.CommandMessage type not found!"); return false; } object obj3 = Activator.CreateInstance(type4); type4.GetField("netId").SetValue(obj3, netIdentity.netId); type4.GetField("componentIndex").SetValue(obj3, (byte)componentIndex); type4.GetField("functionHash").SetValue(obj3, num); type4.GetField("payload").SetValue(obj3, value); Plugin.Log.LogInfo((object)$"[DirectConnect] Sending command: netId={netIdentity.netId}, component={componentIndex}, hash={num}"); NetworkConnection connection = NetworkClient.connection; ((object)connection).GetType().GetMethod("Send", BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo item2 in from m in ((object)connection).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public) where m.Name == "Send" && m.IsGenericMethod select m) { try { item2.MakeGenericMethod(type4).Invoke(connection, new object[2] { obj3, 0 }); Plugin.Log.LogInfo((object)"[DirectConnect] Command sent successfully!"); try { typeof(NetworkWriterPool).GetMethod("Recycle", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, new object[1] { obj }); } catch { } return true; } catch (Exception ex3) { Plugin.Log.LogWarning((object)("[DirectConnect] Send method variant failed: " + ex3.Message)); } } Plugin.Log.LogError((object)"[DirectConnect] All Send method variants failed!"); return false; } catch (Exception ex4) { Plugin.Log.LogError((object)("[DirectConnect] SendCommandDirectly failed: " + ex4.Message)); Plugin.Log.LogError((object)("[DirectConnect] Stack: " + ex4.StackTrace)); return false; } } private static void FixActionTick(object action, string actionTypeName) { try { Type type = AccessTools.TypeByName("MachineManager"); if (type == null) { Plugin.Log.LogWarning((object)"[DirectConnect] MachineManager type not found, cannot fix tick"); return; } object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj == null) { Plugin.Log.LogWarning((object)"[DirectConnect] MachineManager.instance is null, cannot fix tick"); return; } FieldInfo field = type.GetField("curTick", BindingFlags.Instance | BindingFlags.Public); if (field == null) { Plugin.Log.LogWarning((object)"[DirectConnect] MachineManager.curTick field not found"); return; } int num = (int)field.GetValue(obj); Type type2 = action.GetType(); FieldInfo field2 = type2.GetField("info", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = action; Type type3 = type2; if (field2 != null) { object value = field2.GetValue(action); if (value != null) { obj2 = value; type3 = value.GetType(); } } FieldInfo field3 = type3.GetField("tick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field3 == null) { Type baseType = type3.BaseType; while (baseType != null && field3 == null) { field3 = baseType.GetField("tick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); baseType = baseType.BaseType; } } if (field3 != null) { int num2 = (int)field3.GetValue(obj2); field3.SetValue(obj2, num); Plugin.Log.LogInfo((object)$"[DirectConnect] Fixed {actionTypeName} tick on {type3.Name}: {num2} -> {num}"); return; } Plugin.Log.LogWarning((object)("[DirectConnect] Could not find tick field on " + type3.Name)); foreach (FieldInfo item in type3.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Take(10)) { Plugin.Log.LogInfo((object)("[DirectConnect] Field: " + item.Name + " (" + item.FieldType.Name + ")")); } } catch (Exception ex) { Plugin.Log.LogError((object)("[DirectConnect] FixActionTick failed: " + ex.Message)); } } private static void EnsureRelayReady(NetworkIdentity identity) { uint serverRelayNetId = _serverRelayNetId; if (identity.netId != serverRelayNetId) { typeof(NetworkIdentity).GetField("_netId", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(identity, serverRelayNetId); } if (!NetworkClient.spawned.ContainsKey(serverRelayNetId) || (Object)(object)NetworkClient.spawned[serverRelayNetId] != (Object)(object)identity) { NetworkClient.spawned[serverRelayNetId] = identity; } if (identity.connectionToServer == null && NetworkClient.connection != null) { TrySetConnectionToServer(identity); } FieldInfo field = typeof(NetworkIdentity).GetField("_hasAuthority", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && !(bool)field.GetValue(identity)) { field.SetValue(identity, true); } } public static void CmdSendNetworkAction_Prefix(object __instance, object action) { string text = action?.GetType()?.Name ?? "null"; Plugin.Log.LogInfo((object)("[DirectConnect] !!! CmdSendNetworkAction CALLED: " + text)); } public static void SendCommandInternal_Prefix(object __instance, string cmdName, int channelId, bool requiresAuthority) { _sendCommandCount++; if (_sendCommandCount <= 10) { object obj = ((__instance is NetworkBehaviour) ? __instance : null); NetworkIdentity val = ((obj != null) ? ((NetworkBehaviour)obj).netIdentity : null); Plugin.Log.LogInfo((object)$"[DirectConnect] !!! SendCommandInternal #{_sendCommandCount}:"); Plugin.Log.LogInfo((object)("[DirectConnect] cmdName: " + cmdName)); Plugin.Log.LogInfo((object)$"[DirectConnect] channel: {channelId}, requiresAuthority: {requiresAuthority}"); if ((Object)(object)val != (Object)null) { Plugin.Log.LogInfo((object)$"[DirectConnect] netId: {val.netId}"); Plugin.Log.LogInfo((object)$"[DirectConnect] hasAuthority: {val.hasAuthority}"); Plugin.Log.LogInfo((object)$"[DirectConnect] connectionToServer: {val.connectionToServer != null}"); } } } public static Exception SuppressException_Finalizer(Exception __exception) { if (__exception is NullReferenceException) { Plugin.Log.LogWarning((object)("[DirectConnect] SUPPRESSED NullReferenceException: " + __exception.Message)); Plugin.Log.LogWarning((object)("[DirectConnect] Stack trace: " + __exception.StackTrace)); return null; } return __exception; } public static bool RequestCurrentSimTick_Prefix(object __instance) { try { NetworkBehaviour val = (NetworkBehaviour)((__instance is NetworkBehaviour) ? __instance : null); NetworkIdentity val2 = ((val != null) ? val.netIdentity : null); Plugin.Log.LogInfo((object)"[DirectConnect] RequestCurrentSimTick DEBUG:"); Plugin.Log.LogInfo((object)$"[DirectConnect] __instance != null: {__instance != null}"); Plugin.Log.LogInfo((object)$"[DirectConnect] As NetworkBehaviour: {(Object)(object)val != (Object)null}"); Plugin.Log.LogInfo((object)$"[DirectConnect] netIdentity: {(Object)(object)val2 != (Object)null}"); if ((Object)(object)val2 != (Object)null) { Plugin.Log.LogInfo((object)$"[DirectConnect] netId: {val2.netId}"); Plugin.Log.LogInfo((object)$"[DirectConnect] isClient: {val2.isClient}"); Plugin.Log.LogInfo((object)$"[DirectConnect] isServer: {val2.isServer}"); Plugin.Log.LogInfo((object)$"[DirectConnect] hasAuthority: {val2.hasAuthority}"); Plugin.Log.LogInfo((object)$"[DirectConnect] connectionToServer: {val2.connectionToServer != null}"); if (val2.connectionToServer != null) { Plugin.Log.LogInfo((object)$"[DirectConnect] connectionToServer.isReady: {val2.connectionToServer.isReady}"); } } Plugin.Log.LogInfo((object)$"[DirectConnect] NetworkClient.active: {NetworkClient.active}"); Plugin.Log.LogInfo((object)$"[DirectConnect] NetworkClient.isConnected: {NetworkClient.isConnected}"); Plugin.Log.LogInfo((object)$"[DirectConnect] NetworkClient.connection: {NetworkClient.connection != null}"); if (NetworkClient.connection != null) { Plugin.Log.LogInfo((object)$"[DirectConnect] NetworkClient.connection.isReady: {NetworkClient.connection.isReady}"); } if ((Object)(object)val2 != (Object)null && val2.connectionToServer == null && NetworkClient.connection != null) { Plugin.Log.LogInfo((object)"[DirectConnect] FIXING: connectionToServer is null, setting to NetworkClient.connection..."); TrySetConnectionToServer(val2); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DirectConnect] RequestCurrentSimTick debug logging error: " + ex.Message)); } if (__instance == null) { Plugin.Log.LogWarning((object)"[DirectConnect] RequestCurrentSimTick called on null instance - dedicated server mode?"); TryCallOnFinishLoading(); return false; } Plugin.Log.LogInfo((object)"[DirectConnect] RequestCurrentSimTick proceeding to original method..."); return true; } public static Exception RequestCurrentSimTick_Finalizer(Exception __exception) { if (__exception != null) { Plugin.Log.LogWarning((object)("[DirectConnect] RequestCurrentSimTick exception: " + __exception.Message)); TryCallOnFinishLoading(); return null; } return __exception; } public static bool ProcessCurrentSimTick_Prefix(int tick) { try { Plugin.Log.LogInfo((object)$"[DirectConnect] ProcessCurrentSimTick received tick={tick}"); if (AccessTools.TypeByName("SaveState")?.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) == null) { Plugin.Log.LogWarning((object)"[DirectConnect] ProcessCurrentSimTick: SaveState.instance is null - game not ready, skipping tick sync"); return false; } Type type = AccessTools.TypeByName("MachineManager"); object obj = (type?.GetField("instance", BindingFlags.Static | BindingFlags.Public))?.GetValue(null); if (obj == null) { Plugin.Log.LogWarning((object)"[DirectConnect] ProcessCurrentSimTick: MachineManager.instance is null - game not ready, skipping tick sync"); return false; } Type type2 = AccessTools.TypeByName("FactorySimManager"); if (type2 == null) { Plugin.Log.LogWarning((object)"[DirectConnect] ProcessCurrentSimTick: FactorySimManager type not found!"); return false; } object obj2 = type2.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (obj2 == null) { Plugin.Log.LogWarning((object)"[DirectConnect] FactorySimManager.instance is null - can't sync tick"); return false; } Plugin.Log.LogInfo((object)"[DirectConnect] ProcessCurrentSimTick: Setting ALL tick fields properly..."); FieldInfo field = type2.GetField("_targetTick", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj2, tick); Plugin.Log.LogInfo((object)$"[DirectConnect] Set _targetTick = {tick}"); } FieldInfo field2 = type2.GetField("_lastSyncTick", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 != null) { field2.SetValue(obj2, tick); Plugin.Log.LogInfo((object)$"[DirectConnect] Set _lastSyncTick = {tick}"); } FieldInfo field3 = type2.GetField("_timeToProcess", BindingFlags.Instance | BindingFlags.NonPublic); if (field3 != null) { field3.SetValue(obj2, 0f); } FieldInfo field4 = type2.GetField("_needInit", BindingFlags.Instance | BindingFlags.NonPublic); if (field4 != null) { field4.SetValue(obj2, false); } if (type2.GetField("machineManagers", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj2) is Array array && array.Length > 0) { FieldInfo field5 = type.GetField("curTick", BindingFlags.Instance | BindingFlags.Public); if (field5 != null) { for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if (value != null) { field5.SetValue(value, tick); } } Plugin.Log.LogInfo((object)$"[DirectConnect] Set curTick={tick} on all {array.Length} machineManagers"); } } else { Plugin.Log.LogWarning((object)"[DirectConnect] machineManagers array is null or empty!"); } FieldInfo field6 = type.GetField("curTick", BindingFlags.Instance | BindingFlags.Public); if (field6 != null && obj != null) { field6.SetValue(obj, tick); Plugin.Log.LogInfo((object)$"[DirectConnect] Set MachineManager.instance.curTick = {tick}"); } _saveLoaded = true; Plugin.Log.LogInfo((object)$"[DirectConnect] Successfully synced ALL tick fields to {tick}"); return false; } catch (Exception ex) { string text = ex.InnerException?.Message ?? "no inner"; string text2 = ex.InnerException?.StackTrace ?? ""; Plugin.Log.LogError((object)("[DirectConnect] ProcessCurrentSimTick error: " + ex.Message + " | Inner: " + text)); if (!string.IsNullOrEmpty(text2)) { Plugin.Log.LogError((object)("[DirectConnect] Inner stack: " + text2)); } return false; } } public static void OnStopClient_Prefix(object __instance) { try { object arg = __instance.GetType().GetProperty("netId")?.GetValue(__instance); object arg2 = __instance.GetType().GetProperty("isLocalPlayer")?.GetValue(__instance); Plugin.Log.LogWarning((object)$"[DirectConnect] DISCONNECT TRACKING: NetworkedPlayer.OnStopClient called! netId={arg}, isLocalPlayer={arg2}"); StackFrame[] frames = new StackTrace(fNeedFileInfo: true).GetFrames(); if (frames == null) { return; } string text = ""; for (int i = 0; i < Math.Min(frames.Length, 15); i++) { MethodBase method = frames[i].GetMethod(); if (method != null) { text = text + method.DeclaringType?.Name + "." + method.Name + " -> "; } } Plugin.Log.LogWarning((object)("[DirectConnect] DISCONNECT STACK: " + text)); } catch (Exception ex) { Plugin.Log.LogError((object)("[DirectConnect] OnStopClient_Prefix error: " + ex.Message)); } } public static void OnClientDisconnect_Prefix() { Plugin.Log.LogWarning((object)"[DirectConnect] DISCONNECT TRACKING: NetworkManager.OnClientDisconnect called!"); try { StackFrame[] frames = new StackTrace(fNeedFileInfo: true).GetFrames(); if (frames == null) { return; } string text = ""; for (int i = 0; i < Math.Min(frames.Length, 15); i++) { MethodBase method = frames[i].GetMethod(); if (method != null) { text = text + method.DeclaringType?.Name + "." + method.Name + " -> "; } } Plugin.Log.LogWarning((object)("[DirectConnect] DISCONNECT MANAGER STACK: " + text)); } catch { } } public static void OnObjectSpawn_Prefix(SpawnMessage msg) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)$"[NETWORK] SPAWN: netId={msg.netId}, isLocalPlayer={msg.isLocalPlayer}, isOwner={msg.isOwner}, sceneId={msg.sceneId:X16}, assetId={msg.assetId}"); } public static void OnObjectDestroy_Prefix(ObjectDestroyMessage message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogWarning((object)$"[NETWORK] DESTROY: netId={message.netId}"); } public static void HandleRemoteCall_Prefix(object __instance, int componentIndex, int functionHash, object invokeType, object reader, object senderConnection) { } public static void NetworkClient_Connect_Prefix(string address) { Plugin.Log.LogInfo((object)("[NETWORK] CONNECT: Connecting to " + address)); } public static void KCP_OnClientConnected_Prefix() { Plugin.Log.LogInfo((object)"[NETWORK] KCP: Client connected at transport level!"); } public static void KCP_OnClientDisconnected_Prefix() { Plugin.Log.LogError((object)"[NETWORK] KCP: Client DISCONNECTED at transport level!"); try { StackFrame[] frames = new StackTrace(fNeedFileInfo: true).GetFrames(); if (frames == null) { return; } string text = ""; for (int i = 0; i < Math.Min(frames.Length, 20); i++) { MethodBase method = frames[i].GetMethod(); if (method != null) { text = text + method.DeclaringType?.Name + "." + method.Name + " -> "; } } Plugin.Log.LogError((object)("[NETWORK] KCP DISCONNECT STACK: " + text)); } catch { } } public static void KcpClient_Disconnect_Prefix(object __instance) { Plugin.Log.LogError((object)"[NETWORK] KcpClient.Disconnect called!"); try { StackFrame[] frames = new StackTrace(fNeedFileInfo: true).GetFrames(); if (frames == null) { return; } string text = ""; for (int i = 0; i < Math.Min(frames.Length, 25); i++) { MethodBase method = frames[i].GetMethod(); if (method != null) { text = text + method.DeclaringType?.Name + "." + method.Name + " -> "; } } Plugin.Log.LogError((object)("[NETWORK] KcpClient.Disconnect STACK: " + text)); } catch { } } public static void KcpConnection_Disconnect_Prefix(object __instance) { Plugin.Log.LogError((object)"[NETWORK] KcpConnection.Disconnect called!"); try { object arg = __instance.GetType().GetField("state", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(__instance); Plugin.Log.LogError((object)$"[NETWORK] KcpConnection state before disconnect: {arg}"); StackFrame[] frames = new StackTrace(fNeedFileInfo: true).GetFrames(); if (frames == null) { return; } string text = ""; for (int i = 0; i < Math.Min(frames.Length, 30); i++) { MethodBase method = frames[i].GetMethod(); if (method != null) { text = text + method.DeclaringType?.Name + "." + method.Name + " -> "; } } Plugin.Log.LogError((object)("[NETWORK] KcpConnection.Disconnect STACK: " + text)); } catch (Exception ex) { Plugin.Log.LogError((object)("[NETWORK] KcpConnection logging error: " + ex.Message)); } } private static void TrySetConnectionToServer(NetworkIdentity identity) { if ((Object)(object)identity == (Object)null || NetworkClient.connection == null) { return; } try { PropertyInfo property = typeof(NetworkIdentity).GetProperty("connectionToServer", BindingFlags.Instance | BindingFlags.Public); if (!(property != null)) { return; } MethodInfo setMethod = property.GetSetMethod(nonPublic: true); if (setMethod != null) { setMethod.Invoke(identity, new object[1] { NetworkClient.connection }); Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer via setter"); return; } FieldInfo field = typeof(NetworkIdentity).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(identity, NetworkClient.connection); Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer via backing field"); } else { Plugin.Log.LogWarning((object)"[DirectConnect] Could not find connectionToServer setter or backing field"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DirectConnect] TrySetConnectionToServer error: " + ex.Message)); } } private static void TryCallOnFinishLoading() { if (_requestSimTickHandled) { return; } _requestSimTickHandled = true; try { Plugin.Log.LogInfo((object)"[DirectConnect] Calling LoadingUI.OnFinishLoading() directly (dedicated server mode)"); Type type = AccessTools.TypeByName("LoadingUI"); if (type != null) { object obj = AccessTools.Field(type, "instance")?.GetValue(null); if (obj != null) { MethodInfo methodInfo = AccessTools.Method(type, "OnFinishLoading", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(obj, null); Plugin.Log.LogInfo((object)"[DirectConnect] Successfully called LoadingUI.OnFinishLoading()"); } else { Plugin.Log.LogError((object)"[DirectConnect] LoadingUI.OnFinishLoading method not found"); } } else { Plugin.Log.LogWarning((object)"[DirectConnect] LoadingUI.instance is null - may need to wait"); } } else { Plugin.Log.LogError((object)"[DirectConnect] LoadingUI type not found"); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[DirectConnect] Error calling OnFinishLoading: {arg}"); } } } public static class TickFixPatches { private static bool _patchesApplied; private static Type _machineManagerType; private static FieldInfo _mmInstanceField; private static FieldInfo _mmCurTickField; private static Type _factorySimType; private static PropertyInfo _fsmInstanceProp; private static FieldInfo _machineManagersField; private static FieldInfo _prevIndexField; private static bool _cacheInitialized; private static int _prefixCallCount; private static ManualLogSource Log => Plugin.Log; public static void ApplyPatches(Harmony harmony) { if (_patchesApplied) { return; } try { InitializeCache(); PatchUserCodeMethods(harmony); _patchesApplied = true; Log.LogInfo((object)"[TickFix] Tick adjustment patches applied successfully"); } catch (Exception ex) { Log.LogError((object)("[TickFix] Error applying patches: " + ex.Message + "\n" + ex.StackTrace)); } } private static void InitializeCache() { if (!_cacheInitialized) { _machineManagerType = AccessTools.TypeByName("MachineManager"); _mmInstanceField = _machineManagerType?.GetField("instance", BindingFlags.Static | BindingFlags.Public); _mmCurTickField = _machineManagerType?.GetField("curTick", BindingFlags.Instance | BindingFlags.Public); _factorySimType = AccessTools.TypeByName("FactorySimManager"); _fsmInstanceProp = _factorySimType?.GetProperty("instance", BindingFlags.Static | BindingFlags.Public); _machineManagersField = _factorySimType?.GetField("machineManagers", BindingFlags.Instance | BindingFlags.Public); _prevIndexField = _factorySimType?.GetField("prevMachineStateIndex", BindingFlags.Instance | BindingFlags.NonPublic); _cacheInitialized = true; Log.LogInfo((object)"[TickFix] Reflection cache initialized"); } } private static void PatchUserCodeMethods(Harmony harmony) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown Type type = AccessTools.TypeByName("NetworkMessageRelay"); if (type == null) { Log.LogWarning((object)"[TickFix] NetworkMessageRelay not found"); return; } string[] obj = new string[18] { "UserCode_ExchangeMachineInventory", "UserCode_ActivateMachine", "UserCode_HitDestructable", "UserCode_TakeAllFromMachine", "UserCode_EraseMachine", "UserCode_RotateMachine", "UserCode_SetFilter", "UserCode_HarvestPlant", "UserCode_SwapVariant", "UserCode_Replace", "UserCode_Shoot", "UserCode_Ping", "UserCode_ActivatePower", "UserCode_DeactivateTech", "UserCode_HitVoxel", "UserCode_MOLEAction", "UserCode_RpcBuildSimpleMachine", "UserCode_RpcBuildConveyor" }; int num = 0; string[] array = obj; foreach (string text in array) { MethodInfo method = type.GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { try { HarmonyMethod val = new HarmonyMethod(typeof(TickFixPatches), "UserCode_Prefix", (Type[])null); harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; Log.LogInfo((object)("[TickFix] Patched " + text)); } catch (Exception ex) { Log.LogWarning((object)("[TickFix] Failed to patch " + text + ": " + ex.Message)); } } } array = new string[1] { "UserCode_CraftResponse" }; foreach (string text2 in array) { MethodInfo method2 = type.GetMethod(text2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { try { HarmonyMethod val2 = new HarmonyMethod(typeof(TickFixPatches), "UserCode_Response_Prefix", (Type[])null); harmony.Patch((MethodBase)method2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; Log.LogInfo((object)("[TickFix] Patched " + text2 + " (response method)")); } catch (Exception ex2) { Log.LogWarning((object)("[TickFix] Failed to patch " + text2 + ": " + ex2.Message)); } } } Log.LogInfo((object)$"[TickFix] Patched {num} UserCode methods"); } public static void UserCode_Prefix(object[] __args, MethodBase __originalMethod) { try { _prefixCallCount++; string text = __originalMethod?.Name ?? "unknown"; if (!NetworkClient.active || NetworkServer.active) { if (_prefixCallCount <= 5) { Log.LogInfo((object)$"[TickFix] {text}: Skipping (client={NetworkClient.active}, server={NetworkServer.active})"); } return; } if (__args == null || __args.Length == 0) { Log.LogWarning((object)("[TickFix] " + text + ": __args is null or empty")); return; } object obj = __args[0]; if (obj == null) { Log.LogWarning((object)("[TickFix] " + text + ": info arg is null")); } else { AdjustTickToValidRange(obj, text); } } catch (Exception ex) { Log.LogWarning((object)("[TickFix] UserCode_Prefix error: " + ex.Message)); } } public static void UserCode_Response_Prefix(object[] __args, MethodBase __originalMethod) { try { if (NetworkClient.active && !NetworkServer.active && __args != null && __args.Length >= 2) { object obj = __args[1]; if (obj != null) { AdjustTickToValidRange(obj, __originalMethod?.Name ?? "unknown"); } } } catch (Exception ex) { Log.LogWarning((object)("[TickFix] UserCode_Response_Prefix error: " + ex.Message)); } } private static void AdjustTickToValidRange(object obj, string context) { try { FieldInfo field = obj.GetType().GetField("tick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { Log.LogWarning((object)("[TickFix] " + context + ": No 'tick' field found on " + obj.GetType().Name)); return; } int num = (int)field.GetValue(obj); int currentTick = GetCurrentTick(); if (currentTick <= 0) { Log.LogWarning((object)$"[TickFix] {context}: currentTick is {currentTick}, skipping"); return; } int num2 = GetMinValidTick(); if (num2 <= 0) { num2 = currentTick; } if (num < currentTick || num > currentTick + 64) { int num3 = currentTick; field.SetValue(obj, num3); Log.LogInfo((object)$"[TickFix] {context}: Adjusted tick {num} -> {num3} (current={currentTick}, minValid={num2})"); } } catch (Exception ex) { Log.LogWarning((object)("[TickFix] AdjustTickToValidRange error: " + ex.Message)); } } private static int GetCurrentTick() { try { if (_mmInstanceField == null || _mmCurTickField == null) { return 0; } object value = _mmInstanceField.GetValue(null); if (value == null) { return 0; } return (int)_mmCurTickField.GetValue(value); } catch { return 0; } } private static int GetMinValidTick() { try { if (_fsmInstanceProp == null || _machineManagersField == null || _prevIndexField == null) { return 0; } object value = _fsmInstanceProp.GetValue(null); if (value == null) { return 0; } if (!(_machineManagersField.GetValue(value) is Array array) || array.Length == 0) { return 0; } int index = (int)_prevIndexField.GetValue(value); object value2 = array.GetValue(index); if (value2 == null) { return 0; } if (_mmCurTickField == null) { return 0; } return (int)_mmCurTickField.GetValue(value2); } catch { return 0; } } } }