com.wallstop-studios.dxmessaging 3.0.1 → 3.2.0

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 (148) hide show
  1. package/CHANGELOG.md +497 -2
  2. package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +3 -3
  3. package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +3 -3
  4. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +69 -62
  5. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +3 -3
  6. package/Editor/AssemblyInfo.cs.meta +9 -1
  7. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +3 -3
  8. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +24 -15
  9. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +3 -3
  10. package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +9 -1
  11. package/Editor/DxMessagingEditorIdle.cs +62 -0
  12. package/{Runtime/Core/Internal/TypedDispatchLinkIndex.cs.meta → Editor/DxMessagingEditorIdle.cs.meta} +1 -1
  13. package/Editor/DxMessagingEditorInitializer.cs +112 -15
  14. package/Editor/DxMessagingEditorInitializer.cs.meta +9 -1
  15. package/Editor/DxMessagingEditorLog.cs +32 -0
  16. package/Editor/DxMessagingEditorLog.cs.meta +11 -0
  17. package/Editor/DxMessagingMenu.cs.meta +3 -3
  18. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +3 -3
  19. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +137 -15
  20. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +3 -3
  21. package/Editor/Settings/DxMessagingSettings.cs +92 -31
  22. package/Editor/Settings/DxMessagingSettings.cs.meta +9 -1
  23. package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +9 -1
  24. package/Editor/SetupCscRsp.cs +539 -304
  25. package/Editor/SetupCscRsp.cs.meta +9 -1
  26. package/Editor/Testing/MessagingComponentEditorHarness.cs +1 -1
  27. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +9 -1
  28. package/README.md +17 -18
  29. package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +3 -3
  30. package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll.meta +3 -3
  31. package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll.meta +3 -3
  32. package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll.meta +3 -3
  33. package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +3 -3
  34. package/Runtime/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  35. package/Runtime/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +46 -0
  36. package/Runtime/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  37. package/{Editor → Runtime}/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +2 -2
  38. package/Runtime/Analyzers.meta +8 -0
  39. package/Runtime/AssemblyInfo.cs.meta +9 -1
  40. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +9 -1
  41. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +9 -1
  42. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +3 -3
  43. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
  44. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +9 -1
  45. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +9 -1
  46. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +9 -1
  47. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
  48. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
  49. package/Runtime/Core/DataStructure/CyclicBuffer.cs +44 -26
  50. package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +9 -1
  51. package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +9 -1
  52. package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +9 -1
  53. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +9 -1
  54. package/Runtime/Core/DxMessagingStaticState.cs.meta +3 -3
  55. package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
  56. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +9 -1
  57. package/Runtime/Core/Extensions/IListExtensions.cs.meta +9 -1
  58. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +3 -3
  59. package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
  60. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +3 -3
  61. package/Runtime/Core/Helper/MessageCache.cs.meta +9 -1
  62. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +9 -1
  63. package/Runtime/Core/IMessage.cs.meta +3 -3
  64. package/Runtime/Core/InstanceId.cs +25 -1
  65. package/Runtime/Core/InstanceId.cs.meta +3 -3
  66. package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
  67. package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
  68. package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
  69. package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
  70. package/Runtime/Core/Internal/TypedSlots.cs +47 -21
  71. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +3 -3
  72. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +3 -3
  73. package/Runtime/Core/MessageBus/IMessageBus.cs +75 -40
  74. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +3 -3
  75. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +3 -3
  76. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
  77. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +3 -3
  78. package/Runtime/Core/MessageBus/Internal/BusSlots.cs +7 -6
  79. package/Runtime/Core/MessageBus/MessageBus.cs +2864 -3413
  80. package/Runtime/Core/MessageBus/MessageBus.cs.meta +3 -3
  81. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +3 -3
  82. package/Runtime/Core/MessageBus/MessageBusRegistration.cs +207 -0
  83. package/Runtime/Core/MessageBus/MessageBusRegistration.cs.meta +11 -0
  84. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
  85. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +3 -3
  86. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +3 -3
  87. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +3 -3
  88. package/Runtime/Core/MessageHandler.cs +2206 -1510
  89. package/Runtime/Core/MessageHandler.cs.meta +3 -3
  90. package/Runtime/Core/MessageRegistrationHandle.cs.meta +3 -3
  91. package/Runtime/Core/MessageRegistrationToken.cs +1667 -812
  92. package/Runtime/Core/MessageRegistrationToken.cs.meta +3 -3
  93. package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +9 -1
  94. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +3 -3
  95. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +3 -3
  96. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +3 -3
  97. package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +9 -1
  98. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +3 -3
  99. package/Runtime/Core/Messages/StringMessage.cs.meta +9 -1
  100. package/Runtime/Core/MessagingDebug.cs.meta +3 -3
  101. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +3 -3
  102. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +3 -3
  103. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +3 -3
  104. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +9 -1
  105. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +3 -3
  106. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +9 -1
  107. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +3 -3
  108. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +9 -1
  109. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +3 -3
  110. package/Runtime/Unity/MessageAwareComponent.cs +46 -1
  111. package/Runtime/Unity/MessageAwareComponent.cs.meta +3 -3
  112. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +3 -3
  113. package/Runtime/Unity/MessagingComponent.cs +43 -10
  114. package/Runtime/Unity/MessagingComponent.cs.meta +3 -3
  115. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +3 -3
  116. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +3 -3
  117. package/Runtime/WallstopStudios.DxMessaging.asmdef +1 -1
  118. package/Samples~/DI/README.md +7 -7
  119. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +3 -3
  120. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +3 -3
  121. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +3 -3
  122. package/Samples~/Mini Combat/Boot.cs.meta +3 -3
  123. package/Samples~/Mini Combat/Enemy.cs.meta +3 -3
  124. package/Samples~/Mini Combat/Messages.cs.meta +3 -3
  125. package/Samples~/Mini Combat/Player.cs.meta +3 -3
  126. package/Samples~/Mini Combat/UIOverlay.cs.meta +3 -3
  127. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +3 -3
  128. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +3 -3
  129. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +3 -3
  130. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +3 -3
  131. package/SourceGenerators/Directory.Build.props +50 -3
  132. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
  133. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +3 -3
  134. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
  135. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +9 -1
  136. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +40 -46
  137. package/SourceGenerators/global.json +7 -0
  138. package/SourceGenerators/global.json.meta +7 -0
  139. package/package.json +30 -40
  140. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  141. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +0 -33
  142. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  143. package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs +0 -51
  144. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  145. /package/{Editor → Runtime}/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
  146. /package/{Editor → Runtime}/Analyzers/System.Collections.Immutable.dll +0 -0
  147. /package/{Editor → Runtime}/Analyzers/System.Reflection.Metadata.dll +0 -0
  148. /package/{Editor → Runtime}/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
@@ -28,10 +28,9 @@ namespace DxMessaging.Core.Internal
28
28
  /// surface picks up no new members from the interface retrofit.
29
29
  /// </para>
30
30
  /// <para>
31
- /// Deliberately a thin, marker-style surface: only the six members that
31
+ /// Deliberately a thin, marker-style surface: only the five members that
32
32
  /// staged dispatch (<see cref="Version"/>, <see cref="LastSeenVersion"/>,
33
- /// <see cref="LastSeenEmissionId"/>,
34
- /// <see cref="PrefreezeInvocationCount"/>) and eviction
33
+ /// <see cref="LastSeenEmissionId"/>) and eviction
35
34
  /// (<see cref="IsEmpty"/>, <see cref="Reset"/>) require. The
36
35
  /// <c>entries</c> dictionary and <c>cache</c> list are NOT exposed
37
36
  /// because their generic shape is the very thing this interface erases;
@@ -67,15 +66,6 @@ namespace DxMessaging.Core.Internal
67
66
  /// </summary>
68
67
  long LastSeenEmissionId { get; set; }
69
68
 
70
- /// <summary>
71
- /// Number of invocations observed during the prefreeze window for
72
- /// the most recent dispatch. Mirrors
73
- /// <c>HandlerActionCache&lt;TDelegate&gt;.prefreezeInvocationCount</c>.
74
- /// Read-only on this surface; the cache's own dispatchers maintain
75
- /// the value.
76
- /// </summary>
77
- int PrefreezeInvocationCount { get; }
78
-
79
69
  /// <summary>
80
70
  /// True iff the cache currently retains zero entries. Cheap (single
81
71
  /// integer compare against <c>entries.Count</c>); used by the
@@ -87,12 +77,54 @@ namespace DxMessaging.Core.Internal
87
77
  /// <summary>
88
78
  /// Eviction-driven full clear. Empties the entries dictionary and
89
79
  /// the flat cache list, resets <see cref="LastSeenVersion"/> /
90
- /// <see cref="LastSeenEmissionId"/> / <c>prefreezeInvocationCount</c>,
80
+ /// <see cref="LastSeenEmissionId"/>,
91
81
  /// and bumps <see cref="Version"/> as the LAST step so any captured
92
82
  /// dispatch closure that observed the prior version detects
93
83
  /// invalidation. Idempotent.
94
84
  /// </summary>
95
85
  void Reset();
86
+
87
+ /// <summary>
88
+ /// Non-generic membership probe over the cache's entries map. The
89
+ /// per-handle teardown object (which carries no delegate-shape type
90
+ /// argument) uses this instead of a typed
91
+ /// <c>entries.ContainsKey</c> so it can decide whether the
92
+ /// over-deregistration silent-no-op guard fires without down-casting
93
+ /// to the concrete <c>HandlerActionCache&lt;TDelegate&gt;</c>.
94
+ /// </summary>
95
+ /// <param name="originalHandler">
96
+ /// The dedup/identity key (boxed delegate) the teardown captured at
97
+ /// registration time; cast back to the cache's delegate shape by the
98
+ /// implementation.
99
+ /// </param>
100
+ /// <returns>True iff an entry exists for the handler.</returns>
101
+ bool ContainsEntry(object originalHandler);
102
+
103
+ /// <summary>
104
+ /// Bumps <see cref="Version"/> by one. Mirrors the explicit
105
+ /// <c>version++</c> the legacy deregistration closure performed at the
106
+ /// top of its body (before the bus deregister call) so any in-flight
107
+ /// dispatch snapshot detects invalidation at the same point.
108
+ /// </summary>
109
+ void BumpVersion();
110
+
111
+ /// <summary>
112
+ /// Non-generic decrement-or-remove for the captured handler entry.
113
+ /// Mirrors the EXACT refcount logic the legacy deregistration closure
114
+ /// used: if the entry's refcount is at or below one, removes it from
115
+ /// both the entries map and the insertion-order list and bumps
116
+ /// <see cref="Version"/> a second time (reporting
117
+ /// <paramref name="wasLastForEntry"/> = true so the caller can
118
+ /// decrement the owning slot's live count); otherwise decrements the
119
+ /// refcount in place, preserving the first registration's handler /
120
+ /// flat invoker.
121
+ /// </summary>
122
+ /// <param name="originalHandler">The dedup/identity key (boxed delegate).</param>
123
+ /// <param name="wasLastForEntry">
124
+ /// True iff this call removed the entry (the refcount reached zero).
125
+ /// </param>
126
+ /// <returns>True iff an entry existed for the handler.</returns>
127
+ bool DeregisterEntry(object originalHandler, out bool wasLastForEntry);
96
128
  }
97
129
 
98
130
  /// <summary>
@@ -109,8 +141,8 @@ namespace DxMessaging.Core.Internal
109
141
  int MessageTypeIndex { get; }
110
142
 
111
143
  /// <summary>
112
- /// True when the last sweep found no live typed slots or dispatch links
113
- /// worth retaining and the owning <c>MessageCache</c> entry can be
144
+ /// True when the last sweep found no live typed slots worth
145
+ /// retaining and the owning <c>MessageCache</c> entry can be
114
146
  /// removed.
115
147
  /// </summary>
116
148
  bool MarkedForOuterRemoval { get; }
@@ -169,12 +201,6 @@ namespace DxMessaging.Core.Internal
169
201
  /// <c>_slots[<see cref="TypedSlotIndex.Length"/>]</c> array the
170
202
  /// storage owner.
171
203
  /// </para>
172
- /// <para>
173
- /// <see cref="TypedHandler{T}"/> owns a
174
- /// <c>_dispatchLinks[<see cref="TypedDispatchLinkIndex.Length"/>]</c>
175
- /// array as a plain <c>object[]</c> field. It is not a slot type; the
176
- /// legacy named dispatch-link fields were removed.
177
- /// </para>
178
204
  /// </remarks>
179
205
  /// <typeparam name="T">
180
206
  /// The strongly-typed message contract this slot's parent
@@ -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:
@@ -109,23 +109,23 @@ namespace DxMessaging.Core.MessageBus
109
109
 
110
110
  internal static bool ShouldEnableDiagnostics()
111
111
  {
112
- DiagnosticsTarget targets = GlobalDiagnosticsTargets;
112
+ #if UNITY_2021_3_OR_NEWER
113
+ return ShouldEnableDiagnostics(GlobalDiagnosticsTargets, Application.isEditor);
114
+ #else
115
+ return ShouldEnableDiagnostics(GlobalDiagnosticsTargets, isEditor: false);
116
+ #endif
117
+ }
118
+
119
+ internal static bool ShouldEnableDiagnostics(DiagnosticsTarget targets, bool isEditor)
120
+ {
113
121
  if (targets == DiagnosticsTarget.Off)
114
122
  {
115
123
  return false;
116
124
  }
117
125
 
118
- #if UNITY_2021_3_OR_NEWER
119
- if (Application.isEditor)
120
- {
121
- return targets.HasFlagNoAlloc(DiagnosticsTarget.Editor)
122
- || targets.HasFlagNoAlloc(DiagnosticsTarget.Runtime);
123
- }
124
-
125
- return targets.HasFlagNoAlloc(DiagnosticsTarget.Editor);
126
- #else
127
- return targets.HasFlagNoAlloc(DiagnosticsTarget.Runtime);
128
- #endif
126
+ return isEditor
127
+ ? targets.HasFlagNoAlloc(DiagnosticsTarget.Editor)
128
+ : targets.HasFlagNoAlloc(DiagnosticsTarget.Runtime);
129
129
  }
130
130
 
131
131
  /// <summary>
@@ -297,8 +297,11 @@ namespace DxMessaging.Core.MessageBus
297
297
  /// <typeparam name="T">Specific type of UntargetedMessages to register for.</typeparam>
298
298
  /// <param name="messageHandler">MessageHandler to register to accept UntargetedMessages of the specified type.</param>
299
299
  /// <param name="priority">Priority at which to run; lower runs earlier.</param>
300
- /// <returns>The deregistration action. Invoke when the handler no longer wants to receive messages.</returns>
301
- Action RegisterUntargeted<T>(MessageHandler messageHandler, int priority = 0)
300
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving messages.</returns>
301
+ MessageBusRegistration RegisterUntargeted<T>(
302
+ MessageHandler messageHandler,
303
+ int priority = 0
304
+ )
302
305
  where T : IUntargetedMessage;
303
306
 
304
307
  /// <summary>
@@ -308,7 +311,7 @@ namespace DxMessaging.Core.MessageBus
308
311
  /// <param name="target">Target of messages to listen for.</param>
309
312
  /// <param name="messageHandler">MessageHandler to register the TargetedMessages of the specified type.</param>
310
313
  /// <param name="priority">Priority at which to run; lower runs earlier.</param>
311
- /// <returns>The deregistration action. Invoke when the handler no longer wants to receive the messages.</returns>
314
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving the messages.</returns>
312
315
  /// <remarks>
313
316
  /// To preserve frozen dispatch snapshots during in-flight emissions, the per-MessageHandler
314
317
  /// typed-cache for <c>(target, priority)</c> is NOT removed when the last registration at
@@ -319,7 +322,7 @@ namespace DxMessaging.Core.MessageBus
319
322
  /// at every spawned GameObject) should prefer <see cref="RegisterTargetedWithoutTargeting{T}"/>
320
323
  /// or recycle MessageHandlers to avoid unbounded inner-dictionary growth.
321
324
  /// </remarks>
322
- Action RegisterTargeted<T>(
325
+ MessageBusRegistration RegisterTargeted<T>(
323
326
  InstanceId target,
324
327
  MessageHandler messageHandler,
325
328
  int priority = 0
@@ -333,8 +336,11 @@ namespace DxMessaging.Core.MessageBus
333
336
  /// <typeparam name="T">Specific type of TargetedMessages to register for.</typeparam>
334
337
  /// <param name="messageHandler">MessageHandler to register to accept all TargetedMessages of the specified type.</param>
335
338
  /// <param name="priority">Priority at which to run; lower runs earlier.</param>
336
- /// <returns>The deregistration action. Invoke when the handler no longer wants to receive messages.</returns>
337
- Action RegisterTargetedWithoutTargeting<T>(MessageHandler messageHandler, int priority = 0)
339
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving messages.</returns>
340
+ MessageBusRegistration RegisterTargetedWithoutTargeting<T>(
341
+ MessageHandler messageHandler,
342
+ int priority = 0
343
+ )
338
344
  where T : ITargetedMessage;
339
345
 
340
346
  /// <summary>
@@ -344,7 +350,7 @@ namespace DxMessaging.Core.MessageBus
344
350
  /// <param name="source">InstanceId of the source for BroadcastMessages to listen to.</param>
345
351
  /// <param name="messageHandler">MessageHandler to register to accept BroadcastMessages.</param>
346
352
  /// <param name="priority"></param>
347
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to receive messages.</returns>
353
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving messages.</returns>
348
354
  /// <remarks>
349
355
  /// To preserve frozen dispatch snapshots during in-flight emissions, the per-MessageHandler
350
356
  /// typed-cache for <c>(source, priority)</c> is NOT removed when the last registration at
@@ -355,7 +361,7 @@ namespace DxMessaging.Core.MessageBus
355
361
  /// every spawned GameObject) should prefer <see cref="RegisterSourcedBroadcastWithoutSource{T}"/>
356
362
  /// or recycle MessageHandlers to avoid unbounded inner-dictionary growth.
357
363
  /// </remarks>
358
- Action RegisterSourcedBroadcast<T>(
364
+ MessageBusRegistration RegisterSourcedBroadcast<T>(
359
365
  InstanceId source,
360
366
  MessageHandler messageHandler,
361
367
  int priority = 0
@@ -369,8 +375,8 @@ namespace DxMessaging.Core.MessageBus
369
375
  /// <typeparam name="T">Type of the BroadcastMessage to register.</typeparam>
370
376
  /// <param name="messageHandler">MessageHandler to register to accept BroadcastMessages.</param>
371
377
  /// <param name="priority"></param>
372
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to receive messages.</returns>
373
- Action RegisterSourcedBroadcastWithoutSource<T>(
378
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving messages.</returns>
379
+ MessageBusRegistration RegisterSourcedBroadcastWithoutSource<T>(
374
380
  MessageHandler messageHandler,
375
381
  int priority = 0
376
382
  )
@@ -381,8 +387,8 @@ namespace DxMessaging.Core.MessageBus
381
387
  /// It doesn't matter if the message is Targeted or Untargeted, this MessageHandler will be invoked for it.
382
388
  /// </summary>
383
389
  /// <param name="messageHandler">MessageHandler to register to accept all messages.</param>
384
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to receive messages.</returns>
385
- Action RegisterGlobalAcceptAll(MessageHandler messageHandler);
390
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop receiving messages.</returns>
391
+ MessageBusRegistration RegisterGlobalAcceptAll(MessageHandler messageHandler);
386
392
 
387
393
  /// <summary>
388
394
  /// Registers the specified MessageHandler and transformer function as an interceptor for Messages of type T.
@@ -407,8 +413,8 @@ namespace DxMessaging.Core.MessageBus
407
413
  /// Calling the returned deregistration action removes the interceptor from dispatch, but
408
414
  /// the delegate reference may remain until the final deregistration of that interceptor.
409
415
  /// </remarks>
410
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to intercept messages.</returns>
411
- Action RegisterUntargetedInterceptor<T>(
416
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop intercepting messages.</returns>
417
+ MessageBusRegistration RegisterUntargetedInterceptor<T>(
412
418
  UntargetedInterceptor<T> interceptor,
413
419
  int priority = 0
414
420
  )
@@ -437,8 +443,11 @@ namespace DxMessaging.Core.MessageBus
437
443
  /// Calling the returned deregistration action removes the interceptor from dispatch, but
438
444
  /// the delegate reference may remain until the final deregistration of that interceptor.
439
445
  /// </remarks>
440
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to intercept messages.</returns>
441
- Action RegisterTargetedInterceptor<T>(TargetedInterceptor<T> interceptor, int priority = 0)
446
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop intercepting messages.</returns>
447
+ MessageBusRegistration RegisterTargetedInterceptor<T>(
448
+ TargetedInterceptor<T> interceptor,
449
+ int priority = 0
450
+ )
442
451
  where T : ITargetedMessage;
443
452
 
444
453
  /// <summary>
@@ -464,8 +473,8 @@ namespace DxMessaging.Core.MessageBus
464
473
  /// Calling the returned deregistration action removes the interceptor from dispatch, but
465
474
  /// the delegate reference may remain until the final deregistration of that interceptor.
466
475
  /// </remarks>
467
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to intercept messages.</returns>
468
- Action RegisterBroadcastInterceptor<T>(
476
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop intercepting messages.</returns>
477
+ MessageBusRegistration RegisterBroadcastInterceptor<T>(
469
478
  BroadcastInterceptor<T> interceptor,
470
479
  int priority = 0
471
480
  )
@@ -478,8 +487,11 @@ namespace DxMessaging.Core.MessageBus
478
487
  /// <typeparam name="T">Type of UntargetedMessage to post-process.</typeparam>
479
488
  /// <param name="messageHandler">MessageHandler to post-process messages for.</param>
480
489
  /// <param name="priority">Priority of the interceptor to run at. Handlers run in order of priority from low -> high.</param>
481
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to post-process messages.</returns>
482
- Action RegisterUntargetedPostProcessor<T>(MessageHandler messageHandler, int priority = 0)
490
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop post-processing messages.</returns>
491
+ MessageBusRegistration RegisterUntargetedPostProcessor<T>(
492
+ MessageHandler messageHandler,
493
+ int priority = 0
494
+ )
483
495
  where T : IUntargetedMessage;
484
496
 
485
497
  /// <summary>
@@ -490,8 +502,8 @@ namespace DxMessaging.Core.MessageBus
490
502
  /// <param name="target">Target of messages to listen for.</param>
491
503
  /// <param name="messageHandler">MessageHandler to post-process messages for.</param>
492
504
  /// <param name="priority">Priority of the interceptor to run at. Handlers run in order of priority from low -> high.</param>
493
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to post-process messages.</returns>
494
- Action RegisterTargetedPostProcessor<T>(
505
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop post-processing messages.</returns>
506
+ MessageBusRegistration RegisterTargetedPostProcessor<T>(
495
507
  InstanceId target,
496
508
  MessageHandler messageHandler,
497
509
  int priority = 0
@@ -505,8 +517,8 @@ namespace DxMessaging.Core.MessageBus
505
517
  /// <typeparam name="T">Type of TargetedMessage to post-process.</typeparam>
506
518
  /// <param name="messageHandler">MessageHandler to post-process messages for.</param>
507
519
  /// <param name="priority">Priority of the interceptor to run at. Handlers run in order of priority from low -> high.</param>
508
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to post-process messages.</returns>
509
- Action RegisterTargetedWithoutTargetingPostProcessor<T>(
520
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop post-processing messages.</returns>
521
+ MessageBusRegistration RegisterTargetedWithoutTargetingPostProcessor<T>(
510
522
  MessageHandler messageHandler,
511
523
  int priority = 0
512
524
  )
@@ -520,8 +532,8 @@ namespace DxMessaging.Core.MessageBus
520
532
  /// <param name="source">Source of messages to listen for.</param>
521
533
  /// <param name="messageHandler">MessageHandler to post-process messages for.</param>
522
534
  /// <param name="priority">Priority of the interceptor to run at. Handlers run in order of priority from low -> high.</param>
523
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to post-process messages.</returns>
524
- Action RegisterBroadcastPostProcessor<T>(
535
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop post-processing messages.</returns>
536
+ MessageBusRegistration RegisterBroadcastPostProcessor<T>(
525
537
  InstanceId source,
526
538
  MessageHandler messageHandler,
527
539
  int priority = 0
@@ -535,13 +547,36 @@ namespace DxMessaging.Core.MessageBus
535
547
  /// <typeparam name="T">Type of TargetedMessage to post-process.</typeparam>
536
548
  /// <param name="messageHandler">MessageHandler to post-process messages for.</param>
537
549
  /// <param name="priority">Priority of the interceptor to run at. Handlers run in order of priority from low -> high.</param>
538
- /// <returns>The deregistration action. Should be invoked when the handler no longer wants to post-process messages.</returns>
539
- Action RegisterBroadcastWithoutSourcePostProcessor<T>(
550
+ /// <returns>An opaque registration handle. Pass it to <see cref="Deregister{T}"/> with the same T to stop post-processing messages.</returns>
551
+ MessageBusRegistration RegisterBroadcastWithoutSourcePostProcessor<T>(
540
552
  MessageHandler messageHandler,
541
553
  int priority = 0
542
554
  )
543
555
  where T : IBroadcastMessage;
544
556
 
557
+ /// <summary>
558
+ /// Undoes a registration produced by any of the <c>Register*</c> methods.
559
+ /// </summary>
560
+ /// <remarks>
561
+ /// <para>
562
+ /// Pass the <see cref="MessageBusRegistration"/> returned by the matching
563
+ /// <c>Register*</c> call, using the SAME <typeparamref name="T"/> you registered with
564
+ /// (for <see cref="RegisterGlobalAcceptAll"/>, any <c>T : IMessage</c> such as
565
+ /// <see cref="Messages.IMessage"/> is accepted, since the global slot is not type-keyed).
566
+ /// </para>
567
+ /// <para>
568
+ /// Deregistration is idempotent at the handle level only in the sense that a handle that
569
+ /// has outlived a reset/domain-reload, or a <see cref="MessageBusRegistration.None"/> /
570
+ /// foreign handle, is a silent no-op. Calling it twice for the same live registration is a
571
+ /// genuine over-deregistration and is logged (matching the pre-v4 behaviour of invoking
572
+ /// the returned deregistration action twice).
573
+ /// </para>
574
+ /// </remarks>
575
+ /// <typeparam name="T">The message type the registration was created for.</typeparam>
576
+ /// <param name="registration">The handle returned by the originating <c>Register*</c> call.</param>
577
+ void Deregister<T>(in MessageBusRegistration registration)
578
+ where T : IMessage;
579
+
545
580
  /// <summary>
546
581
  /// Broadcasts an Untargeted message to all listeners registered to this bus.
547
582
  /// </summary>
@@ -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:
@@ -13,6 +13,7 @@ namespace DxMessaging.Core.MessageBus
13
13
  /// <param name="options">Build configuration describing ownership, message bus preference, and lifecycle hooks.</param>
14
14
  /// <returns>Lease that exposes the constructed <see cref="MessageRegistrationToken"/>.</returns>
15
15
  /// <exception cref="ArgumentNullException"><paramref name="options"/> is <see langword="null"/>.</exception>
16
+ /// <exception cref="MessageRegistrationBuildException">ActivateOnBuild activation failed and automatic cleanup remains retryable.</exception>
16
17
  MessageRegistrationLease Build(MessageRegistrationBuildOptions options);
17
18
  }
18
19
  }
@@ -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:
@@ -4,6 +4,7 @@ namespace DxMessaging.Core.MessageBus.Internal
4
4
  using System.Diagnostics;
5
5
  using System.Runtime.CompilerServices;
6
6
  using DxMessaging.Core;
7
+ using DxMessaging.Core.Internal;
7
8
  using DxMessaging.Core.Pooling;
8
9
 
9
10
  /// <summary>
@@ -277,7 +278,7 @@ namespace DxMessaging.Core.MessageBus.Internal
277
278
  /// <c>Dictionary&lt;InstanceId, object&gt;</c> -- generic-erased to share a
278
279
  /// single pool across every message-type instantiation. Each value is a
279
280
  /// <see cref="BusSinkSlot"/>, accessed via
280
- /// <see cref="Unsafe.As{T}(object)"/>; the class is sealed and only inserted
281
+ /// <see cref="DxUnsafe.As{T}(object)"/>; the class is sealed and only inserted
281
282
  /// from this type's own methods, so the cast cannot encounter a foreign
282
283
  /// runtime type. <c>DEBUG</c> builds verify the invariant at every
283
284
  /// cast site.
@@ -378,7 +379,7 @@ namespace DxMessaging.Core.MessageBus.Internal
378
379
  return false;
379
380
  }
380
381
  DebugAssertSlot(boxed);
381
- slot = Unsafe.As<BusSinkSlot>(boxed);
382
+ slot = DxUnsafe.As<BusSinkSlot>(boxed);
382
383
  return true;
383
384
  }
384
385
 
@@ -403,7 +404,7 @@ namespace DxMessaging.Core.MessageBus.Internal
403
404
  if (map.TryGetValue(context, out object boxed))
404
405
  {
405
406
  DebugAssertSlot(boxed);
406
- return Unsafe.As<BusSinkSlot>(boxed);
407
+ return DxUnsafe.As<BusSinkSlot>(boxed);
407
408
  }
408
409
  BusSinkSlot slot = new BusSinkSlot();
409
410
  map[context] = slot;
@@ -463,7 +464,7 @@ namespace DxMessaging.Core.MessageBus.Internal
463
464
  continue;
464
465
  }
465
466
  DebugAssertSlot(boxed);
466
- Unsafe.As<BusSinkSlot>(boxed).Clear();
467
+ DxUnsafe.As<BusSinkSlot>(boxed).Clear();
467
468
  }
468
469
  map.Clear();
469
470
  }
@@ -494,7 +495,7 @@ namespace DxMessaging.Core.MessageBus.Internal
494
495
  continue;
495
496
  }
496
497
  DebugAssertSlot(boxed);
497
- Unsafe.As<BusSinkSlot>(boxed).Reset();
498
+ DxUnsafe.As<BusSinkSlot>(boxed).Reset();
498
499
  }
499
500
  // Pool's onRecycled callback clears the dictionary before re-use.
500
501
  DxPools.InstanceIdDicts.Return(map);
@@ -513,7 +514,7 @@ namespace DxMessaging.Core.MessageBus.Internal
513
514
  Debug.Assert(
514
515
  boxed is BusSinkSlot,
515
516
  "BusContextSlot.byContext must only contain BusSinkSlot values; "
516
- + "Unsafe.As<BusSinkSlot> would otherwise produce undefined behavior."
517
+ + "DxUnsafe.As<BusSinkSlot> would otherwise produce undefined behavior."
517
518
  );
518
519
  }
519
520
  }