204 lines
8.4 KiB
C#
204 lines
8.4 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Scripting;
|
||
|
||
namespace NecromancerTome
|
||
{
|
||
/// <summary>
|
||
/// Меры нагрузки урн (решение 17.10: «общий бюджет шагов работы духов за кадр (очередь по кругу - нагрузка постоянна при
|
||
/// любом числе урн); полная анимация только ближайших ~15-20 духов; замер 100 урн»).
|
||
/// Бюджет: дел (пробный шаг + настоящий) за кадр - не больше StepsPerFrame на все урны сейва; урна, которой не досталось,
|
||
/// пробует в следующем кадре. Урна делает дело раз в CinerariumWork.StepSeconds, поэтому очередь сама идёт по кругу.
|
||
/// Анимация: двигаются (полёт, жесты, покачивание) только AnimateNearest ближайших к игроку духов, остальные замирают дома,
|
||
/// как уснувшие (CinerariumGenieMotion); работа у всех идёт.
|
||
/// Прилив (решение 17.10, «Прилив прячет некротехнологов»): ночью кровавой луны духи всех урн уходят в урны и работа стоит -
|
||
/// признак берётся раз за кадр.
|
||
/// </summary>
|
||
public static class CinerariumLoad
|
||
{
|
||
public static int StepsPerFrame = 3;
|
||
public static int AnimateNearest = 16;
|
||
|
||
static int _frame = -1;
|
||
static int _used;
|
||
static bool _tide;
|
||
|
||
static void NewFrame()
|
||
{
|
||
if (Time.frameCount != _frame)
|
||
{
|
||
_frame = Time.frameCount;
|
||
_used = 0;
|
||
_tide = SnakeMode.IsBloodTide();
|
||
}
|
||
}
|
||
|
||
/// <summary>Взять одно дело из бюджета кадра. _who - урна (учёт для necrourn budget).</summary>
|
||
public static bool TryTakeStep(Vector3i _who)
|
||
{
|
||
NewFrame();
|
||
if (_used >= StepsPerFrame)
|
||
{
|
||
Count(_denied, _who);
|
||
return false;
|
||
}
|
||
_used++;
|
||
Count(_granted, _who);
|
||
return true;
|
||
}
|
||
|
||
// Учёт бюджета (23.10: сборщики 600 с не получили ни одного шага, логист рядом - получал): кому дали, кому отказали.
|
||
static readonly Dictionary<Vector3i, int> _granted = new Dictionary<Vector3i, int>();
|
||
static readonly Dictionary<Vector3i, int> _denied = new Dictionary<Vector3i, int>();
|
||
static float _countSince;
|
||
static int _frames0;
|
||
|
||
static void Count(Dictionary<Vector3i, int> _d, Vector3i _who)
|
||
{
|
||
_d.TryGetValue(_who, out int n);
|
||
_d[_who] = n + 1;
|
||
}
|
||
|
||
/// <summary>Сводка с прошлого вызова и сброс (команда necrourn budget).</summary>
|
||
public static string Budget()
|
||
{
|
||
float sec = Time.time - _countSince;
|
||
int frames = Time.frameCount - _frames0;
|
||
string s = "за " + sec.ToString("0") + " с, кадров " + frames + ", лимит " + StepsPerFrame + "/кадр; дано:";
|
||
foreach (KeyValuePair<Vector3i, int> kv in _granted)
|
||
{
|
||
s += " " + kv.Key + "=" + kv.Value;
|
||
}
|
||
s += "; отказано:";
|
||
foreach (KeyValuePair<Vector3i, int> kv in _denied)
|
||
{
|
||
s += " " + kv.Key + "=" + kv.Value;
|
||
}
|
||
_granted.Clear();
|
||
_denied.Clear();
|
||
_countSince = Time.time;
|
||
_frames0 = Time.frameCount;
|
||
return s;
|
||
}
|
||
|
||
/// <summary>Идёт ли кровавый прилив (раз за кадр).</summary>
|
||
public static bool Tide
|
||
{
|
||
get
|
||
{
|
||
NewFrame();
|
||
return _tide;
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------------
|
||
// Ближайшие духи.
|
||
// ------------------------------------------------------------------
|
||
|
||
static float _nextRank;
|
||
static readonly HashSet<CinerariumGenieMotion> _animated = new HashSet<CinerariumGenieMotion>();
|
||
static readonly List<CinerariumGenieMotion> _sorted = new List<CinerariumGenieMotion>();
|
||
|
||
/// <summary>Двигается ли дух: среди AnimateNearest ближайших к игроку (пересчёт раз в 0.5 с, без выделений памяти).</summary>
|
||
public static bool Animated(CinerariumGenieMotion _m)
|
||
{
|
||
if (Time.time >= _nextRank)
|
||
{
|
||
_nextRank = Time.time + 0.5f;
|
||
Rank();
|
||
}
|
||
return _animated.Contains(_m);
|
||
}
|
||
|
||
static void Rank()
|
||
{
|
||
_animated.Clear();
|
||
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
|
||
if (pl == null)
|
||
{
|
||
return;
|
||
}
|
||
Vector3 me = pl.GetPosition() - Origin.position;
|
||
_sorted.Clear();
|
||
_sorted.AddRange(CinerariumGenie.Live);
|
||
if (_sorted.Count > AnimateNearest)
|
||
{
|
||
_sorted.Sort((a, b) => (a.transform.position - me).sqrMagnitude.CompareTo((b.transform.position - me).sqrMagnitude));
|
||
}
|
||
for (int i = 0; i < _sorted.Count && i < AnimateNearest; i++)
|
||
{
|
||
_animated.Add(_sorted[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>necrofps [секунд] - средний и худший кадр за время замера (по умолчанию 5 с), число духов и урн; ответ - строкой
|
||
/// почтового ящика в лог. Для замера нагрузки урн (17.10: «замер 100 урн»).</summary>
|
||
[Preserve]
|
||
public class ConsoleCmdNecroFps : ConsoleCmdAbstract
|
||
{
|
||
public override string[] getCommands() => new[] { "necrofps" };
|
||
public override string getDescription() => "NecromancerTome (разработка): средний и худший кадр за N секунд";
|
||
public override bool AllowedInMainMenu => false;
|
||
|
||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||
{
|
||
float sec = _params.Count > 0 ? float.Parse(_params[0], System.Globalization.CultureInfo.InvariantCulture) : 5f;
|
||
bool uncap = _params.Count > 1 && _params[1] == "uncap";
|
||
SdtdConsole.Instance.Output("necrofps: замер " + sec + " с" + (uncap ? " без ограничения кадров" : ""));
|
||
ThreadManager.StartCoroutine(Measure(sec, uncap));
|
||
}
|
||
|
||
/// <summary>_uncap - на время замера снять вертикальную синхронизацию и предел кадров: при 60 fps по синхронизации
|
||
/// запас не виден (18.10, замер 100 урн).</summary>
|
||
static IEnumerator Measure(float _sec, bool _uncap)
|
||
{
|
||
int vs = QualitySettings.vSyncCount, fr = Application.targetFrameRate;
|
||
if (_uncap)
|
||
{
|
||
QualitySettings.vSyncCount = 0;
|
||
Application.targetFrameRate = -1;
|
||
yield return new WaitForSecondsRealtime(0.5f);
|
||
}
|
||
float end = Time.realtimeSinceStartup + _sec;
|
||
int frames = 0;
|
||
float worst = 0f, sum = 0f;
|
||
long gc0 = System.GC.CollectionCount(0);
|
||
while (Time.realtimeSinceStartup < end)
|
||
{
|
||
yield return null;
|
||
frames++;
|
||
sum += Time.unscaledDeltaTime;
|
||
worst = Mathf.Max(worst, Time.unscaledDeltaTime);
|
||
}
|
||
if (_uncap)
|
||
{
|
||
QualitySettings.vSyncCount = vs;
|
||
Application.targetFrameRate = fr;
|
||
}
|
||
float avg = frames > 0 ? sum / frames * 1000f : 0f;
|
||
Debug.Log("[NecromancerTome] mailbox: necrofps: кадров " + frames + ", средний " + avg.ToString("F1") + " мс (" + (avg > 0 ? 1000f / avg : 0f).ToString("F0") +
|
||
" fps), худший " + (worst * 1000f).ToString("F1") + " мс, сборок мусора " + (System.GC.CollectionCount(0) - gc0) +
|
||
", духов " + CinerariumGenie.Live.Count + ", урн в сейве " + CinerariumRegistry.All().Count);
|
||
}
|
||
}
|
||
|
||
/// <summary>necrotide on|off|auto - прилив для проверок без ожидания ночи кровавой луны (SnakeMode.ForceTide).</summary>
|
||
[Preserve]
|
||
public class ConsoleCmdNecroTide : ConsoleCmdAbstract
|
||
{
|
||
public override string[] getCommands() => new[] { "necrotide" };
|
||
public override string getDescription() => "NecromancerTome (разработка): прилив on|off|auto";
|
||
public override bool AllowedInMainMenu => false;
|
||
|
||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||
{
|
||
string v = _params.Count > 0 ? _params[0] : "";
|
||
SnakeMode.ForceTide = v == "on" ? true : v == "off" ? (bool?)false : null;
|
||
SdtdConsole.Instance.Output("necrotide: " + (SnakeMode.ForceTide.HasValue ? (SnakeMode.ForceTide.Value ? "прилив" : "нет прилива") : "по часам") +
|
||
", сейчас " + (SnakeMode.IsBloodTide() ? "прилив" : "не прилив"));
|
||
}
|
||
}
|
||
}
|