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 WorldRenewal v0.1.0
plugins/WorldRenewal.dll
Decompiled a week 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.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using WorldRenewal.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("WorldRenewal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+d0bbbb5788adc3c1a83f8182b25e876accae748e")] [assembly: AssemblyProduct("WorldRenewal")] [assembly: AssemblyTitle("WorldRenewal")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 WorldRenewal { [BepInPlugin("sansara.WorldRenewal", "World Renewal", "0.1.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.WorldRenewal"; public const string Name = "World Renewal"; public const string Version = "0.1.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DryRun; internal static ConfigEntry<float> IdleDays; internal static ConfigEntry<float> SpawnProtectRadius; internal static ConfigEntry<int> CheckMinutes; internal static ConfigEntry<int> MaxZonesPerPass; internal static ConfigEntry<int> TouchRadius; internal static ConfigEntry<string> Biomes; private float _nextTouch; private float _nextPass; private float _nextSave; private bool _started; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Учёт посещений и восстановление зон."); DryRun = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DryRun", true, "Пробный режим: только журнал «восстановил бы зону …», мир не меняется. false — восстанавливать по-настоящему."); IdleDays = ((BaseUnityPlugin)this).Config.Bind<float>("General", "IdleDays", 7f, "Сколько дней (реальных) зона и её соседи должны простоять без игроков рядом."); CheckMinutes = ((BaseUnityPlugin)this).Config.Bind<int>("General", "CheckMinutes", 10, "Как часто искать зоны для восстановления, минуты."); MaxZonesPerPass = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxZonesPerPass", 10, "Сколько зон восстанавливать за один проход."); SpawnProtectRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Protection", "SpawnProtectRadius", 300f, "Радиус защиты вокруг стартового алтаря, м."); TouchRadius = ((BaseUnityPlugin)this).Config.Bind<int>("Visits", "TouchRadiusZones", 2, "Сколько зон вокруг игрока считаются посещёнными (сервер держит загруженными около двух)."); Biomes = ((BaseUnityPlugin)this).Config.Bind<string>("Protection", "Biomes", "Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth", "Какие биомы восстанавливать: All или имена через запятую (Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth, Ocean)."); Log.LogInfo((object)"World Renewal 0.1.0 загружен."); } private void Update() { if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || WorldGenerator.instance == null || !ZoneSystem.instance.LocationsGenerated) { return; } try { if (!_started) { _started = true; Visits.Load(); _nextPass = Time.time + 60f; _nextSave = Time.time + 300f; } if (Time.time >= _nextTouch) { _nextTouch = Time.time + 10f; World.TouchPlayers(); } if (Time.time >= _nextPass) { _nextPass = Time.time + (float)Mathf.Max(1, CheckMinutes.Value) * 60f; World.Pass(); } if (Time.time >= _nextSave) { _nextSave = Time.time + 300f; Visits.Save(); } } catch (Exception ex) { Log.LogError((object)("Обновление мира: ошибка: " + ex)); } } private void OnDestroy() { if (_started) { Visits.Save(); } } internal static HashSet<Biome> BiomeFilter() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) string text = Biomes.Value.Trim(); if (text.Equals("All", StringComparison.OrdinalIgnoreCase) || text == "") { return null; } HashSet<Biome> hashSet = new HashSet<Biome>(); string[] array = text.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (Enum.TryParse<Biome>(text2.Trim(), ignoreCase: true, out Biome result)) { hashSet.Add(result); } else { Log.LogWarning((object)("Обновление мира: неизвестный биом «" + text2 + "» в Biomes — пропущен.")); } } return hashSet; } } internal static class Visits { private static string _path; public static VisitBook Book { get; private set; } = new VisitBook(); public static void Load() { string text = Path.Combine(Paths.ConfigPath, "sansara.WorldRenewal"); Directory.CreateDirectory(text); string text2 = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); _path = Path.Combine(text, text2 + ".visits"); double timeSeconds = ZNet.instance.GetTimeSeconds(); if (File.Exists(_path)) { Book = VisitBook.Parse(File.ReadAllText(_path), out var badLines); Plugin.Log.LogInfo((object)($"Обновление мира: посещений зон в учёте {Book.Count}" + ((badLines > 0) ? $", испорченных строк пропущено {badLines}." : "."))); return; } Book = new VisitBook(); int num = Book.AddUnknown(World.GeneratedZones(), timeSeconds); Plugin.Log.LogInfo((object)$"Обновление мира: первый запуск — {num} сгенерированных зон считаются посещёнными сейчас."); Save(); } public static void Save() { if (_path != null && Book.Changed) { string text = _path + ".tmp"; File.WriteAllText(text, Book.Serialize()); if (File.Exists(_path)) { File.Delete(_path); } File.Move(text, _path); Book.Changed = false; } } } internal static class World { private enum Kind { Other, Piece, Tombstone, ItemStand, Player, Character } private struct TrialBlock { public int X; public int Y; public int Radius; } private static readonly Dictionary<int, Kind> Kinds = new Dictionary<int, Kind>(); private static HashSet<Vector2s> _generated; private static List<ZDO>[] _bySector; private static bool _reported; private static HashSet<Vector2s> Generated => _generated ?? (_generated = (HashSet<Vector2s>)AccessTools.Field(typeof(ZoneSystem), "m_generatedZones").GetValue(ZoneSystem.instance)); public static IEnumerable<ZoneKey> GeneratedZones() { return Generated.Select((Vector2s z) => new ZoneKey(z.x, z.y)).ToList(); } public static void TouchPlayers() { //IL_0038: 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) //IL_004f: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) double timeSeconds = ZNet.instance.GetTimeSeconds(); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID)); if (val != null) { Vector2s zone = ZoneSystem.GetZone(val.GetPosition()); Visits.Book.Touch(new ZoneKey(zone.x, zone.y), Mathf.Max(0, Plugin.TouchRadius.Value), timeSeconds); } } } public static void Pass() { //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) double timeSeconds = ZNet.instance.GetTimeSeconds(); List<ZoneKey> list = GeneratedZones().ToList(); int num = Visits.Book.AddUnknown(list, timeSeconds); HashSet<Biome> biomes = Plugin.BiomeFilter(); Vector3? spawn = StartTemple(); List<TrialBlock> trialBlocks = MinePortalBlocks(); if (!_reported) { _reported = true; ManualLogSource log = Plugin.Log; object arg; if (!spawn.HasValue) { arg = "не найден"; } else { Vector3 value = spawn.Value; arg = ((Vector3)(ref value)).ToString("F0"); } log.LogInfo((object)($"Обновление мира: стартовый алтарь {arg} (защита {Plugin.SpawnProtectRadius.Value:0} м), " + string.Format("мест Mine Portal {0}, биомы {1}, срок {2:0.##} дн., {3}.", trialBlocks.Count, Plugin.Biomes.Value, Plugin.IdleDays.Value, Plugin.DryRun.Value ? "пробный режим" : "боевой режим"))); } int protectedZones = 0; List<ZoneKey> list2 = RenewalPicker.Pick(list, Visits.Book, Excluded, delegate(ZoneKey z) { bool flag = IsProtected(z); protectedZones += (flag ? 1 : 0); return flag; }, timeSeconds, (double)Plugin.IdleDays.Value * 86400.0, Plugin.MaxZonesPerPass.Value); bool value2 = Plugin.DryRun.Value; int num2 = 0; foreach (ZoneKey item in list2) { Renew(item, value2, timeSeconds, !value2 || num2++ < 20); } if (value2 && list2.Count > 20) { Plugin.Log.LogInfo((object)$"Обновление мира: … и ещё {list2.Count - 20} зон."); } if (list2.Count > 0 || num > 0) { Plugin.Log.LogInfo((object)($"Обновление мира: проход — зон в мире {list.Count}, новых в учёте {num}, защищённых встречено {protectedZones}, " + (value2 ? $"восстановил бы {list2.Count} (пробный режим)." : $"восстановлено {list2.Count}."))); } bool Excluded(ZoneKey z) { //IL_0025: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) Vector3 zonePos = ZoneSystem.GetZonePos(new Vector2s((short)z.X, (short)z.Y)); Vector2 val = new Vector2(zonePos.x, zonePos.z); if (((Vector2)(ref val)).magnitude > 9936f) { return true; } if (spawn.HasValue && Utils.DistanceXZ(zonePos, spawn.Value) < Plugin.SpawnProtectRadius.Value) { return true; } if (trialBlocks.Any((TrialBlock t) => Mathf.Abs(t.X - z.X) <= t.Radius && Mathf.Abs(t.Y - z.Y) <= t.Radius)) { return true; } if (biomes != null) { return !biomes.Contains(WorldGenerator.instance.GetBiome(zonePos)); } return false; } } private static bool IsProtected(ZoneKey zone) { foreach (ZDO item in ZoneObjects(zone)) { switch (KindOf(item.GetPrefab())) { case Kind.Piece: if (item.GetLong(ZDOVars.s_creator, 0L) == 0L) { break; } goto case Kind.Tombstone; case Kind.Tombstone: case Kind.Player: return true; case Kind.ItemStand: if (item.GetInt(ZDOVars.s_item, 0) != 0) { return true; } break; case Kind.Character: if (item.GetBool(ZDOVars.s_tamed, false)) { return true; } break; } } return false; } private static void Renew(ZoneKey zone, bool dry, double now, bool log) { //IL_001e: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_007d: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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) //IL_015d: Unknown result type (might be due to invalid IL or missing references) Vector2s val = default(Vector2s); ((Vector2s)(ref val))..ctor((short)zone.X, (short)zone.Y); List<ZDO> list = ZoneObjects(zone); Vector3 zonePos = ZoneSystem.GetZonePos(val); LocationInstance value; string text = ((!ZoneSystem.instance.m_locationInstances.TryGetValue(val, out value)) ? "—" : (value.m_location?.m_prefab.Name ?? "?")); string text2 = $"{zone} ({WorldGenerator.instance.GetBiome(zonePos)}, локация {text}, объектов {list.Count})"; if (dry) { if (log) { Plugin.Log.LogInfo((object)("Обновление мира: восстановил бы зону " + text2 + ".")); } return; } foreach (ZDO item in list.Where((ZDO z) => KindOf(z.GetPrefab()) != Kind.Player)) { item.SetOwner(ZDOMan.GetSessionID()); ZDOMan.instance.DestroyZDO(item); } Generated.Remove(val); if (ZoneSystem.instance.m_locationInstances.TryGetValue(val, out var value2)) { value2.m_placed = false; ZoneSystem.instance.m_locationInstances[val] = value2; } Visits.Book.Set(zone, now); Plugin.Log.LogInfo((object)("Обновление мира: восстановлена зона " + text2 + ".")); } private static List<ZDO> ZoneObjects(ZoneKey zone) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (_bySector == null) { _bySector = (List<ZDO>[])AccessTools.Field(typeof(ZDOMan), "m_objectsBySector").GetValue(ZDOMan.instance); } uint sector = ZoneSystem.SectorToIndex(new Vector2s((short)zone.X, (short)zone.Y)).Sector; List<ZDO> list = ((sector < _bySector.Length) ? _bySector[sector] : null); if (list == null) { return new List<ZDO>(); } return new List<ZDO>(list); } private static Kind KindOf(int hash) { if (!Kinds.TryGetValue(hash, out var value)) { GameObject prefab = ZNetScene.instance.GetPrefab(hash); value = ((!((Object)(object)prefab == (Object)null)) ? (((Object)(object)prefab.GetComponent<Player>() != (Object)null) ? Kind.Player : (((Object)(object)prefab.GetComponent<TombStone>() != (Object)null) ? Kind.Tombstone : (((Object)(object)prefab.GetComponent<ItemStand>() != (Object)null && (Object)(object)prefab.GetComponent<Piece>() == (Object)null) ? Kind.ItemStand : (((Object)(object)prefab.GetComponent<Piece>() != (Object)null) ? Kind.Piece : (((Object)(object)prefab.GetComponent<Character>() != (Object)null) ? Kind.Character : Kind.Other))))) : Kind.Other); Kinds[hash] = value; } return value; } private static Vector3? StartTemple() { //IL_001c: 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_004a: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in ZoneSystem.instance.m_locationInstances) { if (locationInstance.Value.m_location?.m_prefab.Name == "StartTemple") { return locationInstance.Value.m_position; } } return null; } private static List<TrialBlock> MinePortalBlocks() { List<TrialBlock> list = new List<TrialBlock>(); string text = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); string path = Path.Combine(Paths.ConfigPath, "sansara.MinePortal", text + ".slots.txt"); if (!File.Exists(path)) { return list; } string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 3 && int.TryParse(array2[1], out var result) && int.TryParse(array2[2], out var result2)) { list.Add(new TrialBlock { X = result, Y = result2, Radius = 6 }); } } return list; } } } namespace WorldRenewal.Core { public struct ZoneKey : IEquatable<ZoneKey> { public int X { get; } public int Y { get; } public ZoneKey(int x, int y) { X = x; Y = y; } public bool Equals(ZoneKey other) { if (X == other.X) { return Y == other.Y; } return false; } public override bool Equals(object obj) { if (obj is ZoneKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (X * 73856093) ^ (Y * 19349663); } public override string ToString() { return X.ToString(CultureInfo.InvariantCulture) + "," + Y.ToString(CultureInfo.InvariantCulture); } public IEnumerable<ZoneKey> Block() { for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { yield return new ZoneKey(X + dx, Y + dy); } } } } public sealed class VisitBook { private readonly Dictionary<ZoneKey, double> _last = new Dictionary<ZoneKey, double>(); public int Count => _last.Count; public bool Changed { get; set; } public bool TryGet(ZoneKey zone, out double time) { return _last.TryGetValue(zone, out time); } public void Set(ZoneKey zone, double time) { _last[zone] = time; Changed = true; } public void Touch(ZoneKey center, int radius, double now) { for (int i = -radius; i <= radius; i++) { for (int j = -radius; j <= radius; j++) { Set(new ZoneKey(center.X + i, center.Y + j), now); } } } public int AddUnknown(IEnumerable<ZoneKey> zones, double now) { int num = 0; foreach (ZoneKey zone in zones) { if (!_last.ContainsKey(zone)) { _last[zone] = now; num++; } } Changed |= num > 0; return num; } public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<ZoneKey, double> item in from p in _last orderby p.Key.X, p.Key.Y select p) { stringBuilder.Append(item.Key.X.ToString(CultureInfo.InvariantCulture)).Append(' ').Append(item.Key.Y.ToString(CultureInfo.InvariantCulture)) .Append(' ') .Append(item.Value.ToString("R", CultureInfo.InvariantCulture)) .Append('\n'); } return stringBuilder.ToString(); } public static VisitBook Parse(string text, out int badLines) { VisitBook visitBook = new VisitBook(); badLines = 0; string[] array = (text ?? "").Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { string[] array2 = text2.Split(new char[1] { ' ' }); if (array2.Length == 3 && int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && double.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { visitBook._last[new ZoneKey(result, result2)] = result3; } else { badLines++; } } } return visitBook; } } public static class RenewalPicker { public static List<ZoneKey> Pick(IEnumerable<ZoneKey> generated, VisitBook book, Func<ZoneKey, bool> excluded, Func<ZoneKey, bool> isProtected, double now, double idleSeconds, int max) { HashSet<ZoneKey> gen = new HashSet<ZoneKey>(generated); Dictionary<ZoneKey, bool> protectedCache = new Dictionary<ZoneKey, bool>(); List<ZoneKey> list = new List<ZoneKey>(); if (max <= 0) { return list; } foreach (ZoneKey item in gen.Where((ZoneKey z) => !excluded(z)).OrderBy(LastVisit).ThenBy((ZoneKey z) => z.X) .ThenBy((ZoneKey z) => z.Y)) { if (item.Block().All(Idle) && !item.Block().Any(Protected)) { list.Add(item); if (list.Count >= max) { break; } } } return list; bool Idle(ZoneKey z) { if (book.TryGet(z, out var time)) { return now - time >= idleSeconds; } return true; } double LastVisit(ZoneKey z) { if (!book.TryGet(z, out var time)) { return double.MinValue; } return time; } bool Protected(ZoneKey z) { if (!gen.Contains(z)) { return false; } if (!protectedCache.TryGetValue(z, out var value)) { value = isProtected(z); protectedCache[z] = value; } return value; } } } }