From a5c96956d3001f3c3cc18a8c022a0d0f805ab6c2 Mon Sep 17 00:00:00 2001 From: neuecc Date: Wed, 28 Apr 2021 17:59:53 +0900 Subject: [PATCH] editor --- .gitignore | 2 + .../Assets/Plugins/MessagePipe/Editor.meta | 8 + .../Editor/MessagePipe.Editor.asmdef | 17 ++ .../Editor/MessagePipe.Editor.asmdef.meta | 7 + .../MessagePipeDiagnosticsInfoTreeView.cs | 150 ++++++++++++ ...MessagePipeDiagnosticsInfoTreeView.cs.meta | 11 + .../MessagePipeDiagnosticsInfoWindow.cs | 220 ++++++++++++++++++ .../MessagePipeDiagnosticsInfoWindow.cs.meta | 11 + .../MessagePipe/Editor/SplitterGUILayout.cs | 62 +++++ .../Editor/SplitterGUILayout.cs.meta | 11 + .../Runtime/InMemoryDistributedPubSub.cs | 14 +- .../Runtime/MessagePipeDiagnosticsInfo.cs | 49 ++-- .../Runtime/Unity/InternalVisibleTo.cs | 3 + .../Runtime/Unity/InternalVisibleTo.cs.meta | 11 + .../Assets/Scripts/GameLifetimeScope.cs | 5 +- src/MessagePipe/InMemoryDistributedPubSub.cs | 14 +- src/MessagePipe/MessagePipeDiagnosticsInfo.cs | 30 ++- src/MessagePipe/_InternalVisibleTo.cs | 2 +- 18 files changed, 582 insertions(+), 45 deletions(-) create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor.meta create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef.meta create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs.meta create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs.meta create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs.meta create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs create mode 100644 src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs.meta diff --git a/.gitignore b/.gitignore index eba18ba..419f92a 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,5 @@ src/MessagePipe.Unity/Assembly-CSharp-Editor.csproj src/MessagePipe.Unity/Zenject-Editor.csproj src/MessagePipe.Unity/Zenject.csproj + +src/MessagePipe.Unity/MessagePipe.Editor.csproj diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor.meta new file mode 100644 index 0000000..9763acd --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 88252c2ba4ae5154d8da30b27304e8dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef new file mode 100644 index 0000000..2610826 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef @@ -0,0 +1,17 @@ +{ + "name": "MessagePipe.Editor", + "references": [ + "MessagePipe" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef.meta new file mode 100644 index 0000000..ad7f64c --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipe.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f87f2db4f7bec46409857b7e7a6938ff +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs new file mode 100644 index 0000000..1d9573f --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs @@ -0,0 +1,150 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System; +using UnityEditor.IMGUI.Controls; +using System.Text; +using System.Text.RegularExpressions; + +namespace MessagePipe.Editor +{ + public class MessagePipeDiagnosticsInfoTreeViewItem : TreeViewItem + { + static Regex removeHref = new Regex("(.+)", RegexOptions.Compiled); + + public int Count { get; set; } + public string Caller { get; set; } + public string[] StackTraces { get; set; } + + public MessagePipeDiagnosticsInfoTreeViewItem(int id) : base(id) + { + + } + } + + public class MessagePipeDiagnosticsInfoTreeView : TreeView + { + const string sortedColumnIndexStateKey = "MessagePipeDiagnosticsInfoTreeView_sortedColumnIndex"; + + public IReadOnlyList CurrentBindingItems; + int trackId = 0; + + public MessagePipeDiagnosticsInfoTreeView() + : this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(new[] + { + new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Count"), width = 5}, + new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Caller")}, + }))) + { + } + + MessagePipeDiagnosticsInfoTreeView(TreeViewState state, MultiColumnHeader header) + : base(state, header) + { + rowHeight = 20; + showAlternatingRowBackgrounds = true; + showBorder = true; + header.sortingChanged += Header_sortingChanged; + + header.ResizeToFit(); + Reload(); + + header.sortedColumnIndex = SessionState.GetInt(sortedColumnIndexStateKey, 1); + } + + public void ReloadAndSort() + { + var currentSelected = this.state.selectedIDs; + Reload(); + Header_sortingChanged(this.multiColumnHeader); + this.state.selectedIDs = currentSelected; + } + + private void Header_sortingChanged(MultiColumnHeader multiColumnHeader) + { + SessionState.SetInt(sortedColumnIndexStateKey, multiColumnHeader.sortedColumnIndex); + var index = multiColumnHeader.sortedColumnIndex; + var ascending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex); + + var items = rootItem.children.Cast(); + + IOrderedEnumerable orderedEnumerable; + switch (index) + { + case 0: + orderedEnumerable = ascending ? items.OrderBy(item => item.Count) : items.OrderByDescending(item => item.Count); + break; + case 1: + orderedEnumerable = ascending ? items.OrderBy(item => item.Caller) : items.OrderByDescending(item => item.Caller); + break; + default: + throw new ArgumentOutOfRangeException(nameof(index), index, null); + } + + CurrentBindingItems = rootItem.children = orderedEnumerable.Cast().ToList(); + BuildRows(rootItem); + } + + protected override TreeViewItem BuildRoot() + { + var root = new TreeViewItem { depth = -1 }; + + var children = new List(); + + if (MessagePipeDiagnosticsInfoWindow.diagnosticsInfo != null) + { + var grouped = MessagePipeDiagnosticsInfoWindow.diagnosticsInfo.GroupedByCaller; + foreach (var item in grouped) + { + var viewItem = new MessagePipeDiagnosticsInfoTreeViewItem(trackId++) + { + Count = item.Count(), + Caller = item.Key, + StackTraces = item.ToArray() + }; + children.Add(viewItem); + } + } + + CurrentBindingItems = children; + root.children = CurrentBindingItems as List; + return root; + } + + protected override bool CanMultiSelect(TreeViewItem item) + { + return false; + } + + protected override void RowGUI(RowGUIArgs args) + { + var item = args.item as MessagePipeDiagnosticsInfoTreeViewItem; + + for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++) + { + var rect = args.GetCellRect(visibleColumnIndex); + var columnIndex = args.GetColumn(visibleColumnIndex); + + var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label; + labelStyle.alignment = TextAnchor.MiddleLeft; + switch (columnIndex) + { + case 0: + EditorGUI.LabelField(rect, item.Count.ToString(), labelStyle); + break; + case 1: + EditorGUI.LabelField(rect, item.Caller, labelStyle); + break; + default: + throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null); + } + } + } + } + +} + diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs.meta new file mode 100644 index 0000000..9dca120 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoTreeView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5cee7ab0cc85bc4cbb6b0a2f68a4fff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs new file mode 100644 index 0000000..475db77 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs @@ -0,0 +1,220 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System; +using UnityEditor.IMGUI.Controls; + +namespace MessagePipe.Editor +{ + public class MessagePipeDiagnosticsInfoWindow : EditorWindow + { + static int interval; + + static MessagePipeDiagnosticsInfoWindow window; + + internal static MessagePipeDiagnosticsInfo diagnosticsInfo; + + [MenuItem("Window/MessagePipe Diagnostics")] + public static void OpenWindow() + { + if (window != null) + { + window.Close(); + } + + // will called OnEnable(singleton instance will be set). + GetWindow("MessagePipe Diagnostics").Show(); + } + + static readonly GUILayoutOption[] EmptyLayoutOption = new GUILayoutOption[0]; + + MessagePipeDiagnosticsInfoTreeView treeView; + object splitterState; + + void OnEnable() + { + window = this; // set singleton. + splitterState = SplitterGUILayout.CreateSplitterState(new float[] { 75f, 25f }, new int[] { 32, 32 }, null); + treeView = new MessagePipeDiagnosticsInfoTreeView(); + } + + void OnGUI() + { + // Head + RenderHeadPanel(); + + // Splittable + SplitterGUILayout.BeginVerticalSplit(this.splitterState, EmptyLayoutOption); + { + // Column Tabble + RenderTable(); + + // StackTrace details + RenderDetailsPanel(); + } + SplitterGUILayout.EndVerticalSplit(); + } + + #region HeadPanel + + static bool EnableAutoReload { get; set; } + static bool EnableCaptureStackTrace { get; set; } + static readonly GUIContent EnableAutoReloadHeadContent = EditorGUIUtility.TrTextContent("Enable AutoReload", "Reload view automatically.", (Texture)null); + static readonly GUIContent EnableCaptureStackTraceHeadContent = EditorGUIUtility.TrTextContent("Enable CaptureStackTrace", "CaptureStackTrace on Subscribe.", (Texture)null); + static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View.", (Texture)null); + + // [Enable CaptureStackTrace] | [Enable AutoReload] | .... | Reload + void RenderHeadPanel() + { + EditorGUILayout.BeginVertical(EmptyLayoutOption); + EditorGUILayout.BeginHorizontal(EditorStyles.toolbar, EmptyLayoutOption); + + if (diagnosticsInfo == null) + { + if (GlobalMessagePipe.IsInitialized) + { + diagnosticsInfo = GlobalMessagePipe.DiagnosticsInfo; + } + } + + if (GUILayout.Toggle(EnableCaptureStackTrace, EnableCaptureStackTraceHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableCaptureStackTrace) + { + if (CheckInitialized()) + { + diagnosticsInfo.MessagePipeOptions.EnableCaptureStackTrace = EnableCaptureStackTrace = !EnableCaptureStackTrace; + } + } + + if (GUILayout.Toggle(EnableAutoReload, EnableAutoReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableAutoReload) + { + if (CheckInitialized()) + { + EnableAutoReload = !EnableAutoReload; + } + } + + GUILayout.FlexibleSpace(); + + if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption)) + { + if (CheckInitialized()) + { + treeView.ReloadAndSort(); + Repaint(); + } + } + + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + } + + bool CheckInitialized() + { + if (diagnosticsInfo == null) + { + Debug.LogError("MessagePackDiagnosticsInfo is not set. Should call GlobalMessagePipe.SetProvider on startup."); + return false; + } + return true; + } + + #endregion + + #region TableColumn + + Vector2 tableScroll; + GUIStyle tableListStyle; + + void RenderTable() + { + if (tableListStyle == null) + { + tableListStyle = new GUIStyle("CN Box"); + tableListStyle.margin.top = 0; + tableListStyle.padding.left = 3; + } + + EditorGUILayout.BeginVertical(tableListStyle, EmptyLayoutOption); + + this.tableScroll = EditorGUILayout.BeginScrollView(this.tableScroll, new GUILayoutOption[] + { + GUILayout.ExpandWidth(true), + GUILayout.MaxWidth(2000f) + }); + var controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[] + { + GUILayout.ExpandHeight(true), + GUILayout.ExpandWidth(true) + }); + + + treeView?.OnGUI(controlRect); + + EditorGUILayout.EndScrollView(); + EditorGUILayout.EndVertical(); + } + + private void Update() + { + if (diagnosticsInfo != null && EnableAutoReload) + { + if (interval++ % 120 == 0) + { + if (diagnosticsInfo.CheckAndResetDirty()) + { + treeView.ReloadAndSort(); + Repaint(); + } + } + } + } + + #endregion + + #region Details + + static GUIStyle detailsStyle; + Vector2 detailsScroll; + + void RenderDetailsPanel() + { + if (detailsStyle == null) + { + detailsStyle = new GUIStyle("CN Message"); + detailsStyle.wordWrap = false; + detailsStyle.stretchHeight = true; + detailsStyle.margin.right = 15; + } + + string message = ""; + var selected = treeView.state.selectedIDs; + if (selected.Count > 0) + { + var first = selected[0]; + var item = treeView.CurrentBindingItems.FirstOrDefault(x => x.id == first) as MessagePipeDiagnosticsInfoTreeViewItem; + if (item != null) + { + message = string.Join(Environment.NewLine + "---" + Environment.NewLine, item.StackTraces); + } + } + + detailsScroll = EditorGUILayout.BeginScrollView(this.detailsScroll, EmptyLayoutOption); + var vector = detailsStyle.CalcSize(new GUIContent(message)); + EditorGUILayout.SelectableLabel(message, detailsStyle, new GUILayoutOption[] + { + GUILayout.ExpandHeight(true), + GUILayout.ExpandWidth(true), + GUILayout.MinWidth(vector.x), + GUILayout.MinHeight(vector.y) + }); + EditorGUILayout.EndScrollView(); + } + + #endregion + } +} + diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs.meta new file mode 100644 index 0000000..bd3562c --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/MessagePipeDiagnosticsInfoWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42968f1c1e4200549b214c533eca73ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs new file mode 100644 index 0000000..3282951 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs @@ -0,0 +1,62 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using System; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace MessagePipe.Editor +{ + // reflection call of UnityEditor.SplitterGUILayout + internal static class SplitterGUILayout + { + static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + + static Lazy splitterStateType = new Lazy(() => + { + var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState"); + return type; + }); + + static Lazy splitterStateCtor = new Lazy(() => + { + var type = splitterStateType.Value; + return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null); + }); + + static Lazy splitterGUILayoutType = new Lazy(() => + { + var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout"); + return type; + }); + + static Lazy beginVerticalSplit = new Lazy(() => + { + var type = splitterGUILayoutType.Value; + return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { splitterStateType.Value, typeof(GUILayoutOption[]) }, null); + }); + + static Lazy endVerticalSplit = new Lazy(() => + { + var type = splitterGUILayoutType.Value; + return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null); + }); + + public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes) + { + return splitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes }); + } + + public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options) + { + beginVerticalSplit.Value.Invoke(null, new object[] { splitterState, options }); + } + + public static void EndVerticalSplit() + { + endVerticalSplit.Value.Invoke(null, Type.EmptyTypes); + } + } +} + diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs.meta new file mode 100644 index 0000000..ac96ca5 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Editor/SplitterGUILayout.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14cc21ce00a64784eab22336b747c4f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/InMemoryDistributedPubSub.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/InMemoryDistributedPubSub.cs index 27b1175..7e4cdf1 100644 --- a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/InMemoryDistributedPubSub.cs +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/InMemoryDistributedPubSub.cs @@ -35,13 +35,13 @@ public InMemoryDistributedSubscriber(IAsyncSubscriber subscriber public UniTask SubscribeAsync(TKey key, IMessageHandler handler, CancellationToken cancellationToken = default) { - var d = subscriber.Subscribe(key, new AsyncMessageHandlerBrdiger(handler)); + var d = subscriber.Subscribe(key, new AsyncMessageHandlerBridge(handler)); return new UniTask(new AsyncDisposableBridge(d)); } public UniTask SubscribeAsync(TKey key, IMessageHandler handler, MessageHandlerFilter[] filters, CancellationToken cancellationToken = default) { - var d = subscriber.Subscribe(key, new AsyncMessageHandlerBrdiger(handler), filters.Select(x => new AsyncMessageHandlerFilterBrdiger(x)).ToArray()); + var d = subscriber.Subscribe(key, new AsyncMessageHandlerBridge(handler), filters.Select(x => new AsyncMessageHandlerFilterBridge(x)).ToArray()); return new UniTask(new AsyncDisposableBridge(d)); } @@ -75,11 +75,11 @@ public UniTask DisposeAsync() } } - internal sealed class AsyncMessageHandlerBrdiger : IAsyncMessageHandler + internal sealed class AsyncMessageHandlerBridge : IAsyncMessageHandler { readonly IMessageHandler handler; - public AsyncMessageHandlerBrdiger(IMessageHandler handler) + public AsyncMessageHandlerBridge(IMessageHandler handler) { this.handler = handler; } @@ -91,11 +91,11 @@ public UniTask HandleAsync(T message, CancellationToken cancellationToken) } } - internal sealed class AsyncMessageHandlerFilterBrdiger : AsyncMessageHandlerFilter + internal sealed class AsyncMessageHandlerFilterBridge : AsyncMessageHandlerFilter { readonly MessageHandlerFilter filter; - public AsyncMessageHandlerFilterBrdiger(MessageHandlerFilter filter) + public AsyncMessageHandlerFilterBridge(MessageHandlerFilter filter) { this.filter = filter; this.Order = filter.Order; @@ -103,7 +103,7 @@ public AsyncMessageHandlerFilterBrdiger(MessageHandlerFilter filter) public override UniTask HandleAsync(T message, CancellationToken cancellationToken, Func next) { - filter.Handle(message, async x => await next(x, CancellationToken.None)); + filter.Handle(message, async x => await next(x, cancellationToken)); return default; } } diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/MessagePipeDiagnosticsInfo.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/MessagePipeDiagnosticsInfo.cs index b342b8b..e16508b 100644 --- a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/MessagePipeDiagnosticsInfo.cs +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/MessagePipeDiagnosticsInfo.cs @@ -17,7 +17,8 @@ internal interface IHandlerHolderMarker public sealed class MessagePipeDiagnosticsInfo { int subscribeCount; - bool enableCaptureStackTrace; + bool dirty; + MessagePipeOptions options; object gate = new object(); Dictionary> capturedStackTraces = new Dictionary>(); @@ -25,12 +26,21 @@ public sealed class MessagePipeDiagnosticsInfo /// Get current subscribed count. public int SubscribeCount => subscribeCount; + internal bool CheckAndResetDirty() + { + var d = dirty; + dirty = false; + return d; + } + + internal MessagePipeOptions MessagePipeOptions => options; + /// /// When MessagePipeOptions.EnableCaptureStackTrace is enabled, list all stacktrace on subscribe. /// public string[] GetCapturedStackTraces() { - if (!enableCaptureStackTrace) return Array.Empty(); + if (!options.EnableCaptureStackTrace) return Array.Empty(); lock (gate) { return capturedStackTraces.SelectMany(x => x.Value.Values).ToArray(); @@ -44,34 +54,35 @@ public ILookup GroupedByCaller { get { - if (!enableCaptureStackTrace) return Array.Empty().ToLookup(x => x); - return capturedStackTraces - .SelectMany(x => x.Value.Values) - .ToLookup(x => - { - var split = x.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - var skips = split.SkipWhile(y => y.TrimStart().Contains(" MessagePipe.")); - return skips.First().TrimStart().Substring(3); // remove "at ". - }); + if (!options.EnableCaptureStackTrace) return Array.Empty().ToLookup(x => x); + lock (gate) + { + return capturedStackTraces + .SelectMany(x => x.Value.Values) + .ToLookup(x => + { + var split = x.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + var skips = split.SkipWhile(y => y.TrimStart().Contains(" MessagePipe.")); + return skips.First().TrimStart().Substring(3); // remove "at ". + }); + } } } public MessagePipeDiagnosticsInfo(MessagePipeOptions options) { - if (options.EnableCaptureStackTrace) - { - enableCaptureStackTrace = true; - } + this.options = options; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void IncrementSubscribe(IHandlerHolderMarker handlerHolder, IDisposable subscription) { Interlocked.Increment(ref subscribeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { AddStackTrace(handlerHolder, subscription); } + dirty = true; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -93,10 +104,11 @@ void AddStackTrace(IHandlerHolderMarker handlerHolder, IDisposable subscription) internal void DecrementSubscribe(IHandlerHolderMarker handlerHolder, IDisposable subscription) { Interlocked.Decrement(ref subscribeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { RemoveStackTrace(handlerHolder, subscription); } + dirty = true; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -116,13 +128,14 @@ void RemoveStackTrace(IHandlerHolderMarker handlerHolder, IDisposable subscripti internal void RemoveTargetDiagnostics(IHandlerHolderMarker targetHolder, int removeCount) { Interlocked.Add(ref subscribeCount, -removeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { lock (gate) { capturedStackTraces.Remove(targetHolder); } } + dirty = true; } } } \ No newline at end of file diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs new file mode 100644 index 0000000..61487dc --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("MessagePipe.Editor")] \ No newline at end of file diff --git a/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs.meta b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs.meta new file mode 100644 index 0000000..8cb9b66 --- /dev/null +++ b/src/MessagePipe.Unity/Assets/Plugins/MessagePipe/Runtime/Unity/InternalVisibleTo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3d6bb660d4c1f54e8548af97a83ec8e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MessagePipe.Unity/Assets/Scripts/GameLifetimeScope.cs b/src/MessagePipe.Unity/Assets/Scripts/GameLifetimeScope.cs index 6cc66be..74798bb 100644 --- a/src/MessagePipe.Unity/Assets/Scripts/GameLifetimeScope.cs +++ b/src/MessagePipe.Unity/Assets/Scripts/GameLifetimeScope.cs @@ -7,7 +7,7 @@ public class GameLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { - var options = builder.RegisterMessagePipe(); + var options = builder.RegisterMessagePipe(x => { x.EnableCaptureStackTrace = true; }); builder.RegisterMessageBroker(options); builder.RegisterEntryPoint(Lifetime.Singleton); @@ -57,6 +57,7 @@ public MessagePipeDemo(IPublisher publisher, ISubscriber subscriber, I this.subscriber = subscriber; GlobalMessagePipe.SetProvider(resolver.AsServiceProvider()); + } public void Start() @@ -72,7 +73,7 @@ public void Start() publisher.Publish(30); var disposable = d.Build(); - disposable.Dispose(); + // disposable.Dispose(); } } diff --git a/src/MessagePipe/InMemoryDistributedPubSub.cs b/src/MessagePipe/InMemoryDistributedPubSub.cs index ceb4bbd..95c99c3 100644 --- a/src/MessagePipe/InMemoryDistributedPubSub.cs +++ b/src/MessagePipe/InMemoryDistributedPubSub.cs @@ -35,13 +35,13 @@ public InMemoryDistributedSubscriber(IAsyncSubscriber subscriber public ValueTask SubscribeAsync(TKey key, IMessageHandler handler, CancellationToken cancellationToken = default) { - var d = subscriber.Subscribe(key, new AsyncMessageHandlerBrdiger(handler)); + var d = subscriber.Subscribe(key, new AsyncMessageHandlerBridge(handler)); return new ValueTask(new AsyncDisposableBridge(d)); } public ValueTask SubscribeAsync(TKey key, IMessageHandler handler, MessageHandlerFilter[] filters, CancellationToken cancellationToken = default) { - var d = subscriber.Subscribe(key, new AsyncMessageHandlerBrdiger(handler), filters.Select(x => new AsyncMessageHandlerFilterBrdiger(x)).ToArray()); + var d = subscriber.Subscribe(key, new AsyncMessageHandlerBridge(handler), filters.Select(x => new AsyncMessageHandlerFilterBridge(x)).ToArray()); return new ValueTask(new AsyncDisposableBridge(d)); } @@ -75,11 +75,11 @@ public ValueTask DisposeAsync() } } - internal sealed class AsyncMessageHandlerBrdiger : IAsyncMessageHandler + internal sealed class AsyncMessageHandlerBridge : IAsyncMessageHandler { readonly IMessageHandler handler; - public AsyncMessageHandlerBrdiger(IMessageHandler handler) + public AsyncMessageHandlerBridge(IMessageHandler handler) { this.handler = handler; } @@ -91,11 +91,11 @@ public ValueTask HandleAsync(T message, CancellationToken cancellationToken) } } - internal sealed class AsyncMessageHandlerFilterBrdiger : AsyncMessageHandlerFilter + internal sealed class AsyncMessageHandlerFilterBridge : AsyncMessageHandlerFilter { readonly MessageHandlerFilter filter; - public AsyncMessageHandlerFilterBrdiger(MessageHandlerFilter filter) + public AsyncMessageHandlerFilterBridge(MessageHandlerFilter filter) { this.filter = filter; this.Order = filter.Order; @@ -103,7 +103,7 @@ public AsyncMessageHandlerFilterBrdiger(MessageHandlerFilter filter) public override ValueTask HandleAsync(T message, CancellationToken cancellationToken, Func next) { - filter.Handle(message, async x => await next(x, CancellationToken.None)); + filter.Handle(message, async x => await next(x, cancellationToken)); return default; } } diff --git a/src/MessagePipe/MessagePipeDiagnosticsInfo.cs b/src/MessagePipe/MessagePipeDiagnosticsInfo.cs index 6d5d23b..19bf213 100644 --- a/src/MessagePipe/MessagePipeDiagnosticsInfo.cs +++ b/src/MessagePipe/MessagePipeDiagnosticsInfo.cs @@ -17,7 +17,8 @@ internal interface IHandlerHolderMarker public sealed class MessagePipeDiagnosticsInfo { int subscribeCount; - bool enableCaptureStackTrace; + bool dirty; + MessagePipeOptions options; object gate = new object(); Dictionary> capturedStackTraces = new Dictionary>(); @@ -25,12 +26,21 @@ public sealed class MessagePipeDiagnosticsInfo /// Get current subscribed count. public int SubscribeCount => subscribeCount; + internal bool CheckAndResetDirty() + { + var d = dirty; + dirty = false; + return d; + } + + internal MessagePipeOptions MessagePipeOptions => options; + /// /// When MessagePipeOptions.EnableCaptureStackTrace is enabled, list all stacktrace on subscribe. /// public string[] GetCapturedStackTraces() { - if (!enableCaptureStackTrace) return Array.Empty(); + if (!options.EnableCaptureStackTrace) return Array.Empty(); lock (gate) { return capturedStackTraces.SelectMany(x => x.Value.Values).ToArray(); @@ -44,7 +54,7 @@ public ILookup GroupedByCaller { get { - if (!enableCaptureStackTrace) return Array.Empty().ToLookup(x => x); + if (!options.EnableCaptureStackTrace) return Array.Empty().ToLookup(x => x); lock (gate) { return capturedStackTraces @@ -61,20 +71,18 @@ public ILookup GroupedByCaller public MessagePipeDiagnosticsInfo(MessagePipeOptions options) { - if (options.EnableCaptureStackTrace) - { - enableCaptureStackTrace = true; - } + this.options = options; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void IncrementSubscribe(IHandlerHolderMarker handlerHolder, IDisposable subscription) { Interlocked.Increment(ref subscribeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { AddStackTrace(handlerHolder, subscription); } + dirty = true; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -96,10 +104,11 @@ void AddStackTrace(IHandlerHolderMarker handlerHolder, IDisposable subscription) internal void DecrementSubscribe(IHandlerHolderMarker handlerHolder, IDisposable subscription) { Interlocked.Decrement(ref subscribeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { RemoveStackTrace(handlerHolder, subscription); } + dirty = true; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -119,13 +128,14 @@ void RemoveStackTrace(IHandlerHolderMarker handlerHolder, IDisposable subscripti internal void RemoveTargetDiagnostics(IHandlerHolderMarker targetHolder, int removeCount) { Interlocked.Add(ref subscribeCount, -removeCount); - if (enableCaptureStackTrace) + if (options.EnableCaptureStackTrace) { lock (gate) { capturedStackTraces.Remove(targetHolder); } } + dirty = true; } } } \ No newline at end of file diff --git a/src/MessagePipe/_InternalVisibleTo.cs b/src/MessagePipe/_InternalVisibleTo.cs index 35694dc..952e242 100644 --- a/src/MessagePipe/_InternalVisibleTo.cs +++ b/src/MessagePipe/_InternalVisibleTo.cs @@ -1,4 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("MessagePipe.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001000144ec28f1e9ef7b17dacc47425a7a153aea0a7baa590743a2d1a86f4b3e10a8a12712c6e647966bfd8bd6e830048b23bd42bbc56f179585c15b8c19cf86c0eed1b73c993dd7a93a30051dd50fdda0e4d6b65e6874e30f1c37cf8bcbc7fe02c7f2e6a0a3327c0ccc1631bf645f40732521fa0b41a30c178d08f7dd779d42a1ee")] -[assembly: InternalsVisibleTo("MessagePipe.Sandbox.ConsoleApp, PublicKey=00240000048000009400000006020000002400005253413100040000010001000144ec28f1e9ef7b17dacc47425a7a153aea0a7baa590743a2d1a86f4b3e10a8a12712c6e647966bfd8bd6e830048b23bd42bbc56f179585c15b8c19cf86c0eed1b73c993dd7a93a30051dd50fdda0e4d6b65e6874e30f1c37cf8bcbc7fe02c7f2e6a0a3327c0ccc1631bf645f40732521fa0b41a30c178d08f7dd779d42a1ee")] +[assembly: InternalsVisibleTo("MessagePipe.Sandbox.ConsoleApp, PublicKey=00240000048000009400000006020000002400005253413100040000010001000144ec28f1e9ef7b17dacc47425a7a153aea0a7baa590743a2d1a86f4b3e10a8a12712c6e647966bfd8bd6e830048b23bd42bbc56f179585c15b8c19cf86c0eed1b73c993dd7a93a30051dd50fdda0e4d6b65e6874e30f1c37cf8bcbc7fe02c7f2e6a0a3327c0ccc1631bf645f40732521fa0b41a30c178d08f7dd779d42a1ee")] \ No newline at end of file