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 Server CreatureRespawn v1.0.0
Server_CreatureRespawn.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("Server_CreatureRespawn")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Server_CreatureRespawn")] [assembly: AssemblyTitle("Server_CreatureRespawn")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 Server_CreatureRespawn { [BepInPlugin("Server_CreatureRespawn", "Server_CreatureRespawn", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "Server_CreatureRespawn"; public const string ModName = "Server_CreatureRespawn"; public const string ModVersion = "1.0.0"; internal static ManualLogSource Log; private Settings _settings; private Scanner _scanner; private FileSystemWatcher _watcher; private volatile bool _configChanged; private float _errorPauseUntil; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; _settings = new Settings(((BaseUnityPlugin)this).Config); _scanner = new Scanner(_settings); SetupConfigWatcher(); Log.LogInfo((object)"Server_CreatureRespawn 1.0.0 loaded. It only does work on the server."); } private void SetupConfigWatcher() { try { _watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _watcher.Changed += delegate { _configChanged = true; }; _watcher.Created += delegate { _configChanged = true; }; _watcher.Renamed += delegate { _configChanged = true; }; _watcher.IncludeSubdirectories = false; _watcher.EnableRaisingEvents = true; } catch (Exception ex) { Log.LogWarning((object)("Config file watcher could not start, config changes need a restart: " + ex.Message)); } } private void Update() { if (_configChanged) { _configChanged = false; try { ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Config reloaded, it applies from the next scan cycle."); } catch (Exception ex) { Log.LogWarning((object)("Config reload failed: " + ex.Message)); } } if (Time.realtimeSinceStartup < _errorPauseUntil) { return; } try { _scanner.Tick(); } catch (Exception arg) { Log.LogError((object)$"Scan error, pausing for 60 seconds: {arg}"); _errorPauseUntil = Time.realtimeSinceStartup + 60f; _scanner.AbortCycle(); } } private void OnApplicationQuit() { _scanner?.Shutdown(); } private void OnDestroy() { _scanner?.Shutdown(); _watcher?.Dispose(); } } internal class Scanner { private struct LocEntry { public Vector2Int Zone; public string Name; public Vector3 Pos; public float Radius; } private class SpawnerInfo { public string Name; public string Creature; public int CreatureHash; public bool OneShot; public bool Boss; public CreatureSpawner Component; } private class RefCounts { public int Inside; public int Outside; public string Tag; } private class RefEntry { public int Placed; public bool Excluded; public readonly SortedDictionary<string, RefCounts> Creatures = new SortedDictionary<string, RefCounts>(StringComparer.OrdinalIgnoreCase); } private enum SpawnerAction { None, Reset, Repaired } private const float InteriorHeight = 3000f; private const float FirstCycleDelay = 30f; private const double FrameBudgetMs = 4.0; private const float DefaultBaseRadius = 20f; private static readonly int DeadSinceKey = StringExtensionMethods.GetStableHashCode("Server_CreatureRespawn_DeadSince"); private readonly Settings _s; private bool _ready; private bool _broken; private readonly Dictionary<int, SpawnerInfo> _spawners = new Dictionary<int, SpawnerInfo>(); private readonly Dictionary<int, float> _baseAreas = new Dictionary<int, float>(); private List<LocEntry> _queue; private int _qi; private float _nextCycle; private long _now; private int _cycleLocations; private int _cycleResets; private readonly List<ZDO> _tmp = new List<ZDO>(); private readonly Dictionary<Vector2Int, List<ZDO>> _spawnerIndex = new Dictionary<Vector2Int, List<ZDO>>(); private readonly Dictionary<Vector2Int, List<ZDO>> _baseIndex = new Dictionary<Vector2Int, List<ZDO>>(); private readonly Dictionary<Vector2Int, List<ZDO>> _creatureIndex = new Dictionary<Vector2Int, List<ZDO>>(); private readonly HashSet<int> _creatureHashes = new HashSet<int>(); private const float AliveCheckRadius = 40f; private readonly Dictionary<ZDOID, int> _stuckPasses = new Dictionary<ZDOID, int>(); private const int StuckPassesNeeded = 2; private int _cycleRepairs; private FieldInfo _objectsByIdField; private bool _loggedIndexTime; private Dictionary<string, RefEntry> _ref = new Dictionary<string, RefEntry>(StringComparer.OrdinalIgnoreCase); private string _lastRefText; private readonly List<ZDO> _baseTmp = new List<ZDO>(); public Scanner(Settings settings) { _s = settings; } public void Tick() { if (!_ready) { TryInit(); } else if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { Shutdown(); } else { if (!_s.Enabled.Value) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (_queue == null) { if (realtimeSinceStartup < _nextCycle) { return; } StartCycle(); } Stopwatch stopwatch = Stopwatch.StartNew(); int num = Math.Max(1, _s.LocationsPerFrame.Value); int num2 = 0; while (_qi < _queue.Count && num2 < num && stopwatch.Elapsed.TotalMilliseconds < 4.0) { ProcessLocation(_queue[_qi++]); num2++; } if (_qi >= _queue.Count) { EndCycle(realtimeSinceStartup); } } } private void TryInit() { if (_broken) { return; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.m_locationInstances == null || ZoneSystem.instance.m_locationInstances.Count == 0) { return; } if (!ResolveGameInternals()) { _broken = true; return; } BuildPrefabInfo(); _queue = null; _ready = true; _nextCycle = Time.realtimeSinceStartup + 30f; int num = 0; foreach (SpawnerInfo value in _spawners.Values) { if (value.OneShot) { num++; } } Plugin.Log.LogInfo((object)$"Watching {num} one-shot spawner type(s); {_baseAreas.Count} player-base piece type(s) block resets. First pass in {30f:0} s."); } public void Shutdown() { _ready = false; _queue = null; _lastRefText = null; _spawnerIndex.Clear(); _baseIndex.Clear(); _creatureIndex.Clear(); _stuckPasses.Clear(); } public void AbortCycle() { _queue = null; _nextCycle = Time.realtimeSinceStartup + 60f; } private bool ResolveGameInternals() { _objectsByIdField = null; FieldInfo[] fields = typeof(ZDOMan).GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsStatic && fieldInfo.FieldType == typeof(Dictionary<ZDOID, ZDO>)) { _objectsByIdField = fieldInfo; break; } } if (_objectsByIdField == null) { Plugin.Log.LogError((object)"Could not find ZDOMan's object dictionary in this game version. The mod is disabled for this session."); return false; } return true; } private void BuildPrefabInfo() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) _spawners.Clear(); _baseAreas.Clear(); foreach (KeyValuePair<int, GameObject> namedPrefab in ZNetScene.instance.m_namedPrefabs) { GameObject value = namedPrefab.Value; if ((Object)(object)value == (Object)null) { continue; } CreatureSpawner component = value.GetComponent<CreatureSpawner>(); if ((Object)(object)component != (Object)null) { GameObject creaturePrefab = component.m_creaturePrefab; if (!((Object)(object)creaturePrefab == (Object)null)) { Character component2 = creaturePrefab.GetComponent<Character>(); _spawners[namedPrefab.Key] = new SpawnerInfo { Name = ((Object)value).name, Creature = ((Object)creaturePrefab).name, CreatureHash = StringExtensionMethods.GetStableHashCode(((Object)creaturePrefab).name), OneShot = (component.m_respawnTimeMinuts <= 0f), Boss = ((Object)(object)component2 != (Object)null && component2.m_boss), Component = component }; } continue; } float num = 0f; EffectArea[] componentsInChildren = value.GetComponentsInChildren<EffectArea>(true); foreach (EffectArea val in componentsInChildren) { if ((val.m_type & 4) != 0) { float num2 = 20f; SphereCollider component3 = ((Component)val).GetComponent<SphereCollider>(); if ((Object)(object)component3 != (Object)null) { Vector3 lossyScale = ((Component)val).transform.lossyScale; num2 = component3.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z)); } num = Mathf.Max(num, num2); } } if (num > 0f) { _baseAreas[namedPrefab.Key] = num; } } } private void StartCycle() { //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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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) _s.Parse(); _now = DateTime.UtcNow.Ticks; _queue = new List<LocEntry>(); _qi = 0; _cycleLocations = (_cycleResets = (_cycleRepairs = 0)); _ref = new Dictionary<string, RefEntry>(StringComparer.OrdinalIgnoreCase); BuildIndexes(); foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in ZoneSystem.instance.m_locationInstances) { LocationInstance value = locationInstance.Value; if (!value.m_placed || value.m_location == null) { continue; } string prefabName = value.m_location.m_prefabName; if (!string.IsNullOrEmpty(prefabName)) { RefEntry refEntry = GetRef(prefabName); refEntry.Placed++; if (_s.ExcludedLocationSet.Contains(prefabName)) { refEntry.Excluded = true; continue; } _queue.Add(new LocEntry { Zone = ZoneOf(value.m_position), Name = prefabName, Pos = value.m_position, Radius = value.m_location.m_exteriorRadius }); } } } private void EndCycle(float t) { //IL_0097: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) _queue = null; _nextCycle = t + (float)Math.Max(10, _s.ScanIntervalSeconds.Value); if (_s.LogResets.Value && (_cycleResets > 0 || _cycleRepairs > 0)) { Plugin.Log.LogInfo((object)$"Pass done: {_cycleResets} spawner(s) reset, {_cycleRepairs} stuck spawner(s) repaired, in {_cycleLocations} location(s)."); } List<ZDOID> list = null; foreach (ZDOID key in _stuckPasses.Keys) { if (ZDOMan.instance.GetZDO(key) == null) { (list ?? (list = new List<ZDOID>())).Add(key); } } if (list != null) { foreach (ZDOID item in list) { _stuckPasses.Remove(item); } } if (_s.WriteReferenceFile.Value) { WriteReference(); } } private void BuildIndexes() { //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) foreach (List<ZDO> value2 in _spawnerIndex.Values) { value2.Clear(); } foreach (List<ZDO> value3 in _baseIndex.Values) { value3.Clear(); } foreach (List<ZDO> value4 in _creatureIndex.Values) { value4.Clear(); } _creatureHashes.Clear(); foreach (SpawnerInfo value5 in _spawners.Values) { if (value5.OneShot) { _creatureHashes.Add(value5.CreatureHash); } } Dictionary<ZDOID, ZDO> dictionary = (Dictionary<ZDOID, ZDO>)_objectsByIdField.GetValue(ZDOMan.instance); if (dictionary == null) { return; } Stopwatch stopwatch = Stopwatch.StartNew(); int num = 0; int num2 = 0; foreach (ZDO value6 in dictionary.Values) { if (value6 == null) { continue; } int prefab = value6.GetPrefab(); Dictionary<Vector2Int, List<ZDO>> dictionary2; if (_spawners.ContainsKey(prefab)) { dictionary2 = _spawnerIndex; num++; } else if (_baseAreas.ContainsKey(prefab)) { dictionary2 = _baseIndex; num2++; } else { if (!_creatureHashes.Contains(prefab)) { continue; } dictionary2 = _creatureIndex; } Vector2Int key = ZoneOf(value6.GetPosition()); if (!dictionary2.TryGetValue(key, out var value)) { value = (dictionary2[key] = new List<ZDO>()); } value.Add(value6); } if (!_loggedIndexTime) { _loggedIndexTime = true; Plugin.Log.LogInfo((object)$"Indexed {num} spawner(s) and {num2} player-base piece(s) out of {dictionary.Count} world objects in {stopwatch.Elapsed.TotalMilliseconds:0} ms."); } } private void ProcessLocation(LocEntry loc) { //IL_000f: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) RefEntry re = GetRef(loc.Name); bool playersNear = PlayersNear(loc.Pos); _tmp.Clear(); Gather(_spawnerIndex, loc.Zone, _tmp); int num = 0; int num2 = 0; foreach (ZDO item in _tmp) { if (_spawners.TryGetValue(item.GetPrefab(), out var value) && BelongsTo(item, loc, out var inside)) { RefCounts counts = GetCounts(re, value); if (inside) { counts.Inside++; } else { counts.Outside++; } switch (HandleSpawner(item, value, loc, playersNear)) { case SpawnerAction.Reset: num++; break; case SpawnerAction.Repaired: num2++; break; } } } if (num > 0 || num2 > 0) { _cycleLocations++; _cycleResets += num; _cycleRepairs += num2; if (_s.LogResets.Value) { Plugin.Log.LogInfo((object)($"{loc.Name} at ({loc.Pos.x:0}, {loc.Pos.z:0}): " + ((num > 0) ? $"{num} spawner(s) reset" : "") + ((num > 0 && num2 > 0) ? ", " : "") + ((num2 > 0) ? $"{num2} stuck spawner(s) repaired" : "") + ".")); } } } private SpawnerAction HandleSpawner(ZDO zdo, SpawnerInfo si, LocEntry loc, bool playersNear) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if (!si.OneShot) { return SpawnerAction.None; } long num = zdo.GetLong(DeadSinceKey, 0L); ZDOConnection connection = zdo.GetConnection(); if (connection == null) { if (num != 0L) { zdo.Set(DeadSinceKey, 0L); } return CheckStuck(zdo, si, playersNear); } _stuckPasses.Remove(zdo.m_uid); if ((int)connection.m_type == 3) { ZDOID target = connection.m_target; if ((((ZDOID)(ref target)).IsNone() || ZDOMan.instance.GetZDO(connection.m_target) == null) && !SameCreatureAliveNear(zdo, si) && !CreatureExcluded(si)) { if (num == 0L) { zdo.Set(DeadSinceKey, _now); return SpawnerAction.None; } if (_now - num < _s.CooldownTicks(loc.Name)) { return SpawnerAction.None; } if (playersNear) { return SpawnerAction.None; } if (_s.BlockInPlayerBase.Value && InsidePlayerBase(zdo.GetPosition())) { return SpawnerAction.None; } if (!ReplaceSpawner(zdo)) { return SpawnerAction.None; } return SpawnerAction.Reset; } } if (num != 0L) { zdo.Set(DeadSinceKey, 0L); } return SpawnerAction.None; } private bool SameCreatureAliveNear(ZDO spawner, SpawnerInfo si) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) Vector3 position = spawner.GetPosition(); float num = 1600f; Vector2Int val = ZoneOf(position); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (!_creatureIndex.TryGetValue(new Vector2Int(((Vector2Int)(ref val)).x + i, ((Vector2Int)(ref val)).y + j), out var value)) { continue; } foreach (ZDO item in value) { if (item != null && item.GetPrefab() == si.CreatureHash) { Vector3 val2 = item.GetPosition() - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num) && ZDOMan.instance.GetZDO(item.m_uid) != null) { return true; } } } } } return false; } private SpawnerAction CheckStuck(ZDO zdo, SpawnerInfo si, bool playersNear) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (!_s.RepairStuckSpawners.Value || CreatureExcluded(si) || !CouldSpawnNow(zdo, si)) { _stuckPasses.Remove(zdo.m_uid); return SpawnerAction.None; } ZDOID uid = zdo.m_uid; if (PlayerWithin3D(zdo.GetPosition(), si.Component.m_triggerDistance)) { _stuckPasses.TryGetValue(uid, out var value); _stuckPasses[uid] = value + 1; return SpawnerAction.None; } if (playersNear) { return SpawnerAction.None; } if (!_stuckPasses.TryGetValue(uid, out var value2) || value2 < 2) { _stuckPasses.Remove(uid); return SpawnerAction.None; } _stuckPasses.Remove(uid); if (!ReplaceWithCleanSpawner(zdo)) { return SpawnerAction.None; } return SpawnerAction.Repaired; } private static bool ReplaceWithCleanSpawner(ZDO old) { //IL_0006: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) try { ZDO val = ZDOMan.instance.CreateNewZDO(old.GetPosition(), old.GetPrefab()); if (val == null) { return false; } val.Persistent = old.Persistent; val.Distant = old.Distant; val.Type = old.Type; val.SetPrefab(old.GetPrefab()); val.SetRotation(old.GetRotation()); val.IncreaseDataRevision(); val.SetOwner(0L); return RemoveSpawner(old); } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Could not repair spawner at {old.GetPosition()}: {ex.Message}"); return false; } } private bool CouldSpawnNow(ZDO zdo, SpawnerInfo si) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) CreatureSpawner component = si.Component; if ((Object)(object)component == (Object)null) { return false; } if (component.m_triggerNoise > 0f) { return false; } if (component.m_spawnGroupRadius > 0f) { return false; } if (!string.IsNullOrEmpty(component.m_requiredGlobalKey) || !string.IsNullOrEmpty(component.m_blockingGlobalKey)) { return false; } bool flag; try { flag = EnvMan.IsDay(); } catch { return false; } if (flag && !component.m_spawnAtDay) { return false; } if (!flag && !component.m_spawnAtNight) { return false; } if (!component.m_spawnInPlayerBase && InsidePlayerBase(zdo.GetPosition())) { return false; } return true; } private bool PlayerWithin3D(Vector3 pos, float range) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0077: Unknown result type (might be due to invalid IL or missing references) float num = range * range; Vector3 val; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null) { val = peer.m_refPos - pos; if (((Vector3)(ref val)).sqrMagnitude <= num) { return true; } } } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { val = ((Component)localPlayer).transform.position - pos; return ((Vector3)(ref val)).sqrMagnitude <= num; } return false; } private static bool RemoveSpawner(ZDO old) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) try { old.SetOwner(ZDOMan.instance.m_sessionID); ZDOMan.instance.DestroyZDO(old); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Could not remove spawner at {old.GetPosition()}: {ex.Message}"); return false; } } private static bool ReplaceSpawner(ZDO old) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_001a: 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_003a: Unknown result type (might be due to invalid IL or missing references) try { ZPackage val = new ZPackage(); old.Serialize(val); val.SetPos(0); ZDO val2 = ZDOMan.instance.CreateNewZDO(old.GetPosition(), old.GetPrefab()); if (val2 == null) { return false; } val2.Deserialize(val); ZDOExtraData.ReleaseConnection(val2.m_uid); val2.Set(DeadSinceKey, 0L); val2.IncreaseDataRevision(); val2.SetOwner(0L); old.SetOwner(ZDOMan.instance.m_sessionID); ZDOMan.instance.DestroyZDO(old); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)$"Could not replace spawner at {old.GetPosition()}: {ex.Message}"); return false; } } private bool CreatureExcluded(SpawnerInfo si) { if ((!_s.SkipBosses.Value || !si.Boss) && !_s.ExcludedCreatureSet.Contains(si.Creature)) { return _s.ExcludedCreatureSet.Contains(si.Name); } return true; } private string CreatureTag(SpawnerInfo si) { if (!si.OneShot) { return "respawns in vanilla, not touched"; } if (_s.SkipBosses.Value && si.Boss) { return "boss, skipped"; } if (_s.ExcludedCreatureSet.Contains(si.Creature) || _s.ExcludedCreatureSet.Contains(si.Name)) { return "excluded"; } return null; } private static bool BelongsTo(ZDO zdo, LocEntry loc, out bool inside) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_004a: Unknown result type (might be due to invalid IL or missing references) Vector3 position = zdo.GetPosition(); inside = position.y > 3000f; if (ZoneOf(position) == loc.Zone) { return true; } if (inside) { return false; } float num = position.x - loc.Pos.x; float num2 = position.z - loc.Pos.z; float num3 = Mathf.Max(loc.Radius, 12f) + 4f; return num * num + num2 * num2 <= num3 * num3; } private bool InsidePlayerBase(Vector3 pos) { //IL_0011: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) _baseTmp.Clear(); Gather(_baseIndex, ZoneOf(pos), _baseTmp); foreach (ZDO item in _baseTmp) { if (_baseAreas.TryGetValue(item.GetPrefab(), out var value)) { Vector3 val = item.GetPosition() - pos; if (((Vector3)(ref val)).sqrMagnitude <= value * value) { return true; } } } return false; } private static void Gather(Dictionary<Vector2Int, List<ZDO>> index, Vector2Int zone, List<ZDO> result) { //IL_001b: 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) for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (!index.TryGetValue(new Vector2Int(((Vector2Int)(ref zone)).x + i, ((Vector2Int)(ref zone)).y + j), out var value)) { continue; } foreach (ZDO item in value) { if (item != null && ZDOMan.instance.GetZDO(item.m_uid) != null) { result.Add(item); } } } } } private static Vector2Int ZoneOf(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) return new Vector2Int(Mathf.FloorToInt((p.x + 32f) / 64f), Mathf.FloorToInt((p.z + 32f) / 64f)); } private bool PlayersNear(Vector3 pos) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0077: Unknown result type (might be due to invalid IL or missing references) float value = _s.PlayerSafeDistance.Value; float num = value * value; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && XZDist2(peer.m_refPos, pos) < num) { return true; } } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && XZDist2(((Component)localPlayer).transform.position, pos) < num) { return true; } return false; } private static float XZDist2(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2; } private RefEntry GetRef(string name) { if (!_ref.TryGetValue(name, out var value)) { value = new RefEntry(); _ref[name] = value; } return value; } private RefCounts GetCounts(RefEntry re, SpawnerInfo si) { if (!re.Creatures.TryGetValue(si.Creature, out var value)) { value = new RefCounts(); re.Creatures[si.Creature] = value; } value.Tag = CreatureTag(si); return value; } private void WriteReference() { List<string> list = new List<string>(_ref.Keys); list.Sort(StringComparer.OrdinalIgnoreCase); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Server_CreatureRespawn reference - rewritten after a pass whenever something changed."); stringBuilder.AppendLine("# Only locations already generated (visited) in this world are listed."); stringBuilder.AppendLine("# inside = in the dungeon/cave interior, outside = at ground level. Counts are spawners across all placed copies."); stringBuilder.AppendLine("# Location names -> ExcludedLocations / LocationCooldowns, creature names -> ExcludedCreatures."); stringBuilder.AppendLine(); foreach (string item in list) { RefEntry refEntry = _ref[item]; stringBuilder.Append(item).Append(" (").Append(refEntry.Placed) .Append(" placed"); if (refEntry.Excluded) { stringBuilder.Append(", location excluded"); } stringBuilder.AppendLine(")"); if (!refEntry.Excluded) { if (refEntry.Creatures.Count == 0) { stringBuilder.AppendLine(" no creature spawners"); } foreach (KeyValuePair<string, RefCounts> creature in refEntry.Creatures) { RefCounts value = creature.Value; stringBuilder.Append(" ").Append(creature.Key).Append(" (inside: ") .Append(value.Inside) .Append(", outside: ") .Append(value.Outside); if (value.Tag != null) { stringBuilder.Append(", ").Append(value.Tag); } stringBuilder.AppendLine(")"); } } stringBuilder.AppendLine(); } string text = stringBuilder.ToString(); if (text == _lastRefText) { return; } try { File.WriteAllText(Path.Combine(Paths.ConfigPath, "Server_CreatureRespawn.reference.txt"), text); _lastRefText = text; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not write the reference file: " + ex.Message)); } } } internal class Settings { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<int> ScanIntervalSeconds; public readonly ConfigEntry<int> LocationsPerFrame; public readonly ConfigEntry<float> PlayerSafeDistance; public readonly ConfigEntry<string> ExcludedLocations; public readonly ConfigEntry<bool> LogResets; public readonly ConfigEntry<bool> WriteReferenceFile; public readonly ConfigEntry<float> CooldownHours; public readonly ConfigEntry<bool> SkipBosses; public readonly ConfigEntry<bool> BlockInPlayerBase; public readonly ConfigEntry<bool> RepairStuckSpawners; public readonly ConfigEntry<string> ExcludedCreatures; public readonly ConfigEntry<string> LocationCooldowns; public HashSet<string> ExcludedLocationSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public HashSet<string> ExcludedCreatureSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private Dictionary<string, float> _locCooldowns = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); public Settings(ConfigFile cfg) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown Enabled = cfg.Bind<bool>("1 - General", "Enabled", true, "Master switch. When false nothing is tracked or reset."); ScanIntervalSeconds = cfg.Bind<int>("1 - General", "ScanIntervalSeconds", 120, new ConfigDescription("Seconds to wait between full passes over all placed locations. Cooldowns are only checked once per pass, so this is also the timing precision.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 3600), Array.Empty<object>())); LocationsPerFrame = cfg.Bind<int>("1 - General", "LocationsPerFrame", 10, new ConfigDescription("How many locations are checked per server frame (also capped at ~4 ms per frame). Lower it if the server ever stutters during a pass.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>())); PlayerSafeDistance = cfg.Bind<float>("1 - General", "PlayerSafeDistance", 150f, new ConfigDescription("A location is only reset while no player is within this many metres of it (horizontal distance, so it also covers players inside its dungeon). Dead creatures are still tracked while players are near.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(32f, 1000f), Array.Empty<object>())); ExcludedLocations = cfg.Bind<string>("1 - General", "ExcludedLocations", "StartTemple", "Comma-separated location names that are never touched. StartTemple is always excluded. Names are listed in the reference file (see WriteReferenceFile)."); LogResets = cfg.Bind<bool>("1 - General", "LogResets", true, "Log every location where a spawner was reset, plus a summary per pass."); WriteReferenceFile = cfg.Bind<bool>("1 - General", "WriteReferenceFile", false, "Write BepInEx/config/Server_CreatureRespawn.reference.txt after each pass: every placed location type with its creature spawners (inside/outside counts), using the exact names these settings expect. Off by default so the mod creates no extra files."); CooldownHours = cfg.Bind<float>("2 - Creatures", "CooldownHours", 24f, "Real-world hours a spawner must stay empty (its creature dead) before it is reset. 24 = 1 day. Time counts while the server is running with nobody online and also while it is offline. Allowed range: from 0.1 to 8760 (use a dot for decimals)."); SkipBosses = cfg.Bind<bool>("2 - Creatures", "SkipBosses", true, "Never reset spawners whose creature is flagged as a boss in the game data."); BlockInPlayerBase = cfg.Bind<bool>("2 - Creatures", "BlockInPlayerBase", true, "Don't reset a spawner while it is inside a player base area (workbench, fire, bed and other pieces that stop monster spawns). It resets once the area is gone and the cooldown has passed."); RepairStuckSpawners = cfg.Bind<bool>("2 - Creatures", "RepairStuckSpawners", true, "Detect one-shot spawners that never spawn even though a player stood within their trigger range for 2 passes in a row while their spawn rules allowed it, and replace them with a fresh copy once nobody is near."); ExcludedCreatures = cfg.Bind<string>("2 - Creatures", "ExcludedCreatures", "Skeleton_Hildir, Fenring_Cultist_Hildir, GoblinBrute_Hildir, GoblinShaman_Hildir", "Comma-separated creature prefab names (or spawner prefab names) that never respawn. Defaults are Hildir's quest bosses, which drop her chests."); LocationCooldowns = cfg.Bind<string>("2 - Creatures", "LocationCooldowns", "", "Per-location cooldown overrides in hours, e.g. \"TrollCave02=48, Crypt3=120\". Locations not listed use CooldownHours."); Parse(); } public void Parse() { ExcludedLocationSet = ParseSet(ExcludedLocations.Value); ExcludedLocationSet.Add("StartTemple"); ExcludedCreatureSet = ParseSet(ExcludedCreatures.Value); _locCooldowns = ParseCooldowns(LocationCooldowns.Value, "Creatures.LocationCooldowns"); } public long CooldownTicks(string location) { float value; return HoursToTicks(ClampHours(_locCooldowns.TryGetValue(location, out value) ? value : CooldownHours.Value)); } private static float ClampHours(float hours) { return Math.Max(0.1f, Math.Min(8760f, hours)); } private static long HoursToTicks(float hours) { return (long)(Math.Max(0.0, hours) * 36000000000.0); } private static IEnumerable<string> Split(string value) { return from s in (value ?? "").Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim() into s where s.Length > 0 select s; } private static HashSet<string> ParseSet(string value) { return new HashSet<string>(Split(value), StringComparer.OrdinalIgnoreCase); } private static Dictionary<string, float> ParseCooldowns(string value, string entryName) { Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); foreach (string item in Split(value)) { int num = item.IndexOfAny(new char[2] { '=', ':' }); if (num <= 0 || num == item.Length - 1) { Plugin.Log.LogWarning((object)(entryName + ": ignoring \"" + item + "\", expected Name=Hours.")); continue; } string key = item.Substring(0, num).Trim(); if (!float.TryParse(item.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || result <= 0f) { Plugin.Log.LogWarning((object)(entryName + ": ignoring \"" + item + "\", hours must be a positive number (use . for decimals).")); } else { dictionary[key] = result; } } return dictionary; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }