Урны работают, пока игрок далеко: наблюдатель участков на каждую группу урн (#78)
CinerariumKeepAlive: группа - клетка 64x64 по реестру урн сейва, наблюдатель без видимой геометрии держит 7x7 участков. Консоль: necrournkeep on|off|dim N. Проверено в test8: игрок в 500 блоках, старатель сделал ходку; 3 наблюдателя, +61 участок, RSS +110 МБ, куча ровная 2.35-2.5 ГБ, FPS 52-56, исключений 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c1a5e76777
commit
5565bba13f
@@ -0,0 +1,147 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NecromancerTome
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Урны работают, пока игрок далеко (решение 23.10.2026, #78: «Харвестры работают только когда рядом» - «если игрок далеко
|
||||||
|
/// можно выключить анимацию духов... и пусть духи продолжат работать. С остальными тоже. Если ящики переполнятся, то
|
||||||
|
/// харвестры встанут»; досчёт по времени не годится - сборщик следит за ростом и сеет). Игра обновляет участки, за
|
||||||
|
/// которыми кто-то наблюдает (ChunkManager: активный набор = все участки наблюдателей), - поэтому у каждой группы урн
|
||||||
|
/// свой наблюдатель без видимой геометрии: участки не выгружаются, урны и грядки идут. Анимация вдали и так выключена
|
||||||
|
/// (CinerariumLoad.AnimateNearest). Группа - клетка CellSize×CellSize блоков по реестру урн сейва (CinerariumRegistry),
|
||||||
|
/// наблюдатель в её центре держит (ViewDim+2) участков во все стороны - клетку и рабочие зоны у её краёв.
|
||||||
|
/// Нагрузку мерить по логу (Heap/RSS, Chunks); если тяжело - сначала оптимизация, запасной путь - урны стоят без игрока.
|
||||||
|
/// Консоль: necrournkeep on|off|dim N.
|
||||||
|
/// </summary>
|
||||||
|
public static class CinerariumKeepAlive
|
||||||
|
{
|
||||||
|
public static bool Enabled = true;
|
||||||
|
public const int CellSize = 64;
|
||||||
|
/// <summary>Радиус наблюдателя в участках минус 2 (так считает ChunkManager): 1 - 7×7 участков.</summary>
|
||||||
|
public static int ViewDim = 1;
|
||||||
|
const float SyncSeconds = 10f;
|
||||||
|
|
||||||
|
static readonly Dictionary<Vector2i, ChunkManager.ChunkObserver> _observers = new Dictionary<Vector2i, ChunkManager.ChunkObserver>();
|
||||||
|
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<Vector2i, (int sum, int n)> cells = new Dictionary<Vector2i, (int, int)>();
|
||||||
|
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<Vector2i> gone = null;
|
||||||
|
foreach (KeyValuePair<Vector2i, ChunkManager.ChunkObserver> kv in _observers)
|
||||||
|
{
|
||||||
|
if (!cells.ContainsKey(kv.Key) || kv.Value.viewDim != ViewDim)
|
||||||
|
{
|
||||||
|
(gone ??= new List<Vector2i>()).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<Vector2i, (int sum, int n)> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Сразу пересобрать (команда).</summary>
|
||||||
|
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 <n> - observer size: (n+2) chunks each way around a 64-block group (default 1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||||||
|
{
|
||||||
|
SdtdConsole con = SingletonMonoBehaviour<SdtdConsole>.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user