using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Landoria.SharedLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Landoria.StructureProtection")] [assembly: AssemblyDescription("Protects offline structures from deliberate creature targeting and player attacks inside active wards when no authorized player is online.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Landoria")] [assembly: AssemblyProduct("Landoria.StructureProtection")] [assembly: AssemblyCopyright("Copyright © 2026 End3rbyte")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("C6FAAFDF-2D25-4CD3-BD71-59BAA98F1D20")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")] [assembly: AssemblyVersion("1.0.0.40065")] namespace Landoria.StructureProtection { internal static class CreatorActivityPolicy { internal static HashSet GetActiveCreators(IEnumerable onlinePlayers) { return new HashSet(onlinePlayers); } internal static bool IsCreatorActive(long creator, ISet onlinePlayers) { return onlinePlayers.Contains(creator); } } internal static class CreatureProtection { [HarmonyPatch(typeof(StaticTarget), "IsPriorityTarget")] private static class PriorityTargetPatch { private static void Postfix(StaticTarget __instance, ref bool __result) { __result = CreatureProtectionPolicy.CanTarget(StructureProtectionPlugin.Settings.CreatureTargetingEnabled, __result, GetActivity(__instance)); } } [HarmonyPatch(typeof(StaticTarget), "IsRandomTarget")] private static class RandomTargetPatch { private static void Postfix(StaticTarget __instance, ref bool __result) { __result = CreatureProtectionPolicy.CanTarget(StructureProtectionPlugin.Settings.CreatureTargetingEnabled, __result, GetActivity(__instance)); } } [HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(StaticTarget) })] private static class VisibilityPatch { private static void Postfix(StaticTarget target, ref bool __result) { __result = CreatureProtectionPolicy.CanTarget(StructureProtectionPlugin.Settings.CreatureTargetingEnabled, __result, GetActivity(target)); } } private static float GetActivity(StaticTarget target) { return PieceActivity.GetMultiplier((target != null) ? ((Component)target).GetComponentInParent() : null); } } internal static class CreatureProtectionPolicy { internal static bool CanTarget(bool enabled, bool vanillaCanTarget, float activityMultiplier) { if (vanillaCanTarget) { if (enabled) { return activityMultiplier > 0f; } return true; } return false; } } [BepInPlugin("Landoria.StructureProtection", "Landoria.StructureProtection", "1.0.0")] public sealed class StructureProtectionPlugin : LandoriaPlugin { private const string PluginGuid = "Landoria.StructureProtection"; private const string PluginName = "Landoria.StructureProtection"; private const string PluginVersion = "1.0.0"; internal static ModLog Log { get; private set; } internal static StructureProtectionSettings Settings { get; private set; } private void Awake() { Log = InitializePlugin("Landoria.StructureProtection"); Settings = new StructureProtectionSettings(); Settings.InitializeServer(Log); Log.LogInfo("Landoria.StructureProtection 1.0.0 is loaded."); } private void Update() { StructureProtectionSession.Update(); } private void OnDestroy() { StructureProtectionSession.Reset(); Log?.LogInfo("Landoria.StructureProtection 1.0.0 is unloaded."); ShutdownPlugin(); Settings = null; Log = null; } } internal static class StructureProtectionArgumentPolicy { internal static bool Resolve(string[] arguments, string name, bool defaultValue) { string text = ReadValue(arguments, name); if (text == null) { return defaultValue; } if (bool.TryParse(text, out var result)) { return result; } throw new InvalidOperationException("Command-line switch " + name + " requires true or false."); } private static string ReadValue(string[] arguments, string name) { string text = null; for (int i = 0; i < arguments.Length; i++) { if (string.Equals(arguments[i], name, StringComparison.OrdinalIgnoreCase)) { if (text != null || i + 1 >= arguments.Length) { throw new InvalidOperationException("Command-line switch " + name + " is missing or duplicated."); } text = arguments[++i]; } } return text; } } internal sealed class StructureProtectionServerConfiguration { internal bool CreatureTargetingEnabled { get; private set; } internal bool WardPlayerDamageEnabled { get; private set; } internal static StructureProtectionServerConfiguration FromArguments(string[] arguments) { return new StructureProtectionServerConfiguration { CreatureTargetingEnabled = StructureProtectionArgumentPolicy.Resolve(arguments, "--structure-protection-creature-targeting", defaultValue: true), WardPlayerDamageEnabled = StructureProtectionArgumentPolicy.Resolve(arguments, "--structure-protection-ward-player-damage", defaultValue: true) }; } } internal static class StructureProtectionSession { [HarmonyPatch(typeof(Player), "OnSpawned")] private static class PlayerSpawnPatch { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { identityServer = 0L; Update(); } } } private const string IdentityRpc = "Landoria_StructureProtection_Identity"; private const string SnapshotRpc = "Landoria_StructureProtection_Snapshot"; private static readonly Dictionary PeerPlayers = new Dictionary(); private static readonly HashSet OnlinePlayers = new HashSet(); private static ZRoutedRpc registeredRpc; private static long identityServer; internal static void Update() { RegisterRpcs(); SendLocalIdentity(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && RemoveDisconnectedPeers()) { BroadcastSnapshot(); } } internal static void Reset() { registeredRpc = null; identityServer = 0L; ClearState(); } private static void ClearState() { PeerPlayers.Clear(); OnlinePlayers.Clear(); StructureProtectionPlugin.Settings?.ResetClientState(); } internal static HashSet GetOnlinePlayers() { return new HashSet(OnlinePlayers); } private static void RegisterRpcs() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != registeredRpc) { instance.Register("Landoria_StructureProtection_Identity", (Action)ReceiveIdentity); instance.Register("Landoria_StructureProtection_Snapshot", (Action)ReceiveSnapshot); ClearState(); registeredRpc = instance; identityServer = 0L; } } private static void SendLocalIdentity() { ZNet instance = ZNet.instance; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !instance.IsServer() && registeredRpc != null) { ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer != null && serverPeer.m_uid != identityServer) { identityServer = serverPeer.m_uid; registeredRpc.InvokeRoutedRPC(serverPeer.m_uid, "Landoria_StructureProtection_Identity", new object[1] { localPlayer.GetPlayerID() }); } } } private static void ReceiveIdentity(long sender, long playerId) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.instance.GetPeer(sender) != null && playerId != 0L) { PeerPlayers[sender] = playerId; OnlinePlayers.Add(playerId); BroadcastSnapshot(); } } private static bool RemoveDisconnectedPeers() { bool result = false; foreach (long item in new List(PeerPlayers.Keys)) { ZNetPeer peer = ZNet.instance.GetPeer(item); if (peer == null || !peer.IsReady()) { OnlinePlayers.Remove(PeerPlayers[item]); PeerPlayers.Remove(item); result = true; } } return result; } private static void BroadcastSnapshot() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (registeredRpc != null) { ZPackage val = new ZPackage(); WriteMappings(val); registeredRpc.InvokeRoutedRPC(ZRoutedRpc.Everybody, "Landoria_StructureProtection_Snapshot", new object[1] { val }); } } private static void WriteMappings(ZPackage package) { StructureProtectionPlugin.Settings.WriteClientState(package); package.Write(PeerPlayers.Count); foreach (KeyValuePair peerPlayer in PeerPlayers) { package.Write(peerPlayer.Value); } } private static void ReceiveSnapshot(long sender, ZPackage package) { if (IsTrustedServer(sender)) { StructureProtectionPlugin.Settings.ReadClientState(package); OnlinePlayers.Clear(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { long item = package.ReadLong(); OnlinePlayers.Add(item); } } } private static bool IsTrustedServer(long sender) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.IsServer()) { if ((Object)(object)instance != (Object)null) { return instance.IsServer(); } return false; } ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer == null) { return false; } return serverPeer.m_uid == sender; } } internal sealed class StructureProtectionSettings { private bool serverInitialized; internal bool CreatureTargetingEnabled { get; private set; } internal bool WardPlayerDamageEnabled { get; private set; } internal void InitializeServer(ModLog logger) { if (!serverInitialized && ServerRole.IsDedicatedServer) { StructureProtectionServerConfiguration structureProtectionServerConfiguration = StructureProtectionServerConfiguration.FromArguments(Environment.GetCommandLineArgs()); CreatureTargetingEnabled = structureProtectionServerConfiguration.CreatureTargetingEnabled; WardPlayerDamageEnabled = structureProtectionServerConfiguration.WardPlayerDamageEnabled; serverInitialized = true; logger.LogInfo("Effective structure protection settings: " + $"creatureTargeting={CreatureTargetingEnabled}, " + $"wardPlayerDamage={WardPlayerDamageEnabled}."); } } internal void WriteClientState(ZPackage package) { package.Write(CreatureTargetingEnabled); } internal void ReadClientState(ZPackage package) { CreatureTargetingEnabled = package.ReadBool(); } internal void ResetClientState() { if (!serverInitialized) { CreatureTargetingEnabled = false; WardPlayerDamageEnabled = false; } } } internal static class PieceActivity { internal static float GetMultiplier(Piece piece) { if ((Object)(object)piece == (Object)null || !piece.IsPlacedByPlayer()) { return 1f; } long creator = piece.GetCreator(); if (creator == 0L) { return 1f; } if (!CreatorActivityPolicy.IsCreatorActive(creator, StructureProtectionSession.GetOnlinePlayers())) { return 0f; } return 1f; } } internal static class WardProtection { [HarmonyPatch(typeof(PrivateArea), "Awake")] private static class WardAwakePatch { private static void Postfix(PrivateArea __instance) { Wards.Add(__instance); } } [HarmonyPatch(typeof(PrivateArea), "OnDestroy")] private static class WardDestroyPatch { private static void Prefix(PrivateArea __instance) { Wards.Remove(__instance); } } [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] private static class PlayerDamagePatch { private static bool Prefix(WearNTear __instance, HitData hit) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (StructureProtectionPlugin.Settings.WardPlayerDamageEnabled && hit != null && (int)hit.m_hitType == 2) { return !ShouldBlockPlayerDamage(((Component)__instance).transform.position); } return true; } } private static readonly HashSet Wards = new HashSet(); private static bool ShouldBlockPlayerDamage(Vector3 position) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) bool result = false; HashSet onlinePlayers = StructureProtectionSession.GetOnlinePlayers(); foreach (PrivateArea ward in Wards) { if (TryGetWardState(ward, position, out var creator, out var permitted)) { result = true; if (WardProtectionPolicy.HasOnlineAuthorizedPlayer(creator, permitted, onlinePlayers)) { return false; } } } return result; } private static bool TryGetWardState(PrivateArea ward, Vector3 position, out long creator, out List permitted) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) creator = 0L; permitted = null; ZNetView obj = (((Object)(object)ward != (Object)null) ? ((Component)ward).GetComponent() : null); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (val == null || !val.GetBool(ZDOVars.s_enabled, false) || Utils.DistanceXZ(((Component)ward).transform.position, position) >= ward.m_radius) { return false; } Piece component = ((Component)ward).GetComponent(); creator = (((Object)(object)component != (Object)null) ? component.GetCreator() : 0); permitted = ReadPermittedPlayers(val); return creator != 0; } private static List ReadPermittedPlayers(ZDO zdo) { List list = new List(); int num = zdo.GetInt(ZDOVars.s_permitted, 0); for (int i = 0; i < num; i++) { long num2 = zdo.GetLong("pu_id" + i, 0L); if (num2 != 0L) { list.Add(num2); } } return list; } } internal static class WardProtectionPolicy { internal static bool HasOnlineAuthorizedPlayer(long creator, IEnumerable permittedPlayers, ISet onlinePlayers) { if (onlinePlayers.Contains(creator)) { return true; } foreach (long permittedPlayer in permittedPlayers) { if (onlinePlayers.Contains(permittedPlayer)) { return true; } } return false; } } } namespace Landoria.SharedLib { public abstract class LandoriaPlugin : BaseUnityPlugin { private Harmony _harmony; private bool _patchesApplied; protected ModLog InitializePlugin(string pluginGuid) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger); Version version = ((object)this).GetType().Assembly.GetName().Version; modLog.LogInfo($"AssemblyVersion: {version}."); _harmony = new Harmony(pluginGuid); PatchOwnNamespace(modLog); return modLog; } protected void PatchOwnNamespace(ModLog log) { if (_patchesApplied) { log.LogDebug("Harmony patches are already active; skipping registration."); return; } string text = ((object)this).GetType().Namespace; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.Namespace == text) { _harmony.CreateClassProcessor(type).Patch(); } } _patchesApplied = true; log.LogDebug("Harmony patches were applied for the plugin namespace."); } protected void ShutdownPlugin() { if (_patchesApplied) { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _patchesApplied = false; } } } public sealed class ModLog { private readonly ManualLogSource _logger; public ModLog(ManualLogSource logger) { _logger = logger; } public void LogFatal(object message) { Write((LogLevel)1, message); } public void LogError(object message) { Write((LogLevel)2, message); } public void LogWarning(object message) { Write((LogLevel)4, message); } public void LogMessage(object message) { Write((LogLevel)8, message); } public void LogInfo(object message) { Write((LogLevel)16, message); } public void LogDebug(object message) { Write((LogLevel)32, message); } public void Log(LogLevel level, object message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Write(level, message); } private void Write(LogLevel level, object message) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) string arg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); _logger.Log(level, (object)$"[{arg}] {message}"); } } public static class ServerRole { public static bool IsDedicatedServer { get { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return ZNet.instance.IsDedicated(); } return false; } } } }