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.
- package/CHANGELOG.md +326 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +3 -3
- package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +3 -3
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +8 -8
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +171 -13
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +391 -96
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs +279 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorView.cs.meta +11 -0
- package/Editor/DxMessagingEditorPalette.cs +66 -0
- package/Editor/DxMessagingEditorPalette.cs.meta +11 -0
- package/Editor/DxMessagingEditorTheme.cs +200 -0
- package/Editor/DxMessagingEditorTheme.cs.meta +11 -0
- package/Editor/DxMessagingMenu.cs.meta +3 -3
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +3 -3
- package/Editor/Icons/dxmessaging-icon-256.png +0 -0
- package/Editor/Icons/dxmessaging-icon-256.png.meta +156 -0
- package/Editor/Icons/dxmessaging-icon-32.png +0 -0
- package/Editor/Icons/dxmessaging-icon-32.png.meta +156 -0
- package/Editor/Icons/dxmessaging-icon-48.png +0 -0
- package/Editor/Icons/dxmessaging-icon-48.png.meta +156 -0
- package/Editor/Icons.meta +8 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +2 -3
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +3 -3
- package/Editor/Settings/DxMessagingSettingsProvider.cs +368 -33
- package/Editor/SetupCscRsp.cs +232 -163
- package/Editor/Testing/MessagingComponentEditorHarness.cs +11 -4
- package/Editor/Theme/DxMessagingTheme.uss +255 -0
- package/Editor/Theme/DxMessagingTheme.uss.meta +11 -0
- package/Editor/Theme/DxTokens.uss +90 -0
- package/Editor/Theme/DxTokens.uss.meta +11 -0
- package/Editor/Theme.meta +8 -0
- package/Editor/Windows/DxMessagingFlowGraphWindow.cs +5513 -0
- package/Editor/Windows/DxMessagingFlowGraphWindow.cs.meta +11 -0
- package/Editor/Windows/DxMessagingMessageMonitorWindow.cs +2288 -0
- package/Editor/Windows/DxMessagingMessageMonitorWindow.cs.meta +11 -0
- package/Editor/Windows.meta +8 -0
- package/README.md +84 -35
- package/Runtime/Analyzers.meta +8 -0
- package/Runtime/AssemblyInfo.cs +1 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +3 -3
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs +29 -1
- package/Runtime/Core/DxMessagingStaticState.cs.meta +3 -3
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +3 -3
- package/Runtime/Core/Extensions/MessageExtensions.cs +4 -5
- package/Runtime/Core/Extensions/MessageExtensions.cs.meta +3 -3
- package/Runtime/Core/Helper/MessageCache.cs +56 -23
- package/Runtime/Core/IMessage.cs.meta +3 -3
- package/Runtime/Core/InstanceId.cs.meta +3 -3
- package/Runtime/Core/Internal/FlatDispatch.cs +32 -0
- package/Runtime/Core/Internal/TypedSlots.cs +45 -7
- package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +3 -3
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageBus.cs +63 -28
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +3 -3
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBus.cs +1135 -626
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessageBusRegistration.cs +207 -0
- package/Runtime/Core/MessageBus/MessageBusRegistration.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +3 -3
- package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +3 -3
- package/Runtime/Core/MessageBus/RegistrationLog.cs +25 -12
- package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +3 -3
- package/Runtime/Core/MessageHandler.cs +1886 -1082
- package/Runtime/Core/MessageHandler.cs.meta +3 -3
- package/Runtime/Core/MessageRegistrationHandle.cs +19 -5
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +3 -3
- package/Runtime/Core/MessageRegistrationToken.cs +1932 -895
- package/Runtime/Core/MessageRegistrationToken.cs.meta +3 -3
- package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/ITargetedMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +3 -3
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +3 -3
- package/Runtime/Core/MessagingDebug.cs.meta +3 -3
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +3 -3
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +3 -3
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +3 -3
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +3 -3
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +3 -3
- package/Runtime/Unity/MessageAwareComponent.cs.meta +3 -3
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +3 -3
- package/Runtime/Unity/MessagingComponent.cs.meta +3 -3
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +3 -3
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +3 -3
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +3 -3
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +3 -3
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +3 -3
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs +173 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity +424 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingExerciser.unity.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs +185 -0
- package/Samples~/Diagnostics Tooling Exerciser/DiagnosticsToolingReceiver.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/Messages.cs +46 -0
- package/Samples~/Diagnostics Tooling Exerciser/Messages.cs.meta +11 -0
- package/Samples~/Diagnostics Tooling Exerciser/README.md +53 -0
- package/Samples~/Diagnostics Tooling Exerciser/README.md.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef +13 -0
- package/Samples~/Diagnostics Tooling Exerciser/WallstopStudios.DxMessaging.DiagnosticsToolingExerciser.Sample.asmdef.meta +7 -0
- package/Samples~/Diagnostics Tooling Exerciser.meta +8 -0
- package/Samples~/Mini Combat/Boot.cs.meta +3 -3
- package/Samples~/Mini Combat/Enemy.cs.meta +3 -3
- package/Samples~/Mini Combat/Messages.cs.meta +3 -3
- package/Samples~/Mini Combat/Player.cs.meta +3 -3
- package/Samples~/Mini Combat/README.md +1 -1
- package/Samples~/Mini Combat/UIOverlay.cs.meta +3 -3
- package/Samples~/Mini Combat/Walkthrough.md +3 -3
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/Messages.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +3 -3
- package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +3 -3
- package/SourceGenerators/Directory.Build.props +5 -5
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +3 -3
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +12 -11
- package/docs/images/DxMessaging-banner.svg +79 -0
- package/docs/images/DxMessaging-banner.svg.meta +53 -0
- package/docs/images.meta +8 -0
- package/docs.meta +8 -0
- package/package.json +17 -5
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +0 -0
- /package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- /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
|
|
@@ -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"
|
|
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
|
|
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
|
-
///
|
|
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
|
|
263
|
-
/// warning is being shown, the overlay
|
|
264
|
-
/// refreshing...
|
|
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
|
|
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;
|
|
@@ -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.
|
|
9
|
-
/// DxMessaging warning
|
|
10
|
-
///
|
|
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
|
|
37
|
-
///
|
|
38
|
-
///
|
|
39
|
-
///
|
|
40
|
-
///
|
|
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
|
-
///
|
|
47
|
-
/// editors twice per frame (Layout + Repaint),
|
|
48
|
-
/// counts, otherwise the inspector window's layout
|
|
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
|
}
|