Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of GetOffMyLawn v0.3.1
plugins/GetOffMyLawn.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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: AssemblyCompany("GetOffMyLawn")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.1.0")] [assembly: AssemblyInformationalVersion("0.3.1")] [assembly: AssemblyProduct("GetOffMyLawn")] [assembly: AssemblyTitle("GetOffMyLawn")] [assembly: AssemblyVersion("0.3.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 GetOffMyLawn { internal sealed class CreatureBlocker { private struct BounceRecord { public int Count; public float WindowStart; } private const int MaxExitIterations = 8; private const float PruneInterval = 60f; private readonly Dictionary<ZDOID, BounceRecord> _bounces = new Dictionary<ZDOID, BounceRecord>(); private readonly List<ZDOID> _prune = new List<ZDOID>(); private readonly HashSet<Faction> _excludedFactions = new HashSet<Faction>(); private readonly PrefabNameFilter _excludedPrefabs = new PrefabNameFilter("ExcludedPrefabs"); private string? _excludedFactionsRaw; private float _pruneTimer; public int Bounced { get; private set; } public int Despawned { get; private set; } public void ResetCounters() { Bounced = 0; Despawned = 0; } public void Reset() { _bounces.Clear(); ResetCounters(); } public void Process(List<WardZone> wards, List<ZDO> creatures, PrefabCatalog catalog, float now, WardShield shield) { //IL_0032: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) RefreshFilters(catalog); float value = GomlConfig.HandBackDelay.Value; BlockMode value2 = GomlConfig.Mode.Value; foreach (ZDO creature in creatures) { if (OwnershipHold.IsHeld(creature.m_uid)) { continue; } Vector3 position = creature.GetPosition(); if (Character.InInterior(position)) { continue; } int num = WardEnforcer.FindContainingWard(wards, position); if (num >= 0 && catalog.TryGet(creature.GetPrefab(), out PrefabCatalog.Entry entry) && entry.Kind == PrefabCatalog.Kind.Creature && ShouldBlock(creature, entry)) { WardZone ward = wards[num]; int count; Vector3 exit; if (value2 == BlockMode.Despawn) { Despawn(creature, entry, in ward, position, "Mode is Despawn", now, shield); } else if (EscalationDue(creature.m_uid, now, out count)) { Despawn(creature, entry, in ward, position, $"bounced {count} times within {GomlConfig.BounceWindowSeconds.Value:0}s", now, shield); } else if (!TryFindExit(wards, num, position, out exit)) { Despawn(creature, entry, in ward, position, "no point outside the overlapping wards within reach", now, shield); } else { Bounce(creature, entry, in ward, position, exit, now, value, shield); } } } _pruneTimer += GomlConfig.ScanInterval.Value; if (_pruneTimer >= 60f) { _pruneTimer = 0f; PruneBounceRecords(now); } } private bool ShouldBlock(ZDO zdo, PrefabCatalog.Entry info) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (_excludedFactions.Contains(info.Faction)) { return false; } if (info.Boss && !GomlConfig.IncludeBosses.Value) { return false; } if (info.Animal && !GomlConfig.IncludeAnimals.Value) { return false; } if (_excludedPrefabs.Contains(zdo.GetPrefab())) { return false; } if (zdo.GetBool(ZDOVars.s_tamed, false)) { return false; } if (info.Tameable && GomlConfig.SkipTamingInProgress.Value && zdo.GetFloat(ZDOVars.s_tameTimeLeft, info.TamingTime) < info.TamingTime - 0.01f) { return false; } if (!GomlConfig.IncludeEventCreatures.Value && zdo.GetBool(ZDOVars.s_eventCreature, false)) { return false; } return true; } private static bool TryFindExit(List<WardZone> wards, int wardIndex, Vector3 from, out Vector3 exit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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) Vector3 val = from; int num = wardIndex; float num2 = GomlConfig.RadiusBonus.Value + GomlConfig.BounceMargin.Value; for (int i = 0; i < 8; i++) { if (num < 0) { break; } WardZone wardZone = wards[num]; float num3 = val.x - wardZone.Center.x; float num4 = val.z - wardZone.Center.z; float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4); if (num5 < 0.05f) { Vector2 val2 = Random.insideUnitCircle; if (((Vector2)(ref val2)).sqrMagnitude < 0.0001f) { val2 = Vector2.right; } ((Vector2)(ref val2)).Normalize(); num3 = val2.x; num4 = val2.y; num5 = 1f; } float num6 = wardZone.Radius + num2; ((Vector3)(ref val))..ctor(wardZone.Center.x + num3 / num5 * num6, val.y, wardZone.Center.z + num4 / num5 * num6); num = WardEnforcer.FindContainingWard(wards, val); } if (num >= 0) { exit = from; return false; } WorldGenerator instance = WorldGenerator.instance; if (instance != null) { float height = instance.GetHeight(val.x, val.z); if (val.y < height + 0.3f) { val.y = height + 0.3f; } } exit = val; return true; } private void Bounce(ZDO zdo, PrefabCatalog.Entry info, in WardZone ward, Vector3 from, Vector3 to, float now, float handBack, WardShield shield) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) bool flag = zdo.IsOwner(); if (flag) { ServerZdo.MoveLocalInstance(zdo, to); } else { OwnershipHold.Claim(zdo, now, handBack); } zdo.SetPosition(to); zdo.Set(ZDOVars.s_velHash, Vector3.zero); RecordBounce(zdo.m_uid, now); Bounced++; shield.Flash(ward.Id, now); Log.Info(string.Format("bounced {0} lv{1} out of {2} at {3}: {4} -> {5}{6}", info.Name, zdo.GetInt(ZDOVars.s_level, 1), ward.Name, Fmt(ward.Center), Fmt(from), Fmt(to), flag ? " (local instance)" : $" (held {handBack:0.##}s)")); } private void Despawn(ZDO zdo, PrefabCatalog.Entry info, in WardZone ward, Vector3 at, string reason, float now, WardShield shield) { //IL_000d: 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_006a: Unknown result type (might be due to invalid IL or missing references) ServerZdo.Despawn(zdo); _bounces.Remove(zdo.m_uid); Despawned++; shield.Flash(ward.Id, now); Log.Info($"despawned {info.Name} lv{zdo.GetInt(ZDOVars.s_level, 1)} inside {ward.Name} at {Fmt(at)}: {reason}."); } private bool EscalationDue(ZDOID id, float now, out int count) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) count = 0; int value = GomlConfig.DespawnAfterBounces.Value; if (value <= 0) { return false; } if (!_bounces.TryGetValue(id, out var value2)) { return false; } if (now - value2.WindowStart > GomlConfig.BounceWindowSeconds.Value) { return false; } count = value2.Count; return value2.Count >= value; } private void RecordBounce(ZDOID id, float now) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (_bounces.TryGetValue(id, out var value) && now - value.WindowStart <= GomlConfig.BounceWindowSeconds.Value) { value.Count++; } else { value = new BounceRecord { Count = 1, WindowStart = now }; } _bounces[id] = value; } private void PruneBounceRecords(float now) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) float value = GomlConfig.BounceWindowSeconds.Value; _prune.Clear(); foreach (KeyValuePair<ZDOID, BounceRecord> bounce in _bounces) { if (now - bounce.Value.WindowStart > value) { _prune.Add(bounce.Key); } } foreach (ZDOID item in _prune) { _bounces.Remove(item); } } private void RefreshFilters(PrefabCatalog catalog) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) _excludedPrefabs.Refresh(GomlConfig.ExcludedPrefabs.Value, catalog); string text = GomlConfig.ExcludedFactions.Value ?? ""; if (string.Equals(text, _excludedFactionsRaw, StringComparison.Ordinal)) { return; } _excludedFactionsRaw = text; _excludedFactions.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { if (Enum.TryParse<Faction>(text2, ignoreCase: true, out Faction result)) { _excludedFactions.Add(result); } else { Log.Warn("ExcludedFactions: '" + text2 + "' is not a Character.Faction name and was ignored."); } } } } private static string Fmt(Vector3 v) { //IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references) return $"({v.x:0.#}, {v.y:0.#}, {v.z:0.#})"; } } internal sealed class DoorCloser { private readonly Dictionary<ZDOID, float> _openSince = new Dictionary<ZDOID, float>(); private readonly HashSet<ZDOID> _seen = new HashSet<ZDOID>(); private readonly List<ZDOID> _prune = new List<ZDOID>(); private readonly PrefabNameFilter _excluded = new PrefabNameFilter("ExcludedDoorPrefabs"); public int Closed { get; private set; } public void ResetCounters() { Closed = 0; } public void Reset() { _openSince.Clear(); ResetCounters(); } public void Process(List<WardZone> wards, List<ZDO> doors, List<Vector3> players, PrefabCatalog catalog, float now) { //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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: 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_01bd: Unknown result type (might be due to invalid IL or missing references) _seen.Clear(); _excluded.Refresh(GomlConfig.ExcludedDoorPrefabs.Value, catalog); float value = GomlConfig.AutoCloseDelay.Value; float value2 = GomlConfig.PlayerClearance.Value; float radiusSqr = value2 * value2; bool value3 = GomlConfig.AutoCloseOnlyInsideWards.Value; float value4 = GomlConfig.HandBackDelay.Value; foreach (ZDO door in doors) { ZDOID uid = door.m_uid; if (OwnershipHold.IsHeld(uid) || _excluded.Contains(door.GetPrefab())) { continue; } if (door.GetInt(ZDOVars.s_state, 0) == 0) { _openSince.Remove(uid); continue; } Vector3 position = door.GetPosition(); if (value3 && WardEnforcer.FindContainingWard(wards, position) < 0) { _openSince.Remove(uid); continue; } _seen.Add(uid); if (!_openSince.TryGetValue(uid, out var value5)) { _openSince[uid] = now; } else if (!(now - value5 < value) && (!(value2 > 0f) || !PlayerWithin(players, position, radiusSqr))) { Close(door, now, value4); _openSince.Remove(uid); Closed++; PrefabCatalog.Entry entry; string text = (catalog.TryGet(door.GetPrefab(), out entry) ? entry.Name : door.GetPrefab().ToString()); Log.Info($"auto-closed {text} at ({position.x:0.#}, {position.y:0.#}, {position.z:0.#}) after {now - value5:0}s open."); } } _prune.Clear(); foreach (KeyValuePair<ZDOID, float> item in _openSince) { if (!_seen.Contains(item.Key)) { _prune.Add(item.Key); } } foreach (ZDOID item2 in _prune) { _openSince.Remove(item2); } } private static bool PlayerWithin(List<Vector3> players, Vector3 position, float radiusSqr) { //IL_000b: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) foreach (Vector3 player in players) { Vector3 val = player - position; if (((Vector3)(ref val)).sqrMagnitude <= radiusSqr) { return true; } } return false; } private static void Close(ZDO zdo, float now, float handBack) { if (!zdo.IsOwner()) { OwnershipHold.Claim(zdo, now, handBack); } zdo.Set(ZDOVars.s_state, 0, false); } } public enum BlockMode { Bounce, Despawn } internal static class GomlConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> VerboseLogging; public static ConfigEntry<float> ScanInterval; public static ConfigEntry<float> HandBackDelay; public static ConfigEntry<bool> CreatureBlockingEnabled; public static ConfigEntry<BlockMode> Mode; public static ConfigEntry<float> WardRadius; public static ConfigEntry<float> RadiusBonus; public static ConfigEntry<float> BounceMargin; public static ConfigEntry<float> MaxVerticalDistance; public static ConfigEntry<bool> IncludeAnimals; public static ConfigEntry<bool> IncludeBosses; public static ConfigEntry<bool> IncludeEventCreatures; public static ConfigEntry<bool> SkipTamingInProgress; public static ConfigEntry<string> ExcludedFactions; public static ConfigEntry<string> ExcludedPrefabs; public static ConfigEntry<int> DespawnAfterBounces; public static ConfigEntry<float> BounceWindowSeconds; public static ConfigEntry<bool> AutoCloseEnabled; public static ConfigEntry<float> AutoCloseDelay; public static ConfigEntry<float> PlayerClearance; public static ConfigEntry<bool> AutoCloseOnlyInsideWards; public static ConfigEntry<string> ExcludedDoorPrefabs; public static ConfigEntry<bool> ShieldEnabled; public static ConfigEntry<bool> AlwaysVisible; public static ConfigEntry<float> AlwaysVisibleInterval; public static ConfigEntry<float> ShieldMinInterval; public static void Bind(ConfigFile config) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Expected O, but got Unknown //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Expected O, but got Unknown //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Expected O, but got Unknown //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Expected O, but got Unknown //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Expected O, but got Unknown //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Expected O, but got Unknown Enabled = config.Bind<bool>("1 - General", "Enabled", true, "Master switch. Off = the mod scans nothing and touches nothing (ownership holds already in flight are still handed back)."); VerboseLogging = config.Bind<bool>("1 - General", "VerboseLogging", false, "Log every bounce, despawn and door close individually. Off = a summary line every 5 minutes when anything happened."); ScanInterval = config.Bind<float>("1 - General", "ScanInterval", 0.3f, new ConfigDescription("Seconds between scans of the ZDOs around every connected player. Lower = creatures get less far into a ward before being pushed out, at the cost of more server CPU. 0.3 keeps a running troll to roughly one stride inside the edge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); HandBackDelay = config.Bind<float>("1 - General", "OwnershipHandBackDelay", 0.75f, new ConfigDescription("Seconds the server keeps ownership of a creature or door it just corrected before handing it back to the nearest client. The owning client must observe at least one frame where it is NOT the owner for its ZSyncTransform to snap the object to the server-written position; below ~0.3 that can be lost on a laggy link, above ~2 the creature sits frozen for that long.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 3f), Array.Empty<object>())); CreatureBlockingEnabled = config.Bind<bool>("2 - Creature Blocking", "Enabled", true, "Keep wild creatures out of every ACTIVE player ward. Only wards a player has switched on count, exactly like vanilla's build protection."); Mode = config.Bind<BlockMode>("2 - Creature Blocking", "Mode", BlockMode.Bounce, "Bounce = teleport the creature to just outside the ward edge every time it is found inside (the ward visibly repels it). Despawn = delete it outright."); WardRadius = config.Bind<float>("2 - Creature Blocking", "WardRadius", 32f, new ConfigDescription("Effective barrier radius in metres of an active ward for creature blocking and door auto-closing. Default is 32m, exactly matching vanilla Valheim's guard_stone radius. Set to 0 to use each ward prefab's native radius. Note: On vanilla clients without client-side mods, player building protection (CheckAccess) is evaluated locally on the client using the client's fixed 32m prefab value; this server setting governs all creature repelling/bouncing/despawning boundaries and door closing zones.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 150f), Array.Empty<object>())); RadiusBonus = config.Bind<float>("2 - Creature Blocking", "RadiusBonus", 0f, new ConfigDescription("Extra metres added to the ward radius (on top of WardRadius) for creature blocking only. Building protection is untouched.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>())); BounceMargin = config.Bind<float>("2 - Creature Blocking", "BounceMargin", 2f, new ConfigDescription("How far outside the (radius + RadiusBonus) edge a bounced creature is placed. Too small and it re-enters on its next stride.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f), Array.Empty<object>())); MaxVerticalDistance = config.Bind<float>("2 - Creature Blocking", "MaxVerticalDistance", 30f, new ConfigDescription("A creature more than this many metres above or below the ward stone is ignored, so a hilltop ward does not police a cave underneath it. 0 = ignore height completely (vanilla's own ward test is purely horizontal).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>())); IncludeAnimals = config.Bind<bool>("2 - Creature Blocking", "IncludeAnimals", true, "Also block passive/fleeing animals (AnimalAI: deer, hares, etc.). Off = only MonsterAI creatures are blocked."); IncludeBosses = config.Bind<bool>("2 - Creature Blocking", "IncludeBosses", false, "Also block bosses. Off by default: bouncing a boss out of a ward makes boss fights trivial and looks bad."); IncludeEventCreatures = config.Bind<bool>("2 - Creature Blocking", "IncludeEventCreatures", true, "Also block raid/event creatures. On = an active ward keeps a raid out of the base entirely. Off = raids behave as vanilla."); SkipTamingInProgress = config.Bind<bool>("2 - Creature Blocking", "SkipTamingInProgress", true, "Never bounce a wild tameable creature whose taming has already started (its TameTimeLeft is below the prefab's taming time), so a boar or wolf being tamed inside a warded pen stays put. Tamed creatures are always left alone regardless."); ExcludedFactions = config.Bind<string>("2 - Creature Blocking", "ExcludedFactions", "Players,PlayerSpawned,TrainingDummy", "Comma-separated Character.Faction names that are never blocked. Valid: Players, AnimalsVeg, ForestMonsters, Undead, Demon, MountainMonsters, SeaMonsters, PlainsMonsters, Boss, MistlandsMonsters, Dverger, PlayerSpawned, TrainingDummy, DeepNorth."); ExcludedPrefabs = config.Bind<string>("2 - Creature Blocking", "ExcludedPrefabs", "", "Comma-separated creature prefab names that are never blocked (e.g. Boar,Wolf,Deer). Case-insensitive."); DespawnAfterBounces = config.Bind<int>("2 - Creature Blocking", "DespawnAfterBounces", 0, new ConfigDescription("In Bounce mode: despawn a creature that has been bounced this many times within BounceWindowSeconds (a monster that keeps charging the same ward). 0 = never despawn, bounce forever.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); BounceWindowSeconds = config.Bind<float>("2 - Creature Blocking", "BounceWindowSeconds", 60f, new ConfigDescription("The window DespawnAfterBounces counts over.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 3600f), Array.Empty<object>())); AutoCloseEnabled = config.Bind<bool>("3 - Auto Close Doors", "Enabled", true, "Automatically close doors and gates that have been left open."); AutoCloseDelay = config.Bind<float>("3 - Auto Close Doors", "Delay", 10f, new ConfigDescription("Seconds a door may stay open before it is closed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f), Array.Empty<object>())); PlayerClearance = config.Bind<float>("3 - Auto Close Doors", "PlayerClearance", 3f, new ConfigDescription("Do not close a door while any player is within this many metres of it (nobody gets a door shut in their face). The delay keeps counting; the door closes as soon as they step away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); AutoCloseOnlyInsideWards = config.Bind<bool>("3 - Auto Close Doors", "OnlyInsideWards", true, "On = only doors standing inside an ACTIVE player ward auto-close (the ward feature). Off = every door near a player, warded or not."); ExcludedDoorPrefabs = config.Bind<string>("3 - Auto Close Doors", "ExcludedDoorPrefabs", "piece_drawbridge,piece_drawbridge_log,Morkhalla_Drawbridge,wood_window", "Comma-separated door prefab names that are never auto-closed. Drawbridges are excluded by default because raising one under a player halfway across is worse than leaving it down, and window shutters because nobody wants their light shut off. Case-insensitive. Key doors and one-way dungeon doors are always skipped."); ShieldEnabled = config.Bind<bool>("4 - Visible Shield", "Enabled", true, "Master switch for visible shield effects. When enabled, active player wards visibly display their vanilla shield dome."); AlwaysVisible = config.Bind<bool>("4 - Visible Shield", "AlwaysVisible", false, "Keep the ward's shield shimmer continuously visible on active wards by pulsing it periodically (fully server-side, vanilla clients see the ward dome without installing anything)."); AlwaysVisibleInterval = config.Bind<float>("4 - Visible Shield", "AlwaysVisibleInterval", 1.3f, new ConfigDescription("Seconds between shield pulses on active wards when AlwaysVisible is enabled. 1.2-1.4s creates an overlapping continuous shimmer wave; higher values produce spaced flashes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); ShieldMinInterval = config.Bind<float>("4 - Visible Shield", "MinInterval", 1.5f, new ConfigDescription("Minimum seconds between shield flashes on the same ward when repelling creatures, so a monster repeatedly charging the same edge does not spam the effect.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>())); } } internal static class Log { private static ManualLogSource? _source; public static void Init(ManualLogSource source) { _source = source; } public static void Info(string message) { if (GomlConfig.VerboseLogging != null && GomlConfig.VerboseLogging.Value) { ManualLogSource? source = _source; if (source != null) { source.LogInfo((object)message); } } } public static void Always(string message) { ManualLogSource? source = _source; if (source != null) { source.LogMessage((object)message); } } public static void Warn(string message) { ManualLogSource? source = _source; if (source != null) { source.LogWarning((object)message); } } public static void Error(string message) { ManualLogSource? source = _source; if (source != null) { source.LogError((object)message); } } } internal static class OwnershipHold { private struct Hold { public long PreviousOwner; public float Due; } [HarmonyPatch(typeof(ZDO), "SetOwner")] internal static class SetOwnerGuard { private static bool Prefix(ZDO __instance, long uid) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (_holds.Count == 0 || _releasing || ZDOMan.instance == null) { return true; } if (uid == ZDOMan.GetSessionID()) { return true; } return !_holds.ContainsKey(__instance.m_uid); } } public const uint RevisionJump = 4096u; private const uint RevisionCeiling = 4293967295u; private static readonly Dictionary<ZDOID, Hold> _holds = new Dictionary<ZDOID, Hold>(); private static readonly List<ZDOID> _due = new List<ZDOID>(); private static bool _releasing; private static bool _warnedCeiling; public static int Count => _holds.Count; public static bool IsHeld(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return _holds.ContainsKey(id); } public static void Claim(ZDO zdo, float now, float handBackDelay) { //IL_006b: 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_007d: Unknown result type (might be due to invalid IL or missing references) long owner = zdo.GetOwner(); zdo.SetOwner(ZDOMan.GetSessionID()); if (zdo.DataRevision < 4293967295u) { zdo.DataRevision += 4096; } else if (!_warnedCeiling) { _warnedCeiling = true; Log.Warn($"ZDO {zdo.m_uid} has DataRevision {zdo.DataRevision}, too close to the uint ceiling to jump safely - corrections to it may lose the race against its owner until the world is reloaded."); } if (!_holds.ContainsKey(zdo.m_uid)) { _holds[zdo.m_uid] = new Hold { PreviousOwner = owner, Due = now + handBackDelay }; } } public static void Update(float now) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (_holds.Count == 0) { return; } _due.Clear(); foreach (KeyValuePair<ZDOID, Hold> hold2 in _holds) { if (hold2.Value.Due <= now) { _due.Add(hold2.Key); } } if (_due.Count == 0) { return; } ZDOMan instance = ZDOMan.instance; foreach (ZDOID item in _due) { Hold hold = _holds[item]; _holds.Remove(item); ZDO val = ((instance != null) ? instance.GetZDO(item) : null); if (val != null && val.IsValid() && val.IsOwner()) { long owner = ResolveHandBackTarget(hold.PreviousOwner, val.GetPosition()); _releasing = true; try { val.SetOwner(owner); } finally { _releasing = false; } } } } public static void Forget(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) _holds.Remove(id); } public static void Clear() { _holds.Clear(); } private static long ResolveHandBackTarget(long previous, Vector3 position) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return 0L; } if (previous != 0L) { ZNetPeer peer = instance.GetPeer(previous); if (peer != null && peer.IsReady() && ZNetScene.InActiveArea(position, peer.GetRefPos())) { return previous; } } long result = 0L; float num = float.MaxValue; foreach (ZNetPeer peer2 in instance.GetPeers()) { if (peer2 == null || !peer2.IsReady()) { continue; } Vector3 refPos = peer2.GetRefPos(); if (ZNetScene.InActiveArea(position, refPos)) { Vector3 val = refPos - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = peer2.m_uid; } } } return result; } } internal static class ServerZdo { public static void Despawn(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) OwnershipHold.Forget(zdo.m_uid); if (!zdo.IsOwner()) { zdo.SetOwner(ZDOMan.GetSessionID()); } ZDOMan.instance.DestroyZDO(zdo); } public static void MoveLocalInstance(ZDO zdo, Vector3 target) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return; } GameObject val = instance.FindInstance(zdo.m_uid); if (!((Object)(object)val == (Object)null)) { Rigidbody component = val.GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.linearVelocity = Vector3.zero; component.angularVelocity = Vector3.zero; component.position = target; } val.transform.position = target; Physics.SyncTransforms(); } } } [BepInPlugin("wubarrk.getoffmylawn", "GetOffMyLawn", "0.3.1")] public class GetOffMyLawnPlugin : BaseUnityPlugin { public const string ModGUID = "wubarrk.getoffmylawn"; public const string ModName = "GetOffMyLawn"; public const string ModVersion = "0.3.1"; private const float ConfigPollInterval = 5f; private readonly Harmony _harmony = new Harmony("wubarrk.getoffmylawn"); private WardEnforcer? _enforcer; private float _configPollTimer; private DateTime _configStamp; public static GetOffMyLawnPlugin Instance { get; private set; } private void Awake() { Instance = this; Log.Init(((BaseUnityPlugin)this).Logger); GomlConfig.Bind(((BaseUnityPlugin)this).Config); _configStamp = ReadConfigStamp(); Log.Always("Starting GetOffMyLawn v0.3.1 (server-side only, built for Valheim 1.0+)..."); try { _harmony.PatchAll(typeof(OwnershipHold.SetOwnerGuard)); } catch (Exception ex) { Log.Warn("could not patch ZDO.SetOwner (" + ex.GetType().Name + ": " + ex.Message + ") - running without the ownership guard; a small share of corrections may be lost to the vanilla ownership sweep."); } _enforcer = new WardEnforcer(); Log.Always("GetOffMyLawn initialized."); } private void Update() { PollConfigFile(); try { _enforcer?.Tick(Time.deltaTime); } catch (Exception arg) { Log.Error($"tick failed: {arg}"); } } private void OnDestroy() { OwnershipHold.Clear(); try { _harmony.UnpatchSelf(); } catch { } } private void PollConfigFile() { _configPollTimer += Time.deltaTime; if (_configPollTimer < 5f) { return; } _configPollTimer = 0f; try { if (!(ReadConfigStamp() == _configStamp)) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ((BaseUnityPlugin)this).Config.Reload(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } _configStamp = ReadConfigStamp(); Log.Always("config file changed on disk - settings reloaded."); } } catch (Exception ex) { Log.Warn("config reload failed: " + ex.GetType().Name + ": " + ex.Message); } } private DateTime ReadConfigStamp() { try { return File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath) ? File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath) : DateTime.MinValue; } catch { return DateTime.MinValue; } } } internal sealed class PrefabCatalog { public enum Kind : byte { Ward, Door, Creature } public sealed class Entry { public Kind Kind; public string Name = ""; public float WardRadius; public bool DoorClosable; public Faction Faction; public bool Boss; public bool Animal; public bool Tameable; public float TamingTime; } private readonly Dictionary<int, Entry> _entries = new Dictionary<int, Entry>(); private readonly Dictionary<string, int> _hashByName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); public int Generation { get; private set; } public int WardCount { get; private set; } public int DoorCount { get; private set; } public int CreatureCount { get; private set; } public bool TryGet(int prefabHash, out Entry entry) { return _entries.TryGetValue(prefabHash, out entry); } public bool TryResolve(string prefabName, out int prefabHash) { return _hashByName.TryGetValue(prefabName, out prefabHash); } public void Build(ZNetScene scene) { _entries.Clear(); _hashByName.Clear(); Generation++; WardCount = 0; DoorCount = 0; CreatureCount = 0; List<string> list = new List<string>(); List<string> list2 = new List<string>(); List<string> list3 = new List<string>(); foreach (GameObject prefab in scene.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Entry entry = Classify(prefab); if (entry != null) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); _entries[stableHashCode] = entry; _hashByName[((Object)prefab).name] = stableHashCode; switch (entry.Kind) { case Kind.Ward: WardCount++; list.Add($"{entry.Name} (r={entry.WardRadius:0.#}m)"); continue; case Kind.Door: DoorCount++; list2.Add(entry.DoorClosable ? entry.Name : (entry.Name + " (never closed: key/one-way)")); continue; } CreatureCount++; list3.Add(entry.Name + (entry.Boss ? " [boss]" : "") + (entry.Animal ? " [animal]" : "") + (entry.Tameable ? " [tameable]" : "") + " <" + ((object)Unsafe.As<Faction, Faction>(ref entry.Faction)/*cast due to .constrained prefix*/).ToString() + ">"); } } Log.Always(string.Format("prefab catalog: {0} player ward type(s) [{1}], {2} door type(s), {3} creature type(s).", WardCount, string.Join(", ", list), DoorCount, CreatureCount)); Log.Info("doors: " + string.Join(", ", list2)); Log.Info("creatures: " + string.Join(", ", list3)); } private static Entry? Classify(GameObject prefab) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Invalid comparison between Unknown and I4 PrivateArea component = prefab.GetComponent<PrivateArea>(); if ((Object)(object)component != (Object)null) { if ((int)component.m_ownerFaction != 0) { return null; } return new Entry { Kind = Kind.Ward, Name = ((Object)prefab).name, WardRadius = component.m_radius }; } Door component2 = prefab.GetComponent<Door>(); if ((Object)(object)component2 != (Object)null) { return new Entry { Kind = Kind.Door, Name = ((Object)prefab).name, DoorClosable = ((Object)(object)component2.m_keyItem == (Object)null && !component2.m_canNotBeClosed) }; } Character component3 = prefab.GetComponent<Character>(); if ((Object)(object)component3 == (Object)null || component3 is Player) { return null; } BaseAI component4 = prefab.GetComponent<BaseAI>(); if ((Object)(object)component4 == (Object)null) { return null; } Tameable component5 = prefab.GetComponent<Tameable>(); return new Entry { Kind = Kind.Creature, Name = ((Object)prefab).name, Faction = component3.m_faction, Boss = (component3.m_boss || (int)component3.m_faction == 8), Animal = (component4 is AnimalAI), Tameable = ((Object)(object)component5 != (Object)null), TamingTime = (((Object)(object)component5 != (Object)null) ? component5.m_tamingTime : 0f) }; } } internal sealed class PrefabNameFilter { private readonly string _settingName; private readonly HashSet<int> _hashes = new HashSet<int>(); private string? _raw; private int _generation = -1; public PrefabNameFilter(string settingName) { _settingName = settingName; } public bool Contains(int prefabHash) { if (_hashes.Count != 0) { return _hashes.Contains(prefabHash); } return false; } public void Refresh(string? configured, PrefabCatalog catalog) { string text = configured ?? ""; if (_generation == catalog.Generation && string.Equals(text, _raw, StringComparison.Ordinal)) { return; } _generation = catalog.Generation; _raw = text; _hashes.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { if (catalog.TryResolve(text2, out var prefabHash)) { _hashes.Add(prefabHash); continue; } _hashes.Add(StringExtensionMethods.GetStableHashCode(text2)); Log.Warn(_settingName + ": no ward, door or creature prefab named '" + text2 + "' is known to this world - kept as an exact-name match only."); } } } } internal readonly struct WardZone { public readonly Vector3 Center; public readonly float Radius; public readonly string Name; public readonly ZDOID Id; public WardZone(Vector3 center, float radius, string name, ZDOID id) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Center = center; Radius = radius; Name = name; Id = id; } } internal sealed class WardEnforcer { private const float SummaryInterval = 300f; private readonly PrefabCatalog _catalog = new PrefabCatalog(); private readonly CreatureBlocker _creatures = new CreatureBlocker(); private readonly DoorCloser _doors = new DoorCloser(); private readonly WardShield _shield = new WardShield(); private ZNetScene? _catalogScene; private float _scanTimer; private float _summaryTimer; private readonly List<ZDO> _sector = new List<ZDO>(); private readonly HashSet<int> _zones = new HashSet<int>(); private readonly HashSet<ZDOID> _seen = new HashSet<ZDOID>(); private readonly List<WardZone> _wards = new List<WardZone>(); private readonly List<ZDO> _creatureZdos = new List<ZDO>(); private readonly List<ZDO> _doorZdos = new List<ZDO>(); private readonly List<Vector3> _players = new List<Vector3>(); public void Tick(float dt) { ZNet instance = ZNet.instance; ZNetScene instance2 = ZNetScene.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null) { if ((Object)(object)_catalogScene != (Object)null) { Reset(); } } else { if (!instance.IsServer()) { return; } if (_catalogScene != instance2) { Reset(); _catalog.Build(instance2); _catalogScene = instance2; } float time = Time.time; OwnershipHold.Update(time); if (!GomlConfig.Enabled.Value) { return; } _scanTimer += dt; if (_scanTimer >= Mathf.Max(0.1f, GomlConfig.ScanInterval.Value)) { _scanTimer = 0f; Scan(instance, time); } _summaryTimer += dt; if (_summaryTimer >= 300f) { _summaryTimer = 0f; if (_creatures.Bounced + _creatures.Despawned + _doors.Closed > 0) { Log.Always($"last {5f:0} min: {_creatures.Bounced} creature bounce(s), {_creatures.Despawned} despawn(s), {_doors.Closed} door(s) auto-closed."); } _creatures.ResetCounters(); _doors.ResetCounters(); } } } private void Reset() { _catalogScene = null; OwnershipHold.Clear(); _creatures.Reset(); _doors.Reset(); _shield.Reset(); _scanTimer = 0f; } private void Scan(ZNet net, float now) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) _players.Clear(); foreach (ZDO allCharacterZDO in net.GetAllCharacterZDOS()) { if (allCharacterZDO != null && allCharacterZDO.IsValid()) { _players.Add(allCharacterZDO.GetPosition()); } } if (_players.Count == 0) { return; } bool value = GomlConfig.CreatureBlockingEnabled.Value; bool value2 = GomlConfig.AutoCloseEnabled.Value; bool flag = GomlConfig.ShieldEnabled.Value && GomlConfig.AlwaysVisible.Value; if (!value && !value2 && !flag) { return; } _zones.Clear(); _seen.Clear(); _wards.Clear(); _creatureZdos.Clear(); _doorZdos.Clear(); SimulationDistance syncedSimulationDistance = net.GetSyncedSimulationDistance(); int num = Mathf.Clamp(((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance, 1, 3); SimulationDistance val = default(SimulationDistance); ((SimulationDistance)(ref val))..ctor(num, 0, true); ZDOMan instance = ZDOMan.instance; foreach (Vector3 player in _players) { Vector2s zone = ZoneSystem.GetZone(player); if (!_zones.Add((zone.x << 16) | (zone.y & 0xFFFF))) { continue; } _sector.Clear(); instance.FindSectorObjects(zone, val, _sector, (List<ZDO>)null); foreach (ZDO item in _sector) { if (item == null || !item.IsValid() || !_catalog.TryGet(item.GetPrefab(), out PrefabCatalog.Entry entry) || !_seen.Add(item.m_uid)) { continue; } switch (entry.Kind) { case PrefabCatalog.Kind.Ward: if (item.GetBool(ZDOVars.s_enabled, false)) { float radius = ((GomlConfig.WardRadius.Value > 0f) ? GomlConfig.WardRadius.Value : entry.WardRadius); _wards.Add(new WardZone(item.GetPosition(), radius, entry.Name, item.m_uid)); } break; case PrefabCatalog.Kind.Door: if (value2 && entry.DoorClosable) { _doorZdos.Add(item); } break; default: if (value) { _creatureZdos.Add(item); } break; } } } if (_wards.Count > 0) { _shield.UpdateActiveWards(_wards, now); } if (value && _wards.Count > 0 && _creatureZdos.Count > 0) { _creatures.Process(_wards, _creatureZdos, _catalog, now, _shield); } if (value2 && _doorZdos.Count > 0) { _doors.Process(_wards, _doorZdos, _players, _catalog, now); } } public static bool InsideWard(in WardZone ward, Vector3 point) { //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_0057: Unknown result type (might be due to invalid IL or missing references) float num = point.x - ward.Center.x; float num2 = point.z - ward.Center.z; float num3 = ward.Radius + GomlConfig.RadiusBonus.Value; if (num * num + num2 * num2 >= num3 * num3) { return false; } float value = GomlConfig.MaxVerticalDistance.Value; if (!(value <= 0f)) { return Mathf.Abs(point.y - ward.Center.y) <= value; } return true; } public static int FindContainingWard(List<WardZone> wards, Vector3 point) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < wards.Count; i++) { if (InsideWard(wards[i], point)) { return i; } } return -1; } } internal sealed class WardShield { private readonly Dictionary<ZDOID, float> _lastPulse = new Dictionary<ZDOID, float>(); private readonly Dictionary<ZDOID, float> _lastReactiveFlash = new Dictionary<ZDOID, float>(); public void Reset() { _lastPulse.Clear(); _lastReactiveFlash.Clear(); } public void UpdateActiveWards(List<WardZone> wards, float now) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!GomlConfig.ShieldEnabled.Value || !GomlConfig.AlwaysVisible.Value) { return; } float num = Mathf.Max(0.5f, GomlConfig.AlwaysVisibleInterval.Value); for (int i = 0; i < wards.Count; i++) { ZDOID id = wards[i].Id; if (!_lastPulse.TryGetValue(id, out var value) || now - value >= num) { _lastPulse[id] = now; ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZNetView.Everybody, id, "FlashShield", Array.Empty<object>()); } } } } public void Flash(ZDOID wardId, float now) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (GomlConfig.ShieldEnabled.Value && (!_lastReactiveFlash.TryGetValue(wardId, out var value) || !(now - value < GomlConfig.ShieldMinInterval.Value))) { _lastReactiveFlash[wardId] = now; _lastPulse[wardId] = now; ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZNetView.Everybody, wardId, "FlashShield", Array.Empty<object>()); } } } } }