diff --git a/PersistentID/Class1.cs b/PersistentID/Class1.cs deleted file mode 100644 index d7df471..0000000 --- a/PersistentID/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace PersistentID; - -public class Class1 -{ - -} diff --git a/PersistentID/PersistentID.cs b/PersistentID/PersistentID.cs new file mode 100644 index 0000000..bf6244f --- /dev/null +++ b/PersistentID/PersistentID.cs @@ -0,0 +1,330 @@ +using HarmonyLib; +using Il2Cpp; +using MelonLoader; + +[assembly: MelonInfo(typeof(PersistentID.PersistentID), "Persistent IDs", "1.0.0", "404NyanFound")] +namespace PersistentID; + +public class PersistentID : MelonMod +{ + private static readonly HashSet PatchedDevices = new(); + + private const string SwitchPrefix = "404ID:Switch:"; + private const string PatchPanelPrefix = "404ID:PatchPanel:"; + private const string ServerPrefix = "404ID:Server:"; + + public override void OnInitializeMelon() => MelonLogger.Msg(System.ConsoleColor.Green, "[Persistent IDs] Hello Administrator!"); + + private static string CleanId(string prefix, string switchId, int hashCode) + { + if (switchId.StartsWith(prefix) && switchId.Contains('_')) + { + string cleanId = switchId.Split('_')[0]; + + if (!PatchedDevices.Contains(hashCode)) + MelonLogger.Msg($"Unity Id Removal | Cleaned Device Id | {switchId} -> {cleanId}"); + + return cleanId; + } + + return switchId; + } + + #region SWITCH ID PERSISTENCE + + [HarmonyPatch(typeof(NetworkSwitch), nameof(NetworkSwitch.Start))] + public static class NewSwitchIdPatch + { + public static void Postfix(NetworkSwitch __instance) + { + string currentId = __instance.switchId; + + if (string.IsNullOrEmpty(currentId) || !currentId.StartsWith(SwitchPrefix)) + { + // Apply new GUID-based ID. Remapping will handle legacy ids. + string uniqueId = $"{SwitchPrefix}{Guid.NewGuid().ToString()[..8]}"; + + // Set values with unique id + __instance.switchId = uniqueId; + __instance.gameObject.name = uniqueId; + + // Persist the unique id in the save + if (SaveSystem.displayToRawMap != null) + { + SaveSystem.displayToRawMap[uniqueId] = uniqueId; + + if (!string.IsNullOrEmpty(currentId)) + SaveSystem.displayToRawMap.Remove(currentId); // Clean up the old legacy ids + } + + // Refresh UI + __instance.UpdateScreenUI(); + } + } + } + + [HarmonyPatch(typeof(NetworkSwitch), nameof(NetworkSwitch.switchId), MethodType.Getter)] + public static class SwitchIdPropertyPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(NetworkSwitch __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(SwitchPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + [HarmonyPatch(typeof(NetworkSwitch), nameof(NetworkSwitch.GenerateUniqueSwitchId))] + public static class UniqueSwitchIdPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(NetworkSwitch __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(SwitchPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + #endregion + + #region PATCH PANEL ID PERSISTENCE + + [HarmonyPatch(typeof(PatchPanel), nameof(PatchPanel.Awake))] + public static class NewPatchPanelIdpatch + { + public static void Postfix(PatchPanel __instance) + { + string currentId = __instance.patchPanelId; + + if (string.IsNullOrEmpty(currentId) || !currentId.StartsWith(PatchPanelPrefix)) + { + // Apply new GUID-based ID. Remapping will handle legacy ids. + string uniqueId = $"{PatchPanelPrefix}{Guid.NewGuid().ToString()[..8]}"; + + // Set values with unique id + __instance.patchPanelId = uniqueId; + __instance.gameObject.name = uniqueId; + + // Persist the unique id in the save + if (SaveSystem.displayToRawMap != null) + { + SaveSystem.displayToRawMap[uniqueId] = uniqueId; + + if (!string.IsNullOrEmpty(currentId)) + SaveSystem.displayToRawMap.Remove(currentId); // Clean up the old legacy ids + } + } + } + } + + [HarmonyPatch(typeof(PatchPanel), nameof(PatchPanel.patchPanelId), MethodType.Getter)] + public static class PatchPanelIdPropertyPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(PatchPanel __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(PatchPanelPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + [HarmonyPatch(typeof(PatchPanel), nameof(PatchPanel.GenerateUniquePatchPanelId))] + public static class UniquePatchPanelIdPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(Server __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(PatchPanelPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + #endregion + + #region SERVER ID PERSISTENCE + + [HarmonyPatch(typeof(Server), nameof(Server.Start))] + public static class NewServerIdPatch + { + public static void Postfix(Server __instance) + { + string currentId = __instance.ServerID; + + if (string.IsNullOrEmpty(currentId) || !currentId.StartsWith(ServerPrefix)) + { + // Apply new GUID-based ID. Remapping will handle legacy ids. + string uniqueId = $"{ServerPrefix}{Guid.NewGuid().ToString()[..8]}"; + + // Set values with unique id + __instance.ServerID = uniqueId; + __instance.gameObject.name = uniqueId; + + // Persist the unique id in the save + if (SaveSystem.displayToRawMap != null) + { + SaveSystem.displayToRawMap[uniqueId] = uniqueId; + + if (!string.IsNullOrEmpty(currentId)) + SaveSystem.displayToRawMap.Remove(currentId); // Clean up the old legacy ids + } + + // Refresh UI + __instance.UpdateServerScreenUI(); + } + } + } + + [HarmonyPatch(typeof(Server), nameof(Server.ServerID), MethodType.Getter)] + public static class ServerIdPropertyPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(Server __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(ServerPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + [HarmonyPatch(typeof(Server), nameof(Server.GenerateUniqueServerId))] + public static class UniqueServerIdPatch + { + // Capture generated ID and clean it! SAY NO TO UNITY "GETINSTANCEID" SUFFIXES! + public static void Postfix(Server __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) return; + + int instanceKey = __instance.GetHashCode(); + + __result = CleanId(ServerPrefix, __result, instanceKey); + + if (!PatchedDevices.Contains(instanceKey)) + PatchedDevices.Add(instanceKey); + } + } + + #endregion + + [HarmonyPatch(typeof(WaypointInitializationSystem), nameof(WaypointInitializationSystem.LoadNetworkState))] + public static class MapDataHealing + { + public static void Prefix(object[] __args) + { + if (__args is null || __args.Length < 3) return; + NetworkSaveData data = (NetworkSaveData)__args[0]; + + MelonLogger.Msg("Checking for legacy Switch IDs in map data..."); + + foreach (var swData in data.switches) + { + string oldId = swData.switchID; + if (!string.IsNullOrEmpty(oldId) && !oldId.StartsWith(SwitchPrefix)) + { + string newGuid = $"{SwitchPrefix}{Guid.NewGuid().ToString()[..8]}"; + + swData.switchID = newGuid; + + int healedCables = 0; + foreach (var cable in data.cables) + { + if (cable.startPoint.switchID == oldId) + { + cable.startPoint.switchID = newGuid; + healedCables++; + } + if (cable.endPoint.switchID == oldId) + { + cable.endPoint.switchID = newGuid; + healedCables++; + } + } + + MelonLogger.Msg($"Legacy Mapping | Switch: {oldId} -> {newGuid} | Healed Cables: {healedCables}"); + } + } + + foreach (var ppData in data.patchPanels) + { + string oldId = ppData.patchPanelID; + if (!string.IsNullOrEmpty(oldId) && !oldId.StartsWith(PatchPanelPrefix)) + { + string newGuid = $"{PatchPanelPrefix}{Guid.NewGuid().ToString()[..8]}"; + ppData.patchPanelID = newGuid; + int healedCables = 0; + foreach (var cable in data.cables) + { + if (cable.startPoint.switchID.StartsWith(oldId)) + { + cable.startPoint.switchID = cable.startPoint.switchID.Replace(oldId, newGuid); + healedCables++; + } + if (cable.endPoint.switchID.StartsWith(oldId)) + { + cable.endPoint.switchID = cable.endPoint.switchID.Replace(oldId, newGuid); + healedCables++; + } + } + MelonLogger.Msg($"Legacy Mapping | Patch Panel: {oldId} -> {newGuid} | Healed Cables: {healedCables}"); + } + } + + foreach (var serverData in data.servers) + { + string oldId = serverData.serverID; + if (!string.IsNullOrEmpty(oldId) && !oldId.StartsWith(ServerPrefix)) + { + string newGuid = $"{ServerPrefix}{Guid.NewGuid().ToString()[..8]}"; + + serverData.serverID = newGuid; + + int healedCables = 0; + foreach (var cable in data.cables) + { + if (cable.startPoint.serverID == oldId) + { + cable.startPoint.serverID = newGuid; + healedCables++; + } + if (cable.endPoint.serverID == oldId) + { + cable.endPoint.serverID = newGuid; + healedCables++; + } + } + MelonLogger.Msg($"Legacy Mapping | Server: {oldId} -> {newGuid} | Healed Cables: {healedCables}"); + } + } + } + + public static void Postfix(WaypointInitializationSystem __instance) => __instance.RequestRouteEvaluation(); + } +} \ No newline at end of file diff --git a/PersistentID/PersistentID.csproj b/PersistentID/PersistentID.csproj index 132c02c..21c758d 100644 --- a/PersistentID/PersistentID.csproj +++ b/PersistentID/PersistentID.csproj @@ -3,7 +3,34 @@ net6.0 enable - enable + disable + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\net6\0Harmony.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\Il2CppAssemblies\Assembly-CSharp.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\net6\Il2CppInterop.Common.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\net6\Il2CppInterop.Runtime.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\Il2CppAssemblies\Il2Cppmscorlib.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\net6\MelonLoader.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\Il2CppAssemblies\Unity.Entities.dll + + + F:\SteamLibrary\steamapps\common\Data Center\MelonLoader\Il2CppAssemblies\UnityEngine.CoreModule.dll + + +