using System; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace BakaSwampKeyPersist; [BepInPlugin("baka.SwampKeyPersist", "Swamp Key Persist", "1.1.0")] public class SwampKeyPersistPlugin : BaseUnityPlugin { public const string GUID = "baka.SwampKeyPersist"; public const string NAME = "Swamp Key Persist"; public const string VERSION = "1.1.0"; internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry ProtectAllKeyedDoors; internal static ConfigEntry CryptDoorNameContains; private Harmony _harmony; private void Awake() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch. When true, the door key (Swamp/Crypt Key) is never consumed when opening a Sunken Crypt gate. Only takes effect on servers that also run this mod (anti-cheat)."); ProtectAllKeyedDoors = ((BaseUnityPlugin)this).Config.Bind("General", "ProtectAllKeyedDoors", false, "When true, protect the key of ANY keyed door from being consumed, not just Sunken Crypt gates."); CryptDoorNameContains = ((BaseUnityPlugin)this).Config.Bind("General", "CryptDoorNameContains", "sunken_crypt_gate", "Comma-separated substrings. A door whose GameObject name contains any of these is treated as a crypt gate whose key must persist."); _harmony = new Harmony("baka.SwampKeyPersist"); _harmony.PatchAll(); Log.LogInfo((object)"Swamp Key Persist v1.1.0 loaded - keys persist on sanctioning servers/host (server-gated anti-cheat)."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class ServerSanction { internal const string HELLO_RPC = "SwampKeyPersist_ServerHello"; internal const int PROTO = 1; internal static bool ServerHasMod; internal static bool Allowed() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } if (instance.IsServer()) { return true; } return ServerHasMod; } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class ZNet_OnNewConnection_Patch { private static void Postfix(ZNet __instance, ZNetPeer peer) { if (peer == null || peer.m_rpc == null) { return; } try { peer.m_rpc.Register("SwampKeyPersist_ServerHello", (Action)RPC_ServerHello); if ((Object)(object)__instance != (Object)null && __instance.IsServer()) { peer.m_rpc.Invoke("SwampKeyPersist_ServerHello", new object[1] { 1 }); } else { ServerSanction.ServerHasMod = false; } } catch (Exception ex) { if (SwampKeyPersistPlugin.Log != null) { SwampKeyPersistPlugin.Log.LogWarning((object)("SwampKeyPersist handshake setup failed: " + ex.Message)); } } } private static void RPC_ServerHello(ZRpc rpc, int proto) { ServerSanction.ServerHasMod = true; if (SwampKeyPersistPlugin.Log != null) { SwampKeyPersistPlugin.Log.LogInfo((object)("Server sanctions SwampKeyPersist (proto " + proto + ") - key protection ACTIVE on this server.")); } } } [HarmonyPatch(typeof(ZNet), "Shutdown", new Type[] { typeof(bool) })] internal static class ZNet_Shutdown_Patch { private static void Postfix() { ServerSanction.ServerHasMod = false; } } [HarmonyPatch(typeof(Door), "Open")] internal static class Door_Open_Guard { internal static bool Active; internal static string ProtectedKeyName; [HarmonyPriority(800)] private static void Prefix(Door __instance) { Active = false; ProtectedKeyName = null; if ((Object)(object)__instance == (Object)null || SwampKeyPersistPlugin.Enabled == null || !SwampKeyPersistPlugin.Enabled.Value || !ServerSanction.Allowed()) { return; } ItemDrop keyItem = __instance.m_keyItem; if ((Object)(object)keyItem == (Object)null) { return; } bool flag = false; try { string text = (((Object)(object)((Component)__instance).gameObject != (Object)null) ? ((Object)((Component)__instance).gameObject).name : ""); string value = SwampKeyPersistPlugin.CryptDoorNameContains.Value; if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(text)) { string[] array = value.Split(new char[1] { ',' }); foreach (string text2 in array) { string text3 = text2.Trim(); if (text3.Length > 0 && text.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; break; } } } } catch { } if (!flag && !SwampKeyPersistPlugin.ProtectAllKeyedDoors.Value) { return; } Active = true; try { if (keyItem.m_itemData != null && keyItem.m_itemData.m_shared != null) { ProtectedKeyName = keyItem.m_itemData.m_shared.m_name; } } catch { ProtectedKeyName = null; } } private static void Finalizer() { Active = false; ProtectedKeyName = null; } internal static bool ShouldProtect(ItemData item) { if (!Active || item == null || item.m_shared == null) { return false; } if (string.IsNullOrEmpty(ProtectedKeyName)) { return true; } return item.m_shared.m_name == ProtectedKeyName; } } [HarmonyPatch(typeof(Inventory), "RemoveOneItem", new Type[] { typeof(ItemData) })] internal static class Inventory_RemoveOneItem_Guard { private static bool Prefix(ItemData item, ref bool __result) { if (Door_Open_Guard.ShouldProtect(item)) { __result = true; if (SwampKeyPersistPlugin.Log != null) { SwampKeyPersistPlugin.Log.LogInfo((object)("Preserved door key '" + item.m_shared.m_name + "' (blocked consume on crypt-gate open).")); } return false; } return true; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData) })] internal static class Inventory_RemoveItem_Guard { private static bool Prefix(ItemData item, ref bool __result) { if (Door_Open_Guard.ShouldProtect(item)) { __result = true; return false; } return true; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class Inventory_RemoveItemAmount_Guard { private static bool Prefix(ItemData item, ref bool __result) { if (Door_Open_Guard.ShouldProtect(item)) { __result = true; return false; } return true; } }