com.wallstop-studios.dxmessaging 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +326 -0
  2. package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +3 -3
  3. package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +3 -3
  4. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +8 -8
  5. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +3 -3
  6. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +171 -13
  7. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +3 -3
  8. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +391 -96
  9. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +3 -3
  10. package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs +279 -0
  11. package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs.meta +11 -0
  12. package/Editor/DxMessagingEditorPalette.cs +66 -0
  13. package/Editor/DxMessagingEditorPalette.cs.meta +11 -0
  14. package/Editor/DxMessagingEditorTheme.cs +200 -0
  15. package/Editor/DxMessagingEditorTheme.cs.meta +11 -0
  16. package/Editor/DxMessagingMenu.cs.meta +3 -3
  17. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +3 -3
  18. package/Editor/Icons/dxmessaging-icon-256.png +0 -0
  19. package/Editor/Icons/dxmessaging-icon-256.png.meta +156 -0
  20. package/Editor/Icons/dxmessaging-icon-32.png +0 -0
  21. package/Editor/Icons/dxmessaging-icon-32.png.meta +156 -0
  22. package/Editor/Icons/dxmessaging-icon-48.png +0 -0
  23. package/Editor/Icons/dxmessaging-icon-48.png.meta +156 -0
  24. package/Editor/Icons.meta +8 -0
  25. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +2 -3
  26. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +3 -3
  27. package/Editor/Settings/DxMessagingSettingsProvider.cs +368 -33
  28. package/Editor/SetupCscRsp.cs +232 -163
  29. package/Editor/Testing/MessagingComponentEditorHarness.cs +11 -4
  30. package/Editor/Theme/DxMessagingTheme.uss +255 -0
  31. package/Editor/Theme/DxMessagingTheme.uss.meta +11 -0
  32. package/Editor/Theme/DxTokens.uss +90 -0
  33. package/Editor/Theme/DxTokens.uss.meta +11 -0
  34. package/Editor/Theme.meta +8 -0
  35. package/Editor/Windows/DxMessagingFlowGraphWindow.cs +5513 -0
  36. package/Editor/Windows/DxMessagingFlowGraphWindow.cs.meta +11 -0
  37. package/Editor/Windows/DxMessagingMessageMonitorWindow.cs +2288 -0
  38. package/Editor/Windows/DxMessagingMessageMonitorWindow.cs.meta +11 -0
  39. package/Editor/Windows.meta +8 -0
  40. package/README.md +84 -35
  41. package/Runtime/Analyzers.meta +8 -0
  42. package/Runtime/AssemblyInfo.cs +1 -0
  43. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +3 -3
  44. package/Runtime/Core/Diagnostics/MessageEmissionData.cs +29 -1
  45. package/Runtime/Core/DxMessagingStaticState.cs.meta +3 -3
  46. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +3 -3
  47. package/Runtime/Core/Extensions/MessageExtensions.cs +4 -5
  48. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +3 -3
  49. package/Runtime/Core/Helper/MessageCache.cs +56 -23
  50. package/Runtime/Core/IMessage.cs.meta +3 -3
  51. package/Runtime/Core/InstanceId.cs.meta +3 -3
  52. package/Runtime/Core/Internal/FlatDispatch.cs +32 -0
  53. package/Runtime/Core/Internal/TypedSlots.cs +45 -7
  54. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +3 -3
  55. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +3 -3
  56. package/Runtime/Core/MessageBus/IMessageBus.cs +63 -28
  57. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +3 -3
  58. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +3 -3
  59. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +3 -3
  60. package/Runtime/Core/MessageBus/MessageBus.cs +1135 -626
  61. package/Runtime/Core/MessageBus/MessageBus.cs.meta +3 -3
  62. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +3 -3
  63. package/Runtime/Core/MessageBus/MessageBusRegistration.cs +207 -0
  64. package/Runtime/Core/MessageBus/MessageBusRegistration.cs.meta +11 -0
  65. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +3 -3
  66. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +3 -3
  67. package/Runtime/Core/MessageBus/RegistrationLog.cs +25 -12
  68. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +3 -3
  69. package/Runtime/Core/MessageHandler.cs +1886 -1082
  70. package/Runtime/Core/MessageHandler.cs.meta +3 -3
  71. package/Runtime/Core/MessageRegistrationHandle.cs +19 -5
  72. package/Runtime/Core/MessageRegistrationHandle.cs.meta +3 -3
  73. package/Runtime/Core/MessageRegistrationToken.cs +1932 -895
  74. package/Runtime/Core/MessageRegistrationToken.cs.meta +3 -3
  75. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +3 -3
  76. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +3 -3
  77. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +3 -3
  78. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +3 -3
  79. package/Runtime/Core/MessagingDebug.cs.meta +3 -3
  80. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +3 -3
  81. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +3 -3
  82. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +3 -3
  83. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +3 -3
  84. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +3 -3
  85. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +3 -3
  86. package/Runtime/Unity/MessageAwareComponent.cs.meta +3 -3
  87. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +3 -3
  88. package/Runtime/Unity/MessagingComponent.cs.meta +3 -3
  89. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +3 -3
  90. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +3 -3
  91. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +3 -3
  92. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +3 -3
  93. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +3 -3
  94. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs +173 -0
  95. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs.meta +11 -0
  96. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity +424 -0
  97. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity.meta +7 -0
  98. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs +185 -0
  99. package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs.meta +11 -0
  100. package/Samples~/Diagnostics Tooling Exerciser/Messages.cs +46 -0
  101. package/Samples~/Diagnostics Tooling Exerciser/Messages.cs.meta +11 -0
  102. package/Samples~/Diagnostics Tooling Exerciser/README.md +53 -0
  103. package/Samples~/Diagnostics Tooling Exerciser/README.md.meta +7 -0
  104. package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef +13 -0
  105. package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef.meta +7 -0
  106. package/Samples~/Diagnostics Tooling Exerciser.meta +8 -0
  107. package/Samples~/Mini Combat/Boot.cs.meta +3 -3
  108. package/Samples~/Mini Combat/Enemy.cs.meta +3 -3
  109. package/Samples~/Mini Combat/Messages.cs.meta +3 -3
  110. package/Samples~/Mini Combat/Player.cs.meta +3 -3
  111. package/Samples~/Mini Combat/README.md +1 -1
  112. package/Samples~/Mini Combat/UIOverlay.cs.meta +3 -3
  113. package/Samples~/Mini Combat/Walkthrough.md +3 -3
  114. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +3 -3
  115. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +3 -3
  116. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +3 -3
  117. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +3 -3
  118. package/SourceGenerators/Directory.Build.props +5 -5
  119. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +3 -3
  120. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +12 -11
  121. package/docs/images/DxMessaging-banner.svg +79 -0
  122. package/docs/images/DxMessaging-banner.svg.meta +53 -0
  123. package/docs/images.meta +8 -0
  124. package/docs.meta +8 -0
  125. package/package.json +17 -5
  126. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  127. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +0 -0
  128. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
  129. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll.meta +0 -0
  130. /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll +0 -0
  131. /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll.meta +0 -0
  132. /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll +0 -0
  133. /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll.meta +0 -0
  134. /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
  135. /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +0 -0
  136. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  137. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +0 -0
  138. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  139. /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,332 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.2.1]
11
+
12
+ ### Changed
13
+
14
+ - Removed the dead typed `MessageHandler.Handle*` callback methods and internalized the
15
+ three global callbacks still used by the message bus. These methods were nominally
16
+ public plumbing rather than supported registration APIs; bus dispatch and token
17
+ registration behavior are unchanged.
18
+ - Refreshed the README banner (`docs/images/DxMessaging-banner.svg`): added
19
+ vertical separation between the tagline and the badge row, replaced the badges
20
+ with **Simple**, **MIT**, and the synced version, and re-drew the right panel
21
+ as one bus spine dispatching three aligned, color-coded message dots along
22
+ routes to labeled **Untargeted**, **Targeted**, and **Broadcast** destinations.
23
+ - Simplified banner sync (`scripts/sync-banner-version.js`) to track only the
24
+ package version; the auto-derived test-count badge and its file-walking logic
25
+ were removed, along with the now-unneeded test-source triggers in the CI banner
26
+ gate and the pre-commit hook.
27
+
28
+ ### Added
29
+
30
+ - Added a **Diagnostics Tooling Exerciser** sample scene that deterministically
31
+ drives Message Monitor, Flow Graph, Inspector diagnostics, and Project
32
+ Settings with untargeted, targeted, broadcast, exact-source broadcast,
33
+ broadcast-without-source, and `RegisterGlobalAcceptAll` routes.
34
+ - Project Settings now exposes the **Ignored Base-Call Types** list under
35
+ Inspector Checks, so project-level base-call suppressions can be reviewed and
36
+ edited from the same settings page as the overlay toggles.
37
+ - Flow Graph now includes visible trace-id lanes and visible trace route-kind
38
+ lanes over visible trace paths, reporting path-membership breadth or
39
+ route-kind delivery breadth plus message, target, and context details without
40
+ changing export schema v5.
41
+ - Message Monitor now includes visible message-type and context lanes over the
42
+ current filter, reporting entry counts, distinct context/message breadth,
43
+ entry share, context lists, message lists, and row-level Filter shortcuts,
44
+ including exact quoted context shortcuts, without changing JSON export.
45
+ - Message Monitor filtering now supports typed `type:`, `message:`,
46
+ `context:`, and `stack:` facets over captured entry fields while preserving
47
+ existing plain text matching, supports quoted typed values with spaces, and
48
+ displays the active typed or plain filter with a Clear action.
49
+
50
+ ## [3.2.0]
51
+
52
+ ### Added
53
+
54
+ - Three per-kind **marginal registration** benchmark scenarios
55
+ (`UntargetedRegistration_Marginal`, `TargetedRegistration_Marginal`,
56
+ `BroadcastRegistration_Marginal`) that measure the GC-allocation cost of an additional
57
+ same-type registration on an already-warm bus -- the surface the registration
58
+ allocation reductions target -- so the published performance tables now show per-kind
59
+ registration cost (allocation count + bytes), not just dispatch throughput. Each
60
+ registers 1000 more handlers of one warmed message type using distinct, pre-built
61
+ handler delegates, so the measured window captures only the registration machinery
62
+ (never the handler delegate, and never a same-handler refcount bump). They are
63
+ wall-clock (zero-throughput) rows whose allocation columns populate on the
64
+ profiler-bearing in-editor PlayMode/Mono leg and read `n/a` on the published Standalone
65
+ IL2CPP Release leg (which strips the profiler), exactly like the existing
66
+ registration-flood rows.
67
+ - Benchmarks now report the total allocated BYTES per measurement batch (`gcAllocatedBytes`)
68
+ alongside the existing managed-allocation CALL count (`gcAllocations`). Bytes are
69
+ measured from a before/after delta of the live Unity
70
+ `ProfilerRecorder(ProfilerCategory.Memory, "GC Allocated In Frame").CurrentValue`
71
+ -- a within-frame `GC.Alloc`-hook byte accumulator that SUMS allocation-hook bytes
72
+ rather than measuring a heap-size difference, so it is exact (verified: 100 x
73
+ `byte[10000]` reads 1,003,200 bytes, run-to-run identical) and immune to mid-window
74
+ collections (a heavy-churn region that swung a `GC.GetTotalMemory` delta to -133 MB
75
+ read a stable 8,000,000 bytes here). The byte counter is profiler-dependent
76
+ like the count probe: in the editor and development players it is functional, and on
77
+ the published non-development Standalone IL2CPP Release leg -- where the profiler is
78
+ stripped -- BOTH metrics report the `Unmeasured` sentinel (rendered `n/a`), never a
79
+ fabricated `0`. Bytes are INFORMATIONAL -- the perf-delta PR comment renders byte
80
+ deltas goodness-signed (`N fewer bytes` / `N more bytes`) -- while the regression gate
81
+ stays on the allocation COUNT, which remains the canonical signal (Unity's own
82
+ Performance Testing package likewise reads the alloc-CALL count, not bytes). The
83
+ cross-library comparison tables now also surface this byte metric as a dedicated
84
+ GC-allocated-bytes matrix, and the Unity `SendMessage` comparison boxes its value
85
+ payload on every dispatch (as any real `SendMessage(value)` call must) so its
86
+ allocation columns show that real per-call cost instead of a misleading zero. New API:
87
+ `AllocationProbe.MeasureWithBytes` returning `AllocationProbe.AllocationSample`,
88
+ `AllocationProbe.Window.SampleBytes()`/`SampleBoth()`,
89
+ `AllocationProbe.BytesFunctional`, `BenchmarkMeasurement.GcAllocatedBytes`,
90
+ `ColdLatencyMeasurement.MedianGcAllocatedBytes`, and
91
+ `MinimumMeasurement<T>.GcAllocatedBytes`.
92
+ - New "What's New in 3.x" documentation page (Getting Started) that summarizes
93
+ the user-visible improvements across the 3.x line -- faster zero-allocation
94
+ dispatch, the base-call analyzer and inspector overlay, project-wide settings,
95
+ memory-reclamation controls, `ReregisterOnEnableAfterRelease`, the clarified
96
+ `ToggleMessageHandler` semantics, Unity 6.4+/6.5 compatibility, and the
97
+ dependency-injection helpers -- with links to the authoritative changelog and
98
+ the migration guide.
99
+
100
+ ### Fixed
101
+
102
+ - The token-creation allocation guard no longer flakes in a warm editor. The
103
+ diagnostics-lazy win (token `Create` allocating 7 managed objects instead of 11) was
104
+ guarded by an absolute `GC.Alloc` recorder COUNT budget, but a warm, long-lived editor
105
+ domain attributes background allocations to whatever measurement window is open --
106
+ measured on the host editor, the minimum over 64 windows was ~19 allocations for that
107
+ 7-allocation operation (median ~51), well above any budget tight enough to catch the
108
+ revert -- so the guard false-failed run-to-run. A measure-first probe also disproved the
109
+ assumption that the swing came from the `DxPools` collection pools: the steady refcount
110
+ registration path does not rent from them at all (hits = misses = 0), so the noise is
111
+ pure background-editor `GC.Alloc` pollution that no pool pre-warm can remove. The guard
112
+ is now a DETERMINISTIC state assertion -- after `Create` with diagnostics off, the lazy
113
+ `_callCounts`/`_emissionBuffer` backing fields must still be `null` -- which uses no
114
+ allocation probe, never flakes, and runs in the per-PR EditMode correctness leg rather
115
+ than the weekly perf-gated Allocation scope.
116
+ - Two more memory-reclamation/registration allocation guards no longer flake in a warm
117
+ editor. The forced-trim guard measured a `GC.Alloc` COUNT budget over a 32-trim window
118
+ and the dirty-target reuse guard a per-batch count budget; both false-failed run-to-run
119
+ on the same warm-editor ambient-noise floor that the token-creation guard hit. The
120
+ forced-trim guard is now a DETERMINISTIC assertion on the exact `IMessageBus.TrimResult`
121
+ eviction counts (the first force-trim reclaims; every subsequent one is an idempotent
122
+ no-op that evicts nothing and leaves the live slot count stable) plus a `DxPools`
123
+ Misses-flat check (repeated no-op trims rent no fresh pooled collection); the
124
+ dirty-target guard now relies solely on its existing exact pool Hits/Misses assertions
125
+ (renting the warmed collection, never allocating a fresh one). Neither uses an allocation
126
+ probe, so neither flakes. The two deterministic registration STORAGE structural guards
127
+ (the by-value metadata parameter and the staging-function map value type) were also moved
128
+ from the weekly perf-gated Allocation suite into the per-PR EditMode correctness leg, so
129
+ they protect every PR. Internal test/quality change only; no public API or runtime
130
+ behavior change.
131
+ - Benchmark and library-comparison allocation reporting is now honest. The harness
132
+ measured allocations with `GC.GetAllocatedBytesForCurrentThread()`, which returns
133
+ `0` for every allocation under Unity's Boehm GC (verified: a forced 1 MB array
134
+ allocation read back a `0`-byte delta), so the published "allocated bytes" column
135
+ was a vacuous `0` for every technology -- hiding real per-operation allocations
136
+ (Unity `SendMessage`, for example, boxes its value-type payload once per call).
137
+ The metric is now a COUNT of managed allocations from the reliable `GC.Alloc`
138
+ profiler recorder (new `AllocationProbe`), renamed `gcAllocations`, and reports an
139
+ `Unmeasured` sentinel (rendered `n/a`) -- never a fabricated `0` -- when no probe
140
+ is available on a backend. The perf-doc/PR-comment renderers, the regression gate,
141
+ and the methodology docs were updated accordingly; the doc's allocation column
142
+ reads `n/a` until the next CI run repopulates real counts.
143
+ - Source generators (`[DxUntargetedMessage]`, `[DxTargetedMessage]`,
144
+ `[DxBroadcastMessage]`, `[DxAutoConstructor]`) now work in projects that do not use
145
+ assembly definitions. The generator and analyzer DLLs previously shipped under the
146
+ editor-only `Editor/Analyzers/` folder, which Unity scopes to the package's editor
147
+ assembly and assemblies that reference it -- so a consumer's runtime code in
148
+ `Assembly-CSharp` (or a runtime asmdef referencing only the runtime assembly) never
149
+ received the generator, and `[Dx*Message]` types failed to implement their generated
150
+ interface with cryptic `CS0315`/`CS0452` errors. The labeled DLLs now ship under
151
+ `Runtime/Analyzers/` (governed by the all-platforms runtime assembly), so Unity
152
+ applies the generator to the DxMessaging runtime assembly and every assembly that
153
+ references it, including the predefined `Assembly-CSharp`. No assembly definition is
154
+ required. Closes GitHub issue #229.
155
+ - Documentation no longer teaches the obsolete `IMessageBus.GlobalDiagnosticsMode`
156
+ API. The reference, patterns, glossary, and migration guides now use
157
+ `IMessageBus.GlobalDiagnosticsTargets` with the `DiagnosticsTarget` flags enum
158
+ (matching the canonical diagnostics guide), and the API reference no longer shows a
159
+ write to the read-only `IMessageBus.DiagnosticsMode` property. A source-derived
160
+ drift-guard (`DocsObsoleteApiReferenceTests`) now fails the build if any published
161
+ doc references a member marked `[Obsolete]` in the runtime, so this cannot regress.
162
+
163
+ ### Changed
164
+
165
+ - Registration now allocates one fewer managed object per handler in the common case. The
166
+ registration token stored every handle's de-registration in a per-handle holder object,
167
+ but the common case is EXACTLY ONE de-registration per handle, so the single
168
+ de-registration delegate is now stored INLINE in the token's de-registration map (with no
169
+ holder object); a holder is allocated only when a rare second de-registration accumulates
170
+ on the same handle (the re-entrant retarget-recovery replay). The ordering,
171
+ partial-failure, and rollback-baseline semantics are unchanged (pinned by the existing
172
+ `PendingDeregistrationStorageTests` and the full re-entrancy suite), and a new
173
+ `RegistrationStorageStructuralGuardTests.DeregistrationsStoreInlineActionNotPerHandleHolder`
174
+ deterministically guards the inline storage against regression. Internal change only; no
175
+ public API or behavior change.
176
+ - **BREAKING (v4):** the `IMessageBus` registration contract now returns an opaque,
177
+ zero-allocation handle instead of a deregistration delegate. The 14
178
+ `IMessageBus.Register*<T>` methods return a new `readonly struct`
179
+ `DxMessaging.Core.MessageBus.MessageBusRegistration` (previously `System.Action`), and a
180
+ new `void IMessageBus.Deregister<T>(in MessageBusRegistration registration) where T : IMessage`
181
+ undoes a registration (call it with the same `T` you registered with; for
182
+ `RegisterGlobalAcceptAll`, any `T : IMessage` works since the global slot is not
183
+ type-keyed). This removes the per-registration bus-side deregistration **closure** (the
184
+ prior `Action` plus its display class) from the handler registration path -- roughly
185
+ **two fewer managed allocations per registration** -- by packing the deregistration
186
+ snapshot into the returned value handle and re-expressing the bus's deregistration logic
187
+ against it (behaviour and the four reentrancy invariants -- generation guard, identity
188
+ liveness / over-deregistration, token idempotency, no counter underflow -- are unchanged).
189
+ Custom `IMessageBus` implementers that wrap `MessageBus` (the
190
+ `DelegatingMessageBus`-style extension point) just forward the new members; from-scratch
191
+ implementers mint handles via the public `MessageBusRegistration(long, object)`
192
+ constructor and read them back in their own `Deregister<T>`.
193
+ **The Unity-facing surface is unaffected:** `MessageRegistrationToken` and
194
+ `MessageAwareComponent` keep their existing public shapes. The `MessageHandler.Register*`
195
+ facades were narrowed from `public` to `internal` (they now return the internal
196
+ `HandlerDeregistration`, still implicitly an `Action`) as part of this bus<->handler
197
+ boundary rework -- see the dedicated entry below; only code that called those facades
198
+ **directly** is affected. The larger per-registration allocation reduction (collapsing the
199
+ token/handler closures onto a single per-handle `Registration` object) landed on top of this
200
+ contract change -- see the entries below.
201
+ - The published performance report no longer prints columns of `n/a`. The
202
+ Standalone IL2CPP leg runs in a Release player whose stripped profiler cannot
203
+ measure GC allocations or bytes, so the renderer now OMITS a memory column from a
204
+ per-scope dispatch table when every row is unmeasured (the Standalone table is
205
+ throughput-only), omits a whole cross-library memory matrix when no leg measured
206
+ that metric, and drops the unmeasured allocation/byte segment from each per-PR
207
+ delta cell -- instead of filling them with a wall of `n/a`. The real allocation
208
+ and byte numbers still publish from the in-editor PlayMode (Mono) leg, and `n/a`
209
+ now appears only as a genuine per-row or per-library cell (a metric measured for
210
+ the scope in general but missing for that one entry).
211
+ - Registration allocates less. Each registration token's diagnostics-only
212
+ call-count and emission-history collections are now created lazily instead of
213
+ eagerly, so a token whose owner never enables diagnostics (the default) no longer
214
+ pays for them -- token creation drops from 11 to 7 managed allocations. The
215
+ per-registration metadata is now passed by value instead of through a closure
216
+ factory that was invoked immediately, removing one delegate allocation per handler
217
+ registration. Steady-state dispatch stays allocation-free, and diagnostics, the
218
+ inspector overlay, and all lifecycle behavior are unchanged.
219
+ - Registering a handler OR a post-processor with the convenience `Action<T>` /
220
+ `Action<InstanceId, T>` overloads now allocates one fewer closure per registration.
221
+ This covers the handler registers (`RegisterUntargeted`, `RegisterTargeted`,
222
+ `RegisterTargetedWithoutTargeting`, the sourced-broadcast register, and
223
+ `RegisterBroadcastWithoutSource`) and the matching post-processor registers
224
+ (`RegisterTargetedPostProcessor`, `RegisterTargetedWithoutTargetingPostProcessor`, the
225
+ GameObject/Component/source broadcast post-processors, and
226
+ `RegisterBroadcastWithoutSourcePostProcessor`). Each such registration previously built
227
+ two delegates -- a diagnostics-augmented `Action` wrapper plus a separate by-ref
228
+ `FastHandler` adapter for the flat-dispatch path -- and now folds diagnostics into a
229
+ single by-ref `FastHandler` closure that is the dispatch target directly (also removing a
230
+ per-dispatch indirection). The public API is unchanged; the optimization is internal and
231
+ verified by differential allocation guards that pin the `Action` registration cost to the
232
+ already-optimal `FastHandler` registration cost. Diagnostics, deduplication, and dispatch
233
+ ordering are preserved.
234
+ - Every registration kind now allocates about two fewer managed objects. The token used to
235
+ wrap each staged registration in a per-registration parameterless `Action` (a delegate plus
236
+ its display class) whose only job was to re-bundle the handle, the staging function, and the
237
+ de-registration bookkeeping; the token now stores each staging function directly and pairs it
238
+ with its handle in the replay queue, so that wrapper -- and the closure `InternalRegister`
239
+ needed to build it -- is gone. Measured cold-registration floor (FastHandler, diagnostics off):
240
+ untargeted drops from 14.69 to 12.69 managed allocations per registration (a clean -2.00), with
241
+ the same ~2-allocation reduction across targeted, broadcast, without-targeting/source, and
242
+ post-processor registrations (~12% fewer registration allocations overall). The de-registration
243
+ replay, rollback-on-failure, re-entrancy, and equal-priority registration-order semantics are
244
+ unchanged; the public API is unchanged. Pinned structurally by a deterministic guard that the
245
+ token stores the staging function (not an `Action` wrapper).
246
+ - Every active registration now allocates about two fewer managed objects again. The token
247
+ tracked each handle's live de-registration in a per-handle holder that eagerly allocated a
248
+ `List` (plus its backing array) to hold what is almost always a single de-registration; the
249
+ holder now keeps that one de-registration inline and only allocates an overflow list on the
250
+ rare second de-registration for the same handle. Measured cold-registration floor (FastHandler,
251
+ diagnostics off): untargeted drops from 13.29 to 11.29 managed allocations per registration (a
252
+ clean -2.00), with the same ~2-allocation reduction across targeted and broadcast. The
253
+ insertion-order, partial-failure-retryable, and rollback-baseline de-registration semantics are
254
+ unchanged (verified by a differential simulation across every count/failure/start-index
255
+ combination); the public API is unchanged.
256
+ - Each handler/post-processor registration now allocates one fewer managed object by replacing
257
+ its per-registration de-registration **closure** with a per-handle **object**. The typed
258
+ handler used to hand back a captured parameterless `Action` (a delegate plus the display class
259
+ holding the slot, cache key, priority, and generation it closed over) as the thing the token
260
+ would later invoke to tear the registration down; that closure is now a single per-handle
261
+ `HandlerDeregistration` object whose `Deregister()` instance method carries the same captured
262
+ state as plain fields (and converts implicitly to `Action`, so the token's replay/rollback
263
+ machinery is untouched). To let one non-generic object tear down a generic
264
+ `HandlerActionCache<TU>` without re-introducing a per-handler-delegate-type closure, the erased
265
+ `IHandlerActionCache` gained three non-generic operations (`ContainsEntry`, `BumpVersion`,
266
+ `DeregisterEntry`). The de-registration **order** (generation guard, slot-version liveness,
267
+ keyed-vs-scalar split, identity/over-de-registration check, version bump, bus de-register,
268
+ refcount decrement) is a faithful re-expression of the prior closure body, pinned by the 19
269
+ arbitrary-order `MixedOrderDeregistrationTests` and the full re-entrancy suite. Because these
270
+ `MessageHandler.Register*` facades now return the internal `HandlerDeregistration` (still
271
+ implicitly an `Action`), they were narrowed from `public` to `internal` -- this is part of the
272
+ v4 bus<->handler boundary rework and does NOT touch the Unity-facing surface
273
+ (`MessageRegistrationToken`, `MessageAwareComponent`); only code calling
274
+ `MessageHandler.Register*` **directly** is affected.
275
+ - Every registration kind now allocates roughly half as many managed objects again by
276
+ collapsing the token's per-handle staging closures into one unified per-handle object.
277
+ Each registration previously staged a `Func<handle, HandlerDeregistration>` (a delegate plus
278
+ its display class capturing the target/source, user handler, and priority) whose nested
279
+ `AugmentedHandler` local function became a _second_ delegate (the diagnostics-augmented flat
280
+ invoker). Both are now a single per-handle `Registration` object stored in `_registrations`:
281
+ its fields hold the captured staging state, and its diagnostics-augmented invoker is an
282
+ _instance method_ bound to the object -- so `MessageHandler` still receives a `FastHandler<T>`
283
+ delegate and the hot dispatch path stays delegate-based (no virtual/interface call per
284
+ dispatch). A `Register()` method runs a kind-switch to call the matching
285
+ `MessageHandler.Register*` and reproduce the exact prior staging body per kind. Because the
286
+ constrained `MessageHandler.Register*<T>` calls require `T : ITargetedMessage` /
287
+ `IUntargetedMessage` / `IBroadcastMessage` (which a single `Registration<T> where T : IMessage`
288
+ cannot satisfy), the object is realized as three constraint-family generic subclasses plus one
289
+ non-generic global-accept-all subclass over a non-generic `Registration` base -- still a
290
+ unified per-handle object with a kind-switch, not a per-method subclass explosion. Measured
291
+ marginal cost (cold, FastHandler, diagnostics off) drops from ~9.3 to ~4.6 managed allocations
292
+ per registration (about half). The equal-priority registration-order dispatch, idempotent
293
+ double-deregister, partial-failure rollback, generation/slot-version guards, refcount handlers,
294
+ and diagnostics call-counts/emission semantics are all unchanged (pinned by the 19
295
+ `MixedOrderDeregistrationTests` and the full re-entrancy suite); the public API is unchanged.
296
+ Pinned structurally by a deterministic guard that `_registrations` stores the unified
297
+ `Registration` object, not a `Func`/`Action` wrapper.
298
+ - Deregistration is now O(1) on the steady path. The v4 bus `Deregister<T>` had been
299
+ re-resolving the sink from the registration method (a `switch` plus `ScalarSinkForMethod` /
300
+ `ContextSinkForMethod`) and re-walking the whole sink -> type -> [context ->] priority ->
301
+ handler chain on every call -- even though the leaf handler-cache is already captured on the
302
+ registration handle. It now operates on that captured cache DIRECTLY, deferring the
303
+ re-resolution to a cold fallback taken only when the handler is not found there (the rare
304
+ post-sweep or over-deregistration case, which the fallback classifies as a silent no-op vs an
305
+ error). This removes the per-deregistration re-resolution that had regressed the
306
+ deregistration-heavy teardown path, with no change to dispatch, registration allocations, the
307
+ generation/sweep-staleness/over-deregistration guards, or the public API; the throw-safe
308
+ ordering (the handler-cache mutation stays after the throwing `IMessageBus.Deregister`) is
309
+ unchanged. A stale over-deregistration whose `(type, priority)` slot was reused by a different
310
+ handler now leaves the live registration completely untouched (new
311
+ `StaleOverDeregistrationTests` regression coverage). The token's de-registration store also
312
+ records the common first-registration in a single dictionary operation (`TryAdd`) instead of a
313
+ probe-then-insert pair.
314
+ - The bug-report issue template now offers the package version as a dropdown of
315
+ released versions (with an `Other` fallback) instead of a free-text field, so
316
+ reports carry an exact, valid version. The list is generated from
317
+ `package.json`, `CHANGELOG.md`, and git tags, kept in sync by
318
+ `npm run check:issue-template-versions` (gated in `validate:all`), and
319
+ self-heals on the default branch via the `Update Issue Template Versions`
320
+ workflow. Closes GitHub issue #230.
321
+ - The Roslyn source generator no longer copies its DLLs into the consumer's
322
+ `Assets/Plugins/Editor/` folder on editor load; it (with its pinned Roslyn dependency
323
+ DLLs) ships ready-to-use under the package's `Runtime/Analyzers/` folder
324
+ (RoslynAnalyzer-labeled, excluded from player builds). Projects upgrading from an
325
+ earlier version have the redundant in-project copy removed automatically during asset
326
+ import, before script compilation, so the package's copy and the old in-project copy
327
+ never both run the generator (which would otherwise duplicate generated members). No
328
+ manual action is required: the cleanup only removes the
329
+ `Assets/Plugins/Editor/WallstopStudios.DxMessaging` folder when it contains the
330
+ first-party source-generator DLL plus exact known legacy analyzer/dependency DLL names
331
+ the package created, and leaves any foreign DLL, foreign `.meta`, subfolder, or
332
+ other content untouched. If a mixed or incomplete legacy payload is detected, the
333
+ editor logs one warning with manual cleanup guidance instead of deleting the folder
334
+ silently.
335
+
10
336
  ## [3.1.0]
11
337
 
12
338
  ### Added
@@ -6,6 +6,6 @@ MonoImporter:
6
6
  defaultReferences: []
7
7
  executionOrder: 0
8
8
  icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -6,6 +6,6 @@ MonoImporter:
6
6
  defaultReferences: []
7
7
  executionOrder: 0
8
8
  icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -84,13 +84,13 @@ namespace DxMessaging.Editor.Analyzers
84
84
  /// </para>
85
85
  /// <para>
86
86
  /// The inspector overlay reads its snapshot from the unified per-FQN map populated here on
87
- /// every rescan. Use the menu <c>Tools > DxMessaging > Rescan Base-Call Warnings</c> for a
87
+ /// every rescan. Use the menu <c>Tools > Wallstop Studios > DxMessaging > Rescan Base-Call Warnings</c> for a
88
88
  /// manual force-rescan.
89
89
  /// </para>
90
90
  /// <para>
91
91
  /// <see cref="IsAvailable"/> stays <c>true</c> as long as the static constructor itself does
92
92
  /// not throw -- the IL scanner is always wired, so the overlay never falls back to its
93
- /// degraded "harvester unavailable" HelpBox in normal operation. <see cref="LogEntriesAvailable"/>
93
+ /// degraded "harvester unavailable" warning surface in normal operation. <see cref="LogEntriesAvailable"/>
94
94
  /// continues to report whether the legacy reflection layer is bindable, for diagnostics only.
95
95
  /// </para>
96
96
  /// </remarks>
@@ -187,7 +187,7 @@ namespace DxMessaging.Editor.Analyzers
187
187
  // or whether it was loaded eagerly from `Library/DxMessaging/baseCallReport.json` in the
188
188
  // static ctor and has not yet been overwritten. The inspector overlay reads this to
189
189
  // distinguish "fresh-this-session" warnings from cached-from-previous-session warnings;
190
- // when the cache is showing, we annotate the HelpBox with a small suffix so the user
190
+ // when the cache is showing, we annotate the warning surface so the user
191
191
  // understands the data may be stale until the first post-reload scan completes.
192
192
  //
193
193
  // Default `false`: the static ctor's `LoadFromDisk` runs first, so by the time anything
@@ -241,7 +241,7 @@ namespace DxMessaging.Editor.Analyzers
241
241
  /// constructor itself throws (a hard initialization failure). The LogEntries reflection
242
242
  /// layer is the optional source -- see <see cref="LogEntriesAvailable"/> for that flag.
243
243
  /// The inspector overlay reads this property to decide whether to render its degraded
244
- /// HelpBox, so the contract here is "should the overlay attempt to render at all".
244
+ /// warning surface, so the contract here is "should the overlay attempt to render at all".
245
245
  /// </remarks>
246
246
  public static bool IsAvailable { get; private set; } = true;
247
247
 
@@ -259,11 +259,11 @@ namespace DxMessaging.Editor.Analyzers
259
259
  /// loaded eagerly by the static constructor.
260
260
  /// </summary>
261
261
  /// <remarks>
262
- /// The inspector overlay reads this to annotate its HelpBox: when <c>false</c> AND a
263
- /// warning is being shown, the overlay appends a "(cached from previous session --
264
- /// refreshing...)" suffix so the user knows the data is from yesterday's scan and a fresh
262
+ /// The inspector overlay reads this to annotate its warning surface: when <c>false</c> AND a
263
+ /// warning is being shown, the overlay adds a "Report is cached from previous session;
264
+ /// refreshing..." note so the user knows the data is from yesterday's scan and a fresh
265
265
  /// one is in flight. The flag is set inside <see cref="RescanNow"/> and never reset, so
266
- /// the suffix disappears as soon as the first post-reload scan lands and stays gone for
266
+ /// the note disappears as soon as the first post-reload scan lands and stays gone for
267
267
  /// the rest of the session.
268
268
  /// </remarks>
269
269
  public static bool IsFreshThisSession => _isFreshThisSession;
@@ -6,6 +6,6 @@ MonoImporter:
6
6
  defaultReferences: []
7
7
  executionOrder: 0
8
8
  icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -1,14 +1,17 @@
1
1
  namespace DxMessaging.Editor.CustomEditors
2
2
  {
3
3
  #if UNITY_EDITOR
4
+ using System;
5
+ using DxMessaging.Editor.Analyzers;
4
6
  using DxMessaging.Unity;
5
7
  using UnityEditor;
8
+ using UnityEditor.UIElements;
9
+ using UnityEngine.UIElements;
6
10
 
7
11
  /// <summary>
8
- /// Primary CustomEditor for every <see cref="MessageAwareComponent"/> subclass. Renders the
9
- /// DxMessaging warning HelpBox above an inspector body that is byte-for-byte identical to
10
- /// Unity's default <c>GenericInspector</c> (achieved via
11
- /// <see cref="Editor.DrawDefaultInspector"/>).
12
+ /// Primary CustomEditor for every <see cref="MessageAwareComponent"/> subclass. Hosts the
13
+ /// DxMessaging warning above a default inspector body that keeps Unity's serialized field
14
+ /// surface intact.
12
15
  /// </summary>
13
16
  /// <remarks>
14
17
  /// We register as a non-fallback (primary) editor with
@@ -33,20 +36,20 @@ namespace DxMessaging.Editor.CustomEditors
33
36
  /// </list>
34
37
  ///
35
38
  /// <para>
36
- /// The current design is the simple one: be the primary editor, prepend the overlay's
37
- /// HelpBox via <see cref="MessageAwareComponentInspectorOverlay.RenderInsideOnInspectorGUI"/>,
38
- /// then call <see cref="Editor.DrawDefaultInspector"/>. The body therefore matches
39
- /// <c>GenericInspector</c> exactly: no missing Script row, no extra vertical gap. To avoid
40
- /// double-rendering when the header hook ALSO fires for our editor instance (Unity 2022+),
39
+ /// The current primary path is <see cref="CreateInspectorGUI"/>: it hosts the UI Toolkit
40
+ /// warning view, then fills Unity's default Inspector body through
41
+ /// <see cref="InspectorElement.FillDefaultInspector"/> so the disabled Script row and
42
+ /// serialized fields remain present. To avoid double-rendering when the header hook ALSO
43
+ /// fires for our editor instance (Unity 2022+),
41
44
  /// <see cref="MessageAwareComponentInspectorOverlay"/> unconditionally skips the header path
42
45
  /// for <see cref="MessageAwareComponentFallbackEditor"/> instances.
43
46
  /// </para>
44
47
  ///
45
48
  /// <para>
46
- /// We do NOT short-circuit <see cref="OnInspectorGUI"/> on event type. Unity invokes
47
- /// editors twice per frame (Layout + Repaint), and both passes MUST emit identical control
48
- /// counts, otherwise the inspector window's layout cache is corrupted and adjacent
49
- /// components fail to render. See
49
+ /// <see cref="OnInspectorGUI"/> remains as an IMGUI compatibility fallback. We do NOT
50
+ /// short-circuit it on event type. Unity invokes editors twice per frame (Layout + Repaint),
51
+ /// and both passes MUST emit identical control counts, otherwise the inspector window's layout
52
+ /// cache is corrupted and adjacent components fail to render. See
50
53
  /// <see cref="MessageAwareComponentInspectorOverlay.RenderInsideOnInspectorGUI"/> for the
51
54
  /// matching invariant on the overlay side.
52
55
  /// </para>
@@ -63,6 +66,23 @@ namespace DxMessaging.Editor.CustomEditors
63
66
  [CanEditMultipleObjects]
64
67
  public sealed class MessageAwareComponentFallbackEditor : Editor
65
68
  {
69
+ internal const string RootName = "dxmessaging-fallback-inspector";
70
+ internal const string RootClassName = "dxmessaging-fallback-inspector";
71
+ internal const string WarningHostName = "dxmessaging-fallback-inspector-warning-host";
72
+ internal const string WarningHostClassName = "dxmessaging-fallback-inspector-warning-host";
73
+ internal const string DefaultInspectorBodyName =
74
+ "dxmessaging-fallback-inspector-default-body";
75
+ internal const string DefaultInspectorBodyClassName =
76
+ "dxmessaging-fallback-inspector-default-body";
77
+
78
+ public override VisualElement CreateInspectorGUI()
79
+ {
80
+ return BuildInspectorGUI(
81
+ this,
82
+ () => MessageAwareComponentInspectorOverlay.ResolveInspectorState(target)
83
+ );
84
+ }
85
+
66
86
  public override void OnInspectorGUI()
67
87
  {
68
88
  // Render the overlay BEFORE the default body so the warning appears prominently at
@@ -76,6 +96,144 @@ namespace DxMessaging.Editor.CustomEditors
76
96
  // [SerializeField] fields.
77
97
  DrawDefaultInspector();
78
98
  }
99
+
100
+ internal static VisualElement BuildInspectorGUI(
101
+ Editor editor,
102
+ Func<MessageAwareComponentInspectorState> resolveState
103
+ )
104
+ {
105
+ if (editor == null)
106
+ {
107
+ throw new ArgumentNullException(nameof(editor));
108
+ }
109
+ if (resolveState == null)
110
+ {
111
+ throw new ArgumentNullException(nameof(resolveState));
112
+ }
113
+
114
+ VisualElement root = new() { name = RootName };
115
+ DxMessagingEditorTheme.Apply(root);
116
+ root.AddToClassList(RootClassName);
117
+
118
+ VisualElement warningHost = new() { name = WarningHostName };
119
+ warningHost.AddToClassList(WarningHostClassName);
120
+ root.Add(warningHost);
121
+
122
+ InspectorWarningBinding binding = new(warningHost, resolveState);
123
+ root.userData = binding;
124
+ binding.Refresh();
125
+
126
+ VisualElement defaultBody = new() { name = DefaultInspectorBodyName };
127
+ defaultBody.AddToClassList(DefaultInspectorBodyClassName);
128
+ InspectorElement.FillDefaultInspector(defaultBody, editor.serializedObject, editor);
129
+ root.Add(defaultBody);
130
+
131
+ root.RegisterCallback<AttachToPanelEvent>(_ => binding.Connect());
132
+ root.RegisterCallback<DetachFromPanelEvent>(_ => binding.Disconnect());
133
+
134
+ return root;
135
+ }
136
+
137
+ internal static void RefreshInspectorWarning(VisualElement root)
138
+ {
139
+ if (root == null)
140
+ {
141
+ throw new ArgumentNullException(nameof(root));
142
+ }
143
+ if (root.userData is not InspectorWarningBinding binding)
144
+ {
145
+ throw new InvalidOperationException(
146
+ "Fallback inspector root is missing its warning binding."
147
+ );
148
+ }
149
+
150
+ binding.Refresh();
151
+ }
152
+
153
+ private sealed class InspectorWarningBinding
154
+ {
155
+ private readonly VisualElement _warningHost;
156
+ private readonly Func<MessageAwareComponentInspectorState> _resolveState;
157
+ private bool _connected;
158
+ private bool _refreshRetryScheduled;
159
+
160
+ internal InspectorWarningBinding(
161
+ VisualElement warningHost,
162
+ Func<MessageAwareComponentInspectorState> resolveState
163
+ )
164
+ {
165
+ _warningHost = warningHost ?? throw new ArgumentNullException(nameof(warningHost));
166
+ _resolveState =
167
+ resolveState ?? throw new ArgumentNullException(nameof(resolveState));
168
+ }
169
+
170
+ internal void Connect()
171
+ {
172
+ if (_connected)
173
+ {
174
+ return;
175
+ }
176
+
177
+ DxMessagingConsoleHarvester.ReportUpdated += Refresh;
178
+ MessageAwareComponentInspectorOverlay.InspectorStateInvalidated += Refresh;
179
+ _connected = true;
180
+ Refresh();
181
+ }
182
+
183
+ internal void Disconnect()
184
+ {
185
+ if (!_connected)
186
+ {
187
+ return;
188
+ }
189
+
190
+ DxMessagingConsoleHarvester.ReportUpdated -= Refresh;
191
+ MessageAwareComponentInspectorOverlay.InspectorStateInvalidated -= Refresh;
192
+ _connected = false;
193
+ }
194
+
195
+ internal void Refresh()
196
+ {
197
+ MessageAwareComponentInspectorState state = _resolveState();
198
+ if (state.Kind == MessageAwareComponentInspectorStateKind.None)
199
+ {
200
+ if (
201
+ MessageAwareComponentInspectorOverlay.IsInspectorResolutionTransientlyBlocked()
202
+ )
203
+ {
204
+ ScheduleRefreshRetry();
205
+ return;
206
+ }
207
+
208
+ _warningHost.Clear();
209
+ _warningHost.style.display = DisplayStyle.None;
210
+ return;
211
+ }
212
+
213
+ _refreshRetryScheduled = false;
214
+ _warningHost.Clear();
215
+ _warningHost.style.display = DisplayStyle.Flex;
216
+ _warningHost.Add(MessageAwareComponentInspectorOverlay.CreateInspectorView(state));
217
+ }
218
+
219
+ private void ScheduleRefreshRetry()
220
+ {
221
+ if (_refreshRetryScheduled)
222
+ {
223
+ return;
224
+ }
225
+
226
+ _refreshRetryScheduled = true;
227
+ System.Action<System.Action> schedule =
228
+ MessageAwareComponentInspectorOverlay.TransientRefreshScheduler
229
+ ?? DxMessagingEditorIdle.ScheduleAssetDatabaseMutation;
230
+ schedule(() =>
231
+ {
232
+ _refreshRetryScheduled = false;
233
+ Refresh();
234
+ });
235
+ }
236
+ }
79
237
  }
80
238
  #endif
81
239
  }
@@ -6,6 +6,6 @@ MonoImporter:
6
6
  defaultReferences: []
7
7
  executionOrder: 0
8
8
  icon: {instanceID: 0}
9
- userData:
10
- assetBundleName:
11
- assetBundleVariant:
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: