From 69e2c926e8f1a8822f45843b11f94c560e04ebb3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 10 Mar 2024 04:56:29 +0800 Subject: [PATCH] update runtime monitors --- src/Connectors/EcsWorldProvider.cs | 5 + .../Editor/EcsWorldProviderBaseEditor.cs | 3 + src/Debug/DebugModule.cs | 20 +++- src/Debug/Monitors/Editor.meta | 8 ++ .../Monitors/Editor/EntityMonitorEditor.cs | 20 ++++ .../Editor/EntityMonitorEditor.cs.meta | 11 ++ .../Monitors/Editor/WorldMonitorEditor.cs | 18 +++ .../Editor/WorldMonitorEditor.cs.meta | 11 ++ src/Debug/Monitors/EntityMonitor.cs | 26 ++--- src/Debug/Monitors/PipelineMonitor.cs | 41 ++++++- src/Debug/Monitors/WorldMonitor.cs | 107 +++++++++++++++++- src/Internal/Editor/EcsGUI.cs | 29 +++++ src/Internal/Editor/UnityEditorUtility.cs | 37 ++++++ 13 files changed, 304 insertions(+), 32 deletions(-) create mode 100644 src/Debug/Monitors/Editor.meta create mode 100644 src/Debug/Monitors/Editor/EntityMonitorEditor.cs create mode 100644 src/Debug/Monitors/Editor/EntityMonitorEditor.cs.meta create mode 100644 src/Debug/Monitors/Editor/WorldMonitorEditor.cs create mode 100644 src/Debug/Monitors/Editor/WorldMonitorEditor.cs.meta diff --git a/src/Connectors/EcsWorldProvider.cs b/src/Connectors/EcsWorldProvider.cs index 1f044fa..21145d2 100644 --- a/src/Connectors/EcsWorldProvider.cs +++ b/src/Connectors/EcsWorldProvider.cs @@ -12,6 +12,7 @@ namespace DCFApixels.DragonECS public abstract bool IsEmpty { get; } public abstract void SetRaw(EcsWorld world); public abstract EcsWorld GetRaw(); + public abstract EcsWorld GetCurrentWorldRaw(); } [Serializable] public abstract class EcsWorldProvider : EcsWorldProviderBase where TWorld : EcsWorld @@ -78,6 +79,10 @@ namespace DCFApixels.DragonECS { return Get(); } + public sealed override EcsWorld GetCurrentWorldRaw() + { + return _world; + } public TWorld Get() { if (_world == null || _world.IsDestroyed) diff --git a/src/Connectors/Editor/EcsWorldProviderBaseEditor.cs b/src/Connectors/Editor/EcsWorldProviderBaseEditor.cs index 4e53ce5..93cee9f 100644 --- a/src/Connectors/Editor/EcsWorldProviderBaseEditor.cs +++ b/src/Connectors/Editor/EcsWorldProviderBaseEditor.cs @@ -23,6 +23,9 @@ namespace DCFApixels.DragonECS.Unity.Editors EcsWorld world = Target.GetRaw(); GUILayout.Box($"{world.GetMeta().Name} ( {world.id} )", style, GUILayout.ExpandWidth(true)); } + EcsGUI.Layout.DrawWorldBaseInfo(Target.GetCurrentWorldRaw()); + + base.OnInspectorGUI(); GUILayout.Space(10); diff --git a/src/Debug/DebugModule.cs b/src/Debug/DebugModule.cs index 9be3304..c0bdec7 100644 --- a/src/Debug/DebugModule.cs +++ b/src/Debug/DebugModule.cs @@ -1,23 +1,33 @@ -namespace DCFApixels.DragonECS +using DCFApixels.DragonECS.Unity.Internal; + +namespace DCFApixels.DragonECS { public sealed class DebugModule : IEcsModule { public const string DEBUG_LAYER = nameof(DEBUG_LAYER); public EcsWorld[] _worlds; - public DebugModule(params EcsWorld[] worlds) { _worlds = worlds; } - void IEcsModule.Import(EcsPipeline.Builder b) { + UnityDebugService.Activate(); b.Layers.Insert(EcsConsts.POST_END_LAYER, DEBUG_LAYER); - //b.Add(new PipelineDebugSystem(), DEBUG_LAYER); + b.Add(new PipelineMonitorSystem(), DEBUG_LAYER); foreach (var world in _worlds) { - //b.Add(new WorldDebugSystem(world), DEBUG_LAYER); + b.Add(new WorldMonitorSystem(world), DEBUG_LAYER); } } } + + public static class DebugModuleExt + { + public static EcsPipeline.Builder AddUnityDebug(this EcsPipeline.Builder self, params EcsWorld[] worlds) + { + self.AddModule(new DebugModule(worlds)); + return self; + } + } } diff --git a/src/Debug/Monitors/Editor.meta b/src/Debug/Monitors/Editor.meta new file mode 100644 index 0000000..bcd0c58 --- /dev/null +++ b/src/Debug/Monitors/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1105324b07e39d449acceb24bdd7eb3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/Monitors/Editor/EntityMonitorEditor.cs b/src/Debug/Monitors/Editor/EntityMonitorEditor.cs new file mode 100644 index 0000000..9bbbeba --- /dev/null +++ b/src/Debug/Monitors/Editor/EntityMonitorEditor.cs @@ -0,0 +1,20 @@ +#if UNITY_EDITOR +using DCFApixels.DragonECS.Unity.Internal; +using UnityEditor; + +namespace DCFApixels.DragonECS.Unity.Editors +{ + [CustomEditor(typeof(EntityMonitor))] + internal class EntityMonitorEditor : Editor + { + private EntityMonitor Target => (EntityMonitor)target; + + public override void OnInspectorGUI() + { + bool isAlive = Target.Entity.TryUnpack(out int id, out short gen, out EcsWorld world); + EcsGUI.Layout.EntityBar(isAlive ? EcsGUI.EntityStatus.Alive : EcsGUI.EntityStatus.NotAlive, id, gen, world.id); + EcsGUI.Layout.DrawRuntimeComponents(Target.Entity, false); + } + } +} +#endif \ No newline at end of file diff --git a/src/Debug/Monitors/Editor/EntityMonitorEditor.cs.meta b/src/Debug/Monitors/Editor/EntityMonitorEditor.cs.meta new file mode 100644 index 0000000..89a9d88 --- /dev/null +++ b/src/Debug/Monitors/Editor/EntityMonitorEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5457dbe0f53566419d37e0ee9d9c13f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/Monitors/Editor/WorldMonitorEditor.cs b/src/Debug/Monitors/Editor/WorldMonitorEditor.cs new file mode 100644 index 0000000..7470fec --- /dev/null +++ b/src/Debug/Monitors/Editor/WorldMonitorEditor.cs @@ -0,0 +1,18 @@ +#if UNITY_EDITOR +using DCFApixels.DragonECS.Unity.Internal; +using UnityEditor; + +namespace DCFApixels.DragonECS.Unity.Editors +{ + [CustomEditor(typeof(WorldMonitor))] + internal class WorldMonitorEditor : Editor + { + private WorldMonitor Target => (WorldMonitor)target; + + public override void OnInspectorGUI() + { + EcsGUI.Layout.DrawWorldBaseInfo(Target.World); + } + } +} +#endif \ No newline at end of file diff --git a/src/Debug/Monitors/Editor/WorldMonitorEditor.cs.meta b/src/Debug/Monitors/Editor/WorldMonitorEditor.cs.meta new file mode 100644 index 0000000..84872b2 --- /dev/null +++ b/src/Debug/Monitors/Editor/WorldMonitorEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7aadf8c836c97694186e4b3aeb01fcce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/Monitors/EntityMonitor.cs b/src/Debug/Monitors/EntityMonitor.cs index 905a720..40ac57a 100644 --- a/src/Debug/Monitors/EntityMonitor.cs +++ b/src/Debug/Monitors/EntityMonitor.cs @@ -1,31 +1,19 @@ using UnityEngine; -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS.Unity.Internal { - [MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)] - public class EntityMonitor : MonoBehaviour, IEcsProcess + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] + public class EntityMonitor : MonoBehaviour { private entlong _entity; - private int _entityID; - private short _gen; - private EcsWorld _world; - - public EcsWorld World + public entlong Entity { - get { return _world; } + get { return _entity; } } - public int EntityID - { - get { return _entityID; } - } - - public EntityMonitor(entlong entity) + public void Set(entlong entity) { _entity = entity; - if (_entity.TryUnpack(out _entityID, out _gen, out _world)) - { - - } } } } diff --git a/src/Debug/Monitors/PipelineMonitor.cs b/src/Debug/Monitors/PipelineMonitor.cs index 66e102d..c2f1c5d 100644 --- a/src/Debug/Monitors/PipelineMonitor.cs +++ b/src/Debug/Monitors/PipelineMonitor.cs @@ -1,9 +1,42 @@ -using UnityEngine; +using DCFApixels.DragonECS.Unity.Editors; +using UnityEngine; -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS.Unity.Internal { - [MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)] - public class PipelineMonitor : MonoBehaviour, IEcsProcess + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] + public class PipelineMonitor : MonoBehaviour { + private EcsPipeline _pipeline; + public EcsPipeline Pipeline + { + get { return _pipeline; } + } + public void Set(EcsPipeline pipeline) + { + _pipeline = pipeline; + } + } + + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] + public class PipelineMonitorSystem : IEcsInit, IEcsPipelineMember, IEcsDestroy + { + private PipelineMonitor _monitor; + public EcsPipeline Pipeline { get; set; } + + public void Init() + { + TypeMeta meta = typeof(EcsPipeline).ToMeta(); + _monitor = new GameObject($"{UnityEditorUtility.TransformToUpperName(meta.Name)}").AddComponent(); + UnityEngine.Object.DontDestroyOnLoad(_monitor); + _monitor.Set(Pipeline); + _monitor.gameObject.SetActive(false); + } + + public void Destroy() + { + UnityEngine.Object.Destroy(_monitor); + } } } diff --git a/src/Debug/Monitors/WorldMonitor.cs b/src/Debug/Monitors/WorldMonitor.cs index 1db0c00..5236296 100644 --- a/src/Debug/Monitors/WorldMonitor.cs +++ b/src/Debug/Monitors/WorldMonitor.cs @@ -1,9 +1,108 @@ -using UnityEngine; +using DCFApixels.DragonECS.Unity.Editors; +using System; +using UnityEngine; -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS.Unity.Internal { - [MetaTags(MetaTags.HIDDEN), MetaColor(MetaColor.Gray)] - public class WorldMonitor : MonoBehaviour, IEcsProcess + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] + public class WorldMonitor : MonoBehaviour { + private EcsWorld _world; + public EcsWorld World + { + get { return _world; } + } + public void Set(EcsWorld world) + { + _world = world; + } + } + + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] + public class WorldMonitorSystem : IEcsInit, IEcsWorldEventListener, IEcsEntityEventListener + { + private EcsWorld _world; + private WorldMonitor _monitor; + private Transform _entityMonitorsPoolRoot; + private EntityMonitor[] _entityMonitors; + public EcsWorld World + { + get { return _world; } + } + public WorldMonitorSystem(EcsWorld world) + { + _world = world; + _entityMonitors = new EntityMonitor[_world.Capacity]; + + _world.AddListener(entityEventListener: this); + _world.AddListener(worldEventListener: this); + } + public void Init() + { + TypeMeta meta = _world.GetMeta(); + _monitor = new GameObject($"{UnityEditorUtility.TransformToUpperName(meta.Name)} ( {_world.id} )").AddComponent(); + UnityEngine.Object.DontDestroyOnLoad(_monitor); + _monitor.Set(_world); + _monitor.gameObject.SetActive(false); + + _entityMonitorsPoolRoot = new GameObject("__POOL").transform; + _entityMonitorsPoolRoot.SetParent(_monitor.transform); + + foreach (var e in _world.Entities) + { + InitNewEntity(e, false); + } + } + + void IEcsWorldEventListener.OnWorldResize(int newSize) + { + Array.Resize(ref _entityMonitors, newSize); + } + void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } + void IEcsWorldEventListener.OnWorldDestroy() + { + UnityEngine.Object.Destroy(_monitor); + UnityEngine.Object.Destroy(_entityMonitorsPoolRoot); + _monitor = null; + _entityMonitorsPoolRoot = null; + } + + void IEcsEntityEventListener.OnNewEntity(int entityID) + { + InitNewEntity(entityID, true); + } + + private void InitNewEntity(int entityID, bool check) + { + if(_monitor == null) { return; } + ref var _entityMonitorRef = ref _entityMonitors[entityID]; + if (_entityMonitorRef == null) + { + _entityMonitorRef = new GameObject($"ENTITY ( {entityID} )").AddComponent(); + } + if (check && _entityMonitorRef.Entity.IsAlive) + { + throw new Exception(); + } + _entityMonitorRef.Set(_world.GetEntityLong(entityID)); + _entityMonitorRef.transform.SetParent(_monitor.transform); + } + + void IEcsEntityEventListener.OnDelEntity(int entityID) + { + if(_monitor == null) { return; } + ref var _entityMonitorRef = ref _entityMonitors[entityID]; + if (_entityMonitorRef != null) + { + if (_entityMonitorRef.Entity.IsAlive) + { + throw new Exception(); + } + _entityMonitorRef.transform.SetParent(_entityMonitorsPoolRoot.transform); + _entityMonitorRef.Set(_world.GetEntityLong(entityID)); + } + } } } diff --git a/src/Internal/Editor/EcsGUI.cs b/src/Internal/Editor/EcsGUI.cs index a6a4a0c..9c1b9d1 100644 --- a/src/Internal/Editor/EcsGUI.cs +++ b/src/Internal/Editor/EcsGUI.cs @@ -1,4 +1,5 @@ #if UNITY_EDITOR +using Codice.Client.Common.GameUI; using DCFApixels.DragonECS.Unity.Internal; using System; using System.Reflection; @@ -23,6 +24,20 @@ namespace DCFApixels.DragonECS.Unity.Editors } } + public struct ContentColorScope : IDisposable + { + private readonly Color _oldColor; + public ContentColorScope(Color color) + { + _oldColor = GUI.contentColor; + GUI.contentColor = color; + } + public void Dispose() + { + GUI.contentColor = _oldColor; + } + } + internal readonly static Color GrayColor = new Color32(100, 100, 100, 255); internal readonly static Color GreenColor = new Color32(75, 255, 0, 255); internal readonly static Color RedColor = new Color32(255, 0, 75, 255); @@ -274,6 +289,20 @@ namespace DCFApixels.DragonECS.Unity.Editors public static class Layout { + public static void DrawWorldBaseInfo(EcsWorld world) + { + bool isNull = world == null || world.id == 0; + int entitesCount = isNull ? 0 : world.Count; + int capacity = isNull ? 0 : world.Capacity; + int leakedEntitesCount = isNull ? 0 : world.CountLeakedEntitesDebug(); + EditorGUILayout.IntField("Entities", entitesCount, EditorStyles.boldLabel); + EditorGUILayout.IntField("Capacity", capacity, EditorStyles.boldLabel); + Color color = leakedEntitesCount > 0 ? Color.yellow : GUI.contentColor; + using (new ContentColorScope(color)) + { + EditorGUILayout.IntField("Leaked Entites", leakedEntitesCount, EditorStyles.boldLabel); + } + } public static void EntityBar(EntityStatus status, int id, short gen, short world) { float width = EditorGUIUtility.currentViewWidth; diff --git a/src/Internal/Editor/UnityEditorUtility.cs b/src/Internal/Editor/UnityEditorUtility.cs index 2be801c..9a9f2f9 100644 --- a/src/Internal/Editor/UnityEditorUtility.cs +++ b/src/Internal/Editor/UnityEditorUtility.cs @@ -20,6 +20,43 @@ namespace DCFApixels.DragonECS.Unity.Editors private static GUIContent _singletonContent = null; #region TransformFieldName + public static string TransformToUpperName(string name) + { + if (name.Length <= 0) + { + return name; + } + StringBuilder b = new StringBuilder(); + bool nextWorld = true; + bool prewIsUpper = false; + + + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + if (char.IsLetter(c) == false) + { + nextWorld = true; + prewIsUpper = false; + continue; + } + + bool isUpper = char.IsUpper(c); + if (isUpper) + { + if (nextWorld == false && prewIsUpper == false) + { + b.Append('_'); + } + } + b.Append(char.ToUpper(c)); + nextWorld = false; + prewIsUpper = isUpper; + } + + return b.ToString(); + } + public static string TransformFieldName(string name) { if (name.Length <= 0)