170 lines
5.8 KiB
C#
170 lines
5.8 KiB
C#
using AlicizaX.Editor;
|
|
using AlicizaX.Timer.Runtime;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AlicizaX.Timer.Editor
|
|
{
|
|
[CustomEditor(typeof(TimerComponent))]
|
|
internal sealed class TimerComponentInspector : GameFrameworkInspector
|
|
{
|
|
private const double UPDATE_INTERVAL = 0.001d;
|
|
private const int MAX_DISPLAY_COUNT = 20;
|
|
|
|
private TimerDebugInfo[] _timerBuffer;
|
|
#if UNITY_EDITOR
|
|
private TimerDebugInfo[] _leakBuffer;
|
|
#endif
|
|
private double _lastUpdateTime;
|
|
private int _cachedActiveCount;
|
|
private int _cachedPoolCapacity;
|
|
private int _cachedPeakActiveCount;
|
|
private int _cachedFreeCount;
|
|
private string _cachedUsageText;
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
base.OnInspectorGUI();
|
|
|
|
serializedObject.Update();
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
DrawRuntimeDebugInfo();
|
|
|
|
if (EditorApplication.isPlaying)
|
|
{
|
|
double currentTime = EditorApplication.timeSinceStartup;
|
|
if (currentTime - _lastUpdateTime >= UPDATE_INTERVAL)
|
|
{
|
|
_lastUpdateTime = currentTime;
|
|
Repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawRuntimeDebugInfo()
|
|
{
|
|
if (!EditorApplication.isPlaying)
|
|
{
|
|
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (!AppServices.TryGet<ITimerService>(out ITimerService timerService))
|
|
{
|
|
EditorGUILayout.HelpBox("Timer service is not initialized.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (timerService is not ITimerServiceDebugView debugView)
|
|
{
|
|
return;
|
|
}
|
|
|
|
debugView.GetStatistics(out _cachedActiveCount, out _cachedPoolCapacity, out _cachedPeakActiveCount, out _cachedFreeCount);
|
|
_cachedUsageText = _cachedPoolCapacity > 0
|
|
? Utility.Text.Format("{0:F1}%", (float)_cachedActiveCount / _cachedPoolCapacity * 100f)
|
|
: "0.0%";
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Runtime Debug", EditorStyles.boldLabel);
|
|
EditorGUILayout.LabelField("Active Timers", _cachedActiveCount.ToString());
|
|
EditorGUILayout.LabelField("Pool Capacity", _cachedPoolCapacity.ToString());
|
|
EditorGUILayout.LabelField("Peak Active Count", _cachedPeakActiveCount.ToString());
|
|
EditorGUILayout.LabelField("Free Slots", _cachedFreeCount.ToString());
|
|
EditorGUILayout.LabelField("Pool Usage", _cachedUsageText);
|
|
|
|
DrawTimerList(debugView, _cachedActiveCount);
|
|
#if UNITY_EDITOR
|
|
DrawLeakDetection(debugView, _cachedActiveCount);
|
|
#endif
|
|
}
|
|
|
|
private void DrawTimerList(ITimerServiceDebugView debugView, int activeCount)
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Active Timers", EditorStyles.boldLabel);
|
|
|
|
if (activeCount <= 0)
|
|
{
|
|
EditorGUILayout.LabelField("No active timers.");
|
|
return;
|
|
}
|
|
|
|
EnsureTimerBuffer(activeCount);
|
|
int timerCount = debugView.GetAllTimers(_timerBuffer);
|
|
int displayCount = Mathf.Min(timerCount, MAX_DISPLAY_COUNT);
|
|
|
|
if (displayCount < timerCount)
|
|
{
|
|
EditorGUILayout.HelpBox(Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, timerCount), MessageType.Info);
|
|
}
|
|
|
|
for (int i = 0; i < displayCount; i++)
|
|
{
|
|
TimerDebugInfo timer = _timerBuffer[i];
|
|
string label = Utility.Text.Format(
|
|
"ID {0} | {1} | {2} | {3}",
|
|
timer.TimerId,
|
|
timer.IsLoop ? "Loop" : "Once",
|
|
timer.IsUnscaled ? "Unscaled" : "Scaled",
|
|
timer.IsRunning ? "Running" : "Paused");
|
|
|
|
string value = Utility.Text.Format(
|
|
"Left {0:F2}s | Duration {1:F2}s",
|
|
timer.LeftTime,
|
|
timer.Duration);
|
|
|
|
EditorGUILayout.LabelField(label, value);
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void DrawLeakDetection(ITimerServiceDebugView debugView, int activeCount)
|
|
{
|
|
if (activeCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureLeakBuffer(activeCount);
|
|
int staleCount = debugView.GetStaleOneShotTimers(_leakBuffer);
|
|
if (staleCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField(Utility.Text.Format("Stale One-Shot Timers ({0})", staleCount), EditorStyles.boldLabel);
|
|
EditorGUILayout.HelpBox("Non-loop timers older than 5 minutes. This may indicate long-delay tasks or paused timers.", MessageType.Warning);
|
|
|
|
for (int i = 0; i < staleCount; i++)
|
|
{
|
|
TimerDebugInfo staleTimer = _leakBuffer[i];
|
|
EditorGUILayout.LabelField(
|
|
Utility.Text.Format("ID {0}", staleTimer.TimerId),
|
|
Utility.Text.Format("Created {0:F1}s ago", staleTimer.CreationTime));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private void EnsureTimerBuffer(int count)
|
|
{
|
|
if (_timerBuffer == null || _timerBuffer.Length < count)
|
|
{
|
|
_timerBuffer = new TimerDebugInfo[count];
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void EnsureLeakBuffer(int count)
|
|
{
|
|
if (_leakBuffer == null || _leakBuffer.Length < count)
|
|
{
|
|
_leakBuffer = new TimerDebugInfo[count];
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|