diff --git a/HarmonySrc/CinerariumLoad.cs b/HarmonySrc/CinerariumLoad.cs index acdccc4..88939dd 100644 --- a/HarmonySrc/CinerariumLoad.cs +++ b/HarmonySrc/CinerariumLoad.cs @@ -8,7 +8,7 @@ namespace NecromancerTome /// /// Меры нагрузки урн (решение 17.10: «общий бюджет шагов работы духов за кадр (очередь по кругу - нагрузка постоянна при /// любом числе урн); полная анимация только ближайших ~15-20 духов; замер 100 урн»). - /// Бюджет: дел (пробный шаг + настоящий) за кадр - не больше StepsPerFrame на все урны сейва; урна, которой не досталось, + /// Бюджет: дел (пробный шаг + настоящий) в секунду - не больше StepsPerSecond на все урны сейва (23.10, было за кадр); урна, которой не досталось, /// пробует в следующем кадре. Урна делает дело раз в CinerariumWork.StepSeconds, поэтому очередь сама идёт по кругу. /// Анимация: двигаются (полёт, жесты, покачивание) только AnimateNearest ближайших к игроку духов, остальные замирают дома, /// как уснувшие (CinerariumGenieMotion); работа у всех идёт. @@ -17,11 +17,24 @@ namespace NecromancerTome /// public static class CinerariumLoad { - public static int StepsPerFrame = 3; + /// Дел в секунду на все урны (23.10, было «3 за кадр»). Замер: урна получает тик раз в ~1.4 с, и тики всех урн + /// приходят пачкой в одном кадре, в порядке участков от ближних к игроку. Лимит «3 за кадр» доставался трём ближним урнам, + /// дальние не получали шага никогда («стою в 19 м - не работают, подхожу - работают»). Теперь бюджет копится между + /// тиками (не больше StepsPerSecond за раз), и пачка обслуживает всех, у кого подошёл срок. Консоль: necrourn budget N. + public static float StepsPerSecond = 30f; + /// Запас бюджета, секунд (пачка тиков раз в ~1.4 с должна уместиться целиком). + public const float BurstSeconds = 2f; + static float _tokens; + static float _lastRefill; + /// Очередь (23.10, пользователь: «нужно иметь очередь урн»): урна, которой отказали, ждёт; пока очередь не пуста, + /// бюджет сначала ей - новая урна получит шаг, только если после всех ждущих ещё останется. Иначе ближние урны (их участки + /// идут первыми) снова забирали бы всё. Запись старше QueueForget секунд выбрасывается (урну сняли или участок выгружен). + static readonly Dictionary _queue = new Dictionary(); + const float QueueForget = 10f; + static readonly List _stale = new List(); public static int AnimateNearest = 16; static int _frame = -1; - static int _used; static bool _tide; static void NewFrame() @@ -29,7 +42,23 @@ namespace NecromancerTome if (Time.frameCount != _frame) { _frame = Time.frameCount; - _used = 0; + // По реальному времени с прошлого пополнения: NewFrame зовут только кадры с тиками урн (раз в ~1.4 с), и + // Time.deltaTime (один кадр) давал полшага на пачку (замер 23.10: 279 шагов за 600 с при лимите 30/с). + float now = Time.time; + _tokens = Mathf.Min(StepsPerSecond * BurstSeconds, _tokens + StepsPerSecond * Mathf.Max(0f, now - _lastRefill)); + _lastRefill = now; + _stale.Clear(); + foreach (KeyValuePair kv in _queue) + { + if (now - kv.Value > QueueForget) + { + _stale.Add(kv.Key); + } + } + foreach (Vector3i p in _stale) + { + _queue.Remove(p); + } _tide = SnakeMode.IsBloodTide(); } } @@ -38,12 +67,19 @@ namespace NecromancerTome public static bool TryTakeStep(Vector3i _who) { NewFrame(); - if (_used >= StepsPerFrame) + bool waiting = _queue.ContainsKey(_who); + // Ждущая урна - если есть хоть один шаг; новая - только если хватит и всем ждущим. + if (_tokens < 1f || (!waiting && _tokens < 1f + _queue.Count)) { + if (!waiting) + { + _queue[_who] = Time.time; + } Count(_denied, _who); return false; } - _used++; + _tokens -= 1f; + _queue.Remove(_who); Count(_granted, _who); return true; } @@ -65,7 +101,7 @@ namespace NecromancerTome { float sec = Time.time - _countSince; int frames = Time.frameCount - _frames0; - string s = "за " + sec.ToString("0") + " с, кадров " + frames + ", лимит " + StepsPerFrame + "/кадр; дано:"; + string s = "за " + sec.ToString("0") + " с, кадров " + frames + ", лимит " + StepsPerSecond + "/с, в очереди " + _queue.Count + "; дано:"; foreach (KeyValuePair kv in _granted) { s += " " + kv.Key + "=" + kv.Value; diff --git a/HarmonySrc/NecroDevCommands.cs b/HarmonySrc/NecroDevCommands.cs index 12464ca..fef6415 100644 --- a/HarmonySrc/NecroDevCommands.cs +++ b/HarmonySrc/NecroDevCommands.cs @@ -461,10 +461,10 @@ namespace NecromancerTome { if (_p.Count > 0 && _p[0] == "budget") { - // necrourn budget [N] - кому из урн дали шаг и кому отказали с прошлого вызова; N - лимит дел на кадр (по умолчанию 3) + // necrourn budget [N] - кому из урн дали шаг и кому отказали с прошлого вызова; N - дел в секунду на все урны (30) if (_p.Count > 1) { - CinerariumLoad.StepsPerFrame = int.Parse(_p[1]); + CinerariumLoad.StepsPerSecond = float.Parse(_p[1], System.Globalization.CultureInfo.InvariantCulture); } Out("necrourn budget: " + CinerariumLoad.Budget()); return; diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 19f4032..3da598c 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index 6b8a1d3..11730ac 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ