diff --git a/HarmonySrc/CinerariumKeepAlive.cs b/HarmonySrc/CinerariumKeepAlive.cs new file mode 100644 index 0000000..90f1ca2 --- /dev/null +++ b/HarmonySrc/CinerariumKeepAlive.cs @@ -0,0 +1,147 @@ +using System.Collections.Generic; +using HarmonyLib; +using UnityEngine; + +namespace NecromancerTome +{ + /// + /// Урны работают, пока игрок далеко (решение 23.10.2026, #78: «Харвестры работают только когда рядом» - «если игрок далеко + /// можно выключить анимацию духов... и пусть духи продолжат работать. С остальными тоже. Если ящики переполнятся, то + /// харвестры встанут»; досчёт по времени не годится - сборщик следит за ростом и сеет). Игра обновляет участки, за + /// которыми кто-то наблюдает (ChunkManager: активный набор = все участки наблюдателей), - поэтому у каждой группы урн + /// свой наблюдатель без видимой геометрии: участки не выгружаются, урны и грядки идут. Анимация вдали и так выключена + /// (CinerariumLoad.AnimateNearest). Группа - клетка CellSize×CellSize блоков по реестру урн сейва (CinerariumRegistry), + /// наблюдатель в её центре держит (ViewDim+2) участков во все стороны - клетку и рабочие зоны у её краёв. + /// Нагрузку мерить по логу (Heap/RSS, Chunks); если тяжело - сначала оптимизация, запасной путь - урны стоят без игрока. + /// Консоль: necrournkeep on|off|dim N. + /// + public static class CinerariumKeepAlive + { + public static bool Enabled = true; + public const int CellSize = 64; + /// Радиус наблюдателя в участках минус 2 (так считает ChunkManager): 1 - 7×7 участков. + public static int ViewDim = 1; + const float SyncSeconds = 10f; + + static readonly Dictionary _observers = new Dictionary(); + static World _world; + static float _nextSync; + + public static int Count => _observers.Count; + + public static void Tick() + { + if (Time.time < _nextSync) + { + return; + } + _nextSync = Time.time + SyncSeconds; + GameManager gm = GameManager.Instance; + World w = gm?.World; + if (w != _world) + { + _observers.Clear(); // мир сменился - прежние наблюдатели ушли вместе с ним + _world = w; + } + if (w == null || !ConnectionManager.Instance.IsServer) + { + return; + } + // Клетки с урнами и средняя высота урн в каждой. + Dictionary cells = new Dictionary(); + if (Enabled) + { + foreach (Vector3i p in CinerariumRegistry.All()) + { + Vector2i c = new Vector2i(Mathf.FloorToInt(p.x / (float)CellSize), Mathf.FloorToInt(p.z / (float)CellSize)); + cells.TryGetValue(c, out (int sum, int n) v); + cells[c] = (v.sum + p.y, v.n + 1); + } + } + List gone = null; + foreach (KeyValuePair kv in _observers) + { + if (!cells.ContainsKey(kv.Key) || kv.Value.viewDim != ViewDim) + { + (gone ??= new List()).Add(kv.Key); + } + } + if (gone != null) + { + foreach (Vector2i c in gone) + { + gm.RemoveChunkObserver(_observers[c]); + _observers.Remove(c); + Debug.Log("[NecromancerTome] CinerariumKeepAlive: группа урн " + c + " - наблюдатель снят, всего " + _observers.Count); + } + } + foreach (KeyValuePair kv in cells) + { + if (_observers.ContainsKey(kv.Key)) + { + continue; + } + Vector3 center = new Vector3((kv.Key.x + 0.5f) * CellSize, kv.Value.sum / (float)kv.Value.n, (kv.Key.y + 0.5f) * CellSize); + _observers[kv.Key] = gm.AddChunkObserver(center, false, ViewDim, -1); + Debug.Log("[NecromancerTome] CinerariumKeepAlive: группа урн " + kv.Key + " (" + kv.Value.n + " шт.) - наблюдатель в " + center + + ", всего " + _observers.Count); + } + } + + /// Сразу пересобрать (команда). + public static void SyncNow() + { + _nextSync = 0f; + Tick(); + } + } + + [HarmonyPatch(typeof(World), nameof(World.OnUpdateTick))] + public static class Patch_World_OnUpdateTick_CinerariumKeepAlive + { + public static void Postfix() + { + CinerariumKeepAlive.Tick(); + } + } + + public class ConsoleCmdNecroUrnKeep : ConsoleCmdAbstract + { + public override bool IsExecuteOnClient => false; + + public override bool AllowedInMainMenu => false; + + public override string[] getCommands() + { + return new string[] { "necrournkeep" }; + } + + public override string getDescription() + { + return "Necromancer's Tome: keep urn areas loaded so urns work while you are away - switch."; + } + + public override string getHelp() + { + return "necrournkeep - status\n" + + "necrournkeep on|off - off: urns stop when you are far (areas unload), as before\n" + + "necrournkeep dim - observer size: (n+2) chunks each way around a 64-block group (default 1)"; + } + + public override void Execute(List _params, CommandSenderInfo _senderInfo) + { + SdtdConsole con = SingletonMonoBehaviour.Instance; + if (_params.Count >= 1 && (_params[0] == "on" || _params[0] == "off")) + { + CinerariumKeepAlive.Enabled = _params[0] == "on"; + } + else if (_params.Count >= 2 && _params[0] == "dim" && int.TryParse(_params[1], out int d) && d >= 0 && d <= 6) + { + CinerariumKeepAlive.ViewDim = d; + } + CinerariumKeepAlive.SyncNow(); + con.Output("necrournkeep: " + (CinerariumKeepAlive.Enabled ? "on" : "off") + ", dim " + CinerariumKeepAlive.ViewDim + + ", observers " + CinerariumKeepAlive.Count); + } + } +} diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 3a37014..74a128e 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index 588eea9..1aa1422 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ