com.wallstop-studios.dxmessaging 2.2.0 → 3.1.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 (270) hide show
  1. package/CHANGELOG.md +315 -67
  2. package/CHANGELOG.md.meta +7 -7
  3. package/Editor/Analyzers/BaseCallIlInspector.cs +277 -0
  4. package/Editor/Analyzers/BaseCallIlInspector.cs.meta +11 -0
  5. package/Editor/Analyzers/BaseCallLogMessageParser.cs +295 -0
  6. package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +11 -0
  7. package/Editor/Analyzers/BaseCallReportAggregator.cs +308 -0
  8. package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +11 -0
  9. package/Editor/Analyzers/BaseCallTypeScanner.cs +110 -0
  10. package/Editor/Analyzers/BaseCallTypeScanner.cs.meta +11 -0
  11. package/Editor/Analyzers/BaseCallTypeScannerCore.cs +562 -0
  12. package/Editor/Analyzers/BaseCallTypeScannerCore.cs.meta +11 -0
  13. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +1129 -0
  14. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +11 -0
  15. package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +44 -44
  16. package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +44 -44
  17. package/Editor/Analyzers/System.Collections.Immutable.dll.meta +44 -44
  18. package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +44 -44
  19. package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +44 -44
  20. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  21. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +46 -0
  22. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  23. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +72 -72
  24. package/Editor/Analyzers.meta +8 -8
  25. package/Editor/AssemblyInfo.cs.meta +11 -3
  26. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
  27. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
  28. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +429 -0
  29. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
  30. package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
  31. package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +11 -3
  32. package/Editor/CustomEditors.meta +2 -2
  33. package/Editor/DxMessagingEditorIdle.cs +62 -0
  34. package/Editor/DxMessagingEditorIdle.cs.meta +11 -0
  35. package/Editor/DxMessagingEditorInitializer.cs +112 -15
  36. package/Editor/DxMessagingEditorInitializer.cs.meta +11 -3
  37. package/Editor/DxMessagingEditorLog.cs +32 -0
  38. package/Editor/DxMessagingEditorLog.cs.meta +11 -0
  39. package/Editor/DxMessagingMenu.cs.meta +11 -11
  40. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
  41. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +313 -0
  42. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
  43. package/Editor/Settings/DxMessagingSettings.cs +261 -11
  44. package/Editor/Settings/DxMessagingSettings.cs.meta +11 -3
  45. package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
  46. package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +11 -3
  47. package/Editor/Settings.meta +2 -2
  48. package/Editor/SetupCscRsp.cs +406 -39
  49. package/Editor/SetupCscRsp.cs.meta +11 -3
  50. package/Editor/Testing/MessagingComponentEditorHarness.cs +2 -2
  51. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +11 -3
  52. package/Editor/Testing.meta +3 -3
  53. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
  54. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
  55. package/Editor.meta +8 -8
  56. package/LICENSE.md +9 -9
  57. package/LICENSE.md.meta +7 -7
  58. package/README.md +940 -900
  59. package/README.md.meta +7 -7
  60. package/Runtime/AssemblyInfo.cs +4 -0
  61. package/Runtime/AssemblyInfo.cs.meta +11 -3
  62. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +11 -3
  63. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +11 -3
  64. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
  65. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
  66. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
  67. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +11 -3
  68. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +11 -3
  69. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +11 -3
  70. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
  71. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
  72. package/Runtime/Core/Attributes.meta +2 -2
  73. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
  74. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
  75. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
  76. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
  77. package/Runtime/Core/Configuration.meta +9 -0
  78. package/Runtime/Core/DataStructure/CyclicBuffer.cs +46 -28
  79. package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +11 -3
  80. package/Runtime/Core/DataStructure.meta +2 -2
  81. package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +11 -3
  82. package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +11 -3
  83. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +11 -3
  84. package/Runtime/Core/Diagnostics.meta +2 -2
  85. package/Runtime/Core/DxMessagingStaticState.cs +19 -0
  86. package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
  87. package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
  88. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +11 -3
  89. package/Runtime/Core/Extensions/IListExtensions.cs.meta +11 -3
  90. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
  91. package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
  92. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
  93. package/Runtime/Core/Extensions.meta +8 -8
  94. package/Runtime/Core/Helper/MessageCache.cs +32 -0
  95. package/Runtime/Core/Helper/MessageCache.cs.meta +11 -3
  96. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +11 -3
  97. package/Runtime/Core/Helper.meta +2 -2
  98. package/Runtime/Core/IMessage.cs +3 -3
  99. package/Runtime/Core/IMessage.cs.meta +11 -11
  100. package/Runtime/Core/InstanceId.cs +25 -1
  101. package/Runtime/Core/InstanceId.cs.meta +11 -11
  102. package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
  103. package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
  104. package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
  105. package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
  106. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
  107. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
  108. package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
  109. package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
  110. package/Runtime/Core/Internal/TypedSlots.cs +597 -0
  111. package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
  112. package/Runtime/Core/Internal.meta +9 -0
  113. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
  114. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
  115. package/Runtime/Core/MessageBus/IMessageBus.cs +189 -15
  116. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
  117. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
  118. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
  119. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
  120. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
  121. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
  122. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
  123. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
  124. package/Runtime/Core/MessageBus/Internal/BusSlots.cs +719 -0
  125. package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
  126. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
  127. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
  128. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
  129. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
  130. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
  131. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
  132. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
  133. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
  134. package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
  135. package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
  136. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
  137. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
  138. package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
  139. package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
  140. package/Runtime/Core/MessageBus/Internal.meta +9 -0
  141. package/Runtime/Core/MessageBus/MessageBus.cs +5366 -3838
  142. package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
  143. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
  144. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
  145. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
  146. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
  147. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
  148. package/Runtime/Core/MessageBus.meta +8 -8
  149. package/Runtime/Core/MessageHandler.cs +2399 -1042
  150. package/Runtime/Core/MessageHandler.cs.meta +11 -11
  151. package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
  152. package/Runtime/Core/MessageRegistrationToken.cs +429 -44
  153. package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
  154. package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +11 -3
  155. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
  156. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
  157. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
  158. package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +11 -3
  159. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
  160. package/Runtime/Core/Messages/StringMessage.cs.meta +11 -3
  161. package/Runtime/Core/Messages.meta +8 -8
  162. package/Runtime/Core/MessagingDebug.cs.meta +11 -11
  163. package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
  164. package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
  165. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
  166. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
  167. package/Runtime/Core/Pooling/DxPools.cs +157 -0
  168. package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
  169. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
  170. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
  171. package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
  172. package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
  173. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
  174. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
  175. package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
  176. package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
  177. package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
  178. package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
  179. package/Runtime/Core/Pooling.meta +9 -0
  180. package/Runtime/Core.meta +8 -8
  181. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
  182. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
  183. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
  184. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +11 -3
  185. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
  186. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
  187. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
  188. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
  189. package/Runtime/Unity/Integrations/Reflex.meta +8 -8
  190. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +11 -3
  191. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
  192. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
  193. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
  194. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
  195. package/Runtime/Unity/Integrations/VContainer.meta +8 -8
  196. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +11 -3
  197. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
  198. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
  199. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
  200. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
  201. package/Runtime/Unity/Integrations/Zenject.meta +8 -8
  202. package/Runtime/Unity/Integrations.meta +8 -8
  203. package/Runtime/Unity/MessageAwareComponent.cs +74 -0
  204. package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
  205. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
  206. package/Runtime/Unity/MessagingComponent.cs +43 -10
  207. package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
  208. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
  209. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
  210. package/Runtime/Unity.meta +8 -8
  211. package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
  212. package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
  213. package/Runtime.meta +8 -8
  214. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
  215. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
  216. package/Samples~/DI/Prefabs.meta +8 -8
  217. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
  218. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
  219. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
  220. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
  221. package/Samples~/DI/Providers.meta +8 -8
  222. package/Samples~/DI/README.md +51 -51
  223. package/Samples~/DI/README.md.meta +7 -7
  224. package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
  225. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
  226. package/Samples~/DI/Reflex.meta +8 -8
  227. package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
  228. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
  229. package/Samples~/DI/VContainer.meta +8 -8
  230. package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
  231. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
  232. package/Samples~/DI/Zenject.meta +8 -8
  233. package/Samples~/DI.meta +8 -8
  234. package/Samples~/Mini Combat/Boot.cs.meta +11 -11
  235. package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
  236. package/Samples~/Mini Combat/Messages.cs.meta +11 -11
  237. package/Samples~/Mini Combat/Player.cs.meta +11 -11
  238. package/Samples~/Mini Combat/README.md +324 -323
  239. package/Samples~/Mini Combat/README.md.meta +7 -7
  240. package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
  241. package/Samples~/Mini Combat/Walkthrough.md +430 -430
  242. package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
  243. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
  244. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
  245. package/Samples~/Mini Combat.meta +8 -8
  246. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
  247. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
  248. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
  249. package/Samples~/UI Buttons + Inspector/README.md +210 -209
  250. package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
  251. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
  252. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
  253. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
  254. package/Samples~/UI Buttons + Inspector.meta +8 -8
  255. package/SourceGenerators/Directory.Build.props +50 -3
  256. package/SourceGenerators/Directory.Build.props.meta +7 -7
  257. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
  258. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
  259. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
  260. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +11 -3
  261. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +39 -46
  262. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
  263. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
  264. package/SourceGenerators/global.json +7 -0
  265. package/SourceGenerators/global.json.meta +7 -0
  266. package/SourceGenerators.meta +8 -8
  267. package/Third Party Notices.md +3 -3
  268. package/Third Party Notices.md.meta +7 -7
  269. package/package.json +102 -92
  270. package/package.json.meta +7 -7
package/README.md CHANGED
@@ -1,900 +1,940 @@
1
- # DxMessaging for Unity
2
-
3
- <p align="center">
4
- <img src="docs/images/DxMessaging-banner.svg" alt="DxMessaging - Type-safe messaging system for Unity" width="800"/>
5
- </p>
6
-
7
- <p align="center">
8
- <a href="https://wallstop.github.io/DxMessaging/">
9
- <img src="https://img.shields.io/badge/📖_Full_Documentation-Visit_the_Docs_Site-2ea44f?style=for-the-badge" alt="Full Documentation" />
10
- </a>
11
- </p>
12
-
13
- [![Unity](https://img.shields.io/badge/Unity-2021.3+-black.svg)](https://unity.com/releases/editor)<br/>
14
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)<br/>
15
- [![openupm](https://img.shields.io/npm/v/com.wallstop-studios.dxmessaging?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.wallstop-studios.dxmessaging/)<br/>
16
- [![Version](https://img.shields.io/npm/v/com.wallstop-studios.dxmessaging.svg)](https://www.npmjs.com/package/com.wallstop-studios.dxmessaging)<br/>
17
- [![Performance: OS-Specific Benchmarks](https://img.shields.io/badge/Performance-OS--specific-blueviolet.svg)](docs/architecture/performance.md)<br/>
18
- [![Markdown Link Validity](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-validity.yml/badge.svg)](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-validity.yml)<br/>
19
- [![Markdown Link Text Check](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-text-check.yml/badge.svg)](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-text-check.yml)
20
-
21
- > **🤖 AI Assistance Disclosure:**
22
- >
23
- > This project has been actively developed and maintained by human authors for over a decade. Recent versions have utilized AI assistance for documentation, test coverage, and performance optimizations.
24
-
25
- **DxMessaging is a type-safe messaging system** that replaces sprawling C# events, brittle UnityEvents, and global static event buses with an observable and lifecycle-managed communication pattern.
26
-
27
- Think of it as: A messaging system designed for decoupled game systems.
28
-
29
- Need install instructions? Try [OpenUPM](https://openupm.com/packages/com.wallstop-studios.dxmessaging/) (recommended) or see the [Install Guide](docs/getting-started/install.md) for Git URLs, scoped registries, and more.
30
-
31
- ## Table of Contents
32
-
33
- - [30-Second Elevator Pitch](#30-second-elevator-pitch)
34
- - [Mental Model: How to Think About DxMessaging](#mental-model-how-to-think-about-dxmessaging)
35
- - [Quick Start (5 Minutes)](#quick-start-5-minutes)
36
- - [Dependency Injection (DI) Compatible](#-dependency-injection-di-compatible)
37
- - [Is DxMessaging Right for You?](#is-dxmessaging-right-for-you)
38
- - [Why DxMessaging](#why-dxmessaging)
39
- - [Key Features](#key-features)
40
- - [The DxMessaging Solution](#the-dxmessaging-solution)
41
- - [Real-World Examples](#real-world-examples)
42
- - [Performance](#performance)
43
- - [Comparison Table](#comparison-table)
44
- - [Samples](#samples)
45
- - [Documentation](#documentation)
46
- - [Requirements](#requirements)
47
- - [Contributing](#contributing)
48
- - [Links](#links)
49
-
50
- ---
51
-
52
- ## 30-Second Elevator Pitch
53
-
54
- ### If you've ever
55
-
56
- - Forgotten to unsubscribe from an event and spent hours debugging memory leaks
57
- - Had UI code tangled with 15 different game systems
58
- - Wondered "which event fired when?" with no way to see message flow
59
- - Copy-pasted event boilerplate dozens of times
60
-
61
- #### Then DxMessaging can help address these challenges
62
-
63
- - **Automatic lifecycle management** - manages subscription lifecycle automatically, no manual unsubscribe
64
- - **Zero coupling** - systems communicate without knowing each other exist
65
- - **Full visibility** - see every message in the Inspector with timestamps and payloads
66
- - **Complete control** - priority-based ordering, validation, and interception
67
-
68
- ##### Three simple message types
69
-
70
- 1. **Untargeted** - Global announcements
71
- 1. **Targeted** - Commands to specific entities
72
- 1. **Broadcast** - Observable facts from a source
73
-
74
- See [Mental Model](#mental-model-how-to-think-about-dxmessaging) for how to choose the right type.
75
-
76
- **One line:** It's a type-safe messaging system with automatic lifecycle management and built-in inspection tools.
77
-
78
- ---
79
-
80
- ## Mental Model: How to Think About DxMessaging
81
-
82
- ### The Core Idea
83
-
84
- DxMessaging is built around one principle: **it gets out of your way**.
85
-
86
- You have data. You need to pass it around. That's the problem. DxMessaging provides fast, simple primitives as building blocks. You model changes as message types with optional context, using game primitives (GameObjects, components) as that context.
87
-
88
- **You don't build your game INTO the messaging system.** It's opt-in and optionala tool you reach for when it helps.
89
-
90
- ### The Three Message Types: Real-World Analogies
91
-
92
- > 💡 _Diagrams below require Mermaid support. If they don't render, try viewing this file directly on [GitHub](https://github.com/wallstop/DxMessaging)._
93
-
94
- Each message type maps to a real-world communication pattern:
95
-
96
- #### 1. Untargeted = PA System 📢
97
-
98
- ```mermaid
99
- flowchart LR
100
- S[Someone] -->|announces| PA[📢 PA System]
101
- PA --> L1[Listener A]
102
- PA --> L2[Listener B]
103
- PA --> L3[Listener C]
104
- ```
105
-
106
- Announcements with no specific recipient. Everyone who cares can hear it.
107
-
108
- **Examples:** "The game is paused", "Settings changed", "Scene finished loading"
109
-
110
- #### 2. Targeted = Addressed Letter 📬
111
-
112
- ```mermaid
113
- flowchart LR
114
- S[Sender] -->|"To: Player"| Letter[📬 Message Bus]
115
- Letter --> Player[Player receives]
116
- Other1[Enemy A] -.->|ignores| Letter
117
- Other2[Enemy B] -.->|ignores| Letter
118
- ```
119
-
120
- Commands to a specific recipient. Only that entity receives them.
121
-
122
- **Examples:** "Player, heal for 10 HP", "Door #7, open", "This enemy, take 25 damage"
123
-
124
- #### 3. Broadcast = Radio Station 📻
125
-
126
- ```mermaid
127
- flowchart LR
128
- Source[Enemy] -->|"I took damage!"| Radio[📻 Message Bus]
129
- Radio --> L1[Damage Numbers UI]
130
- Radio --> L2[Achievement Tracker]
131
- Radio --> L3[Analytics]
132
- Radio --> L4[Combat Log]
133
- ```
134
-
135
- Facts emitted by a specific source. No intended recipientjust an origin. Anyone can tune in.
136
-
137
- **Examples:** "This enemy took 25 damage", "The player picked up item X", "This chest opened"
138
-
139
- ### Choosing the Right Message Type
140
-
141
- ```mermaid
142
- flowchart TD
143
- Start([I need to send a message])
144
- Start --> Q1{Does it matter<br/>WHO sent it?}
145
-
146
- Q1 -->|No| Q2{Does it matter<br/>WHO receives it?}
147
- Q2 -->|No| Untargeted[Use UNTARGETED<br/>Global announcement]
148
- Q2 -->|Yes| Targeted[Use TARGETED<br/>Directed command]
149
-
150
- Q1 -->|Yes| Q3{Am I commanding<br/>someone to act?}
151
- Q3 -->|Yes| Targeted
152
- Q3 -->|No| Broadcast[Use BROADCAST<br/>Observable fact]
153
- ```
154
-
155
- | Question | Untargeted | Targeted | Broadcast |
156
- | ----------------------------------- | :--------: | :------: | :-------: |
157
- | Has a specific sender that matters? | | | |
158
- | Has a specific recipient? | | | |
159
- | Is it a command? | | | |
160
- | Is it an observable fact? | Maybe | | |
161
- | Is it a global announcement? | | | |
162
-
163
- > ⚠️ **Common Mistakes:**
164
- >
165
- > - **Forgetting to enable the token** Messages won't be received. Use `MessageAwareComponent` (auto-enables) or call `Token.Enable()` manually.
166
- > - **Targeting Component when you meant GameObject** These are distinct registration paths. Component-targeted messages won't reach GameObject-level handlers.
167
- > - **Using Broadcast when you need Targeted** Broadcasts have no recipient, just an origin. Use Targeted when commanding a specific entity.
168
- > - **Missing `[Dx*Message]` attribute** The source generator won't process the struct without the marker attribute.
169
- >
170
- > 📖 See [Troubleshooting](docs/reference/troubleshooting.md) for solutions to these and other issues.
171
-
172
- 📖 **Want more depth?** See the full [Mental Model documentation](docs/concepts/mental-model.md) for detailed examples, lifecycle patterns, and edge cases.
173
-
174
- 📖 **Ready to code?** Jump to [Quick Start](#quick-start-5-minutes) to send your first message!
175
-
176
- ---
177
-
178
- ## Quick Start (5 Minutes)
179
-
180
- **New to messaging?** Start with the [Visual Guide](docs/getting-started/visual-guide.md) (5 min) for a beginner-friendly introduction!
181
-
182
- ### 1. Install
183
-
184
- #### Via OpenUPM (Recommended)
185
-
186
- ```bash
187
- openupm add com.wallstop-studios.dxmessaging
188
- ```
189
-
190
- #### Or via Git URL
191
-
192
- ```bash
193
- # Unity Package Manager > Add package from git URL...
194
- https://github.com/wallstop/DxMessaging.git
195
- ```
196
-
197
- See the [Install Guide](docs/getting-started/install.md) for all options including NPM scoped registries and local tarballs.
198
-
199
- ### 2. Define Your First Message
200
-
201
- ```csharp
202
- using DxMessaging.Core.Attributes;
203
-
204
- [DxTargetedMessage]
205
- [DxAutoConstructor] // Auto-generates constructor
206
- public readonly partial struct OpenChest {
207
- public readonly int chestId;
208
- [DxOptionalParameter(true)] // Optional with custom default
209
- public readonly bool playSound;
210
- }
211
- ```
212
-
213
- ### 3. Listen for It
214
-
215
- ```csharp
216
- using DxMessaging.Unity;
217
-
218
- public class ChestController : MessageAwareComponent {
219
- protected override void RegisterMessageHandlers() {
220
- base.RegisterMessageHandlers();
221
- _ = Token.RegisterComponentTargeted<OpenChest>(this, OnOpen);
222
- }
223
-
224
- void OnOpen(ref OpenChest msg) {
225
- Debug.Log($"Opening chest {msg.chestId}");
226
- }
227
- }
228
- ```
229
-
230
- ### 4. Send It
231
-
232
- ```csharp
233
- // From anywhere:
234
- var msg = new OpenChest(chestId: 42);
235
- msg.EmitComponentTargeted(chestComponent);
236
- ```
237
-
238
- No manual unsubscribe needed. Subscriptions are type-safe and lifecycle-managed.
239
-
240
- **Stuck?** See [Troubleshooting](docs/reference/troubleshooting.md) or [FAQ](docs/reference/faq.md)
241
-
242
- ---
243
-
244
- ## 🔧 Dependency Injection (DI) Compatible
245
-
246
- **Using Zenject, VContainer, or Reflex?** DxMessaging is fully DI-compatible out of the box!
247
-
248
- ```csharp
249
- // Inject IMessageBus in any class
250
- public class PlayerService : IInitializable, IDisposable
251
- {
252
- private readonly MessageRegistrationLease _lease;
253
-
254
- public PlayerService(IMessageRegistrationBuilder builder)
255
- {
256
- // Builder automatically resolves your container-managed bus
257
- _lease = builder.Build(new MessageRegistrationBuildOptions
258
- {
259
- Configure = token => token.RegisterUntargeted<PlayerSpawned>(OnSpawn)
260
- });
261
- }
262
-
263
- public void Initialize() => _lease.Activate();
264
- public void Dispose() => _lease.Dispose();
265
- }
266
- ```
267
-
268
- ### Why use DI + Messaging?
269
-
270
- - **DI for construction** Inject services, repositories, managers via constructors
271
- - **Messaging for events** — Reactive, decoupled communication for gameplay events
272
- - **Combined approach** Clean architecture with testable, isolated buses
273
-
274
- #### Automatic integration for
275
-
276
- - **Zenject/Extenject** — Full-featured DI with extensive Unity support
277
- - ✅ **VContainer** — Lightweight, high-performance DI with scoped lifetimes
278
- - **Reflex** Minimal API, high-performance dependency injection
279
-
280
- ##### Get started
281
-
282
- - [Zenject Integration Guide](docs/integrations/zenject.md) — Complete setup with examples
283
- - [VContainer Integration Guide](docs/integrations/vcontainer.md) — Scoped buses for scene isolation
284
- - [Reflex Integration Guide](docs/integrations/reflex.md) Minimal, lightweight patterns
285
-
286
- ##### Core DI concepts
287
-
288
- - [Runtime Configuration](docs/advanced/runtime-configuration.md) — Setting message buses at runtime, re-binding registrations
289
- - [Message Bus Providers](docs/advanced/message-bus-providers.md) — Provider system for design-time and runtime bus configuration
290
-
291
- **Not using DI?** No problem. DxMessaging works standalone with zero dependencies.
292
-
293
- ---
294
-
295
- ## Is DxMessaging Right for You?
296
-
297
- ### Use DxMessaging When
298
-
299
- - **You have cross-system communication** - UI needs to react to gameplay, achievements track events, analytics observe everything
300
- - **You're building for scale** - 10+ systems that need to communicate, or growing from prototype to production
301
- - **Memory leaks are a concern** - You've been bitten by forgotten event unsubscribes before
302
- - **You value observability** - Need to debug "what fired when?" or track message flow
303
- - **Teams/long-term maintenance** - Multiple developers, or you'll maintain this code for years
304
- - **You want decoupling** - When UI classes need references to many game systems
305
- - **You're using DI frameworks** - Compatible with Zenject/VContainer/Reflex (see [DI Compatible](#-dependency-injection-di-compatible))
306
-
307
- ### ❌ Don't Use DxMessaging When
308
-
309
- - **Tiny prototypes/game jams** - If your game is <1000 lines and will be done in a week, C# events are fine
310
- - **Simple, local communication** - A single button calling a single method? Just use UnityEvents or direct references
311
- - **Performance is THE constraint** - Building a physics engine or ECS with millions of events/frame? Raw delegates are faster
312
- - **Team is unfamiliar** - Learning curve exists; if the team isn't on board, it won't be used correctly
313
- - **You need synchronous return values** - DxMessaging is fire-and-forget; if you need bidirectional request/response, consider other patterns
314
-
315
- ### ⚠️ Maybe Use DxMessaging (Start Small)
316
-
317
- - **Existing large codebase** - Migrate incrementally: start with new features, refactor old code gradually (see [Migration Guide](docs/guides/migration-guide.md))
318
- - **Small team learning** - Try it for one system (e.g., achievements) before going all-in
319
- - **Mid-size projects (5-20k lines)** - Evaluate after trying it for one complex interaction (e.g., combat or scene transitions)
320
-
321
- ### Decision Flow
322
-
323
- ```mermaid
324
- flowchart TD
325
- Q1{Does your project have 3+<br/>systems that need to talk to each other?}
326
- Q1 -->|NO| A1[Stick with C# events or direct references]
327
- Q1 -->|YES| Q2
328
-
329
- Q2{Are you okay with a small<br/>upfront learning investment?}
330
- Q2 -->|NO| A2[Stick with what you know]
331
- Q2 -->|YES| Q3
332
-
333
- Q3{Do you need observable, decoupled,<br/>lifecycle-safe messaging?}
334
- Q3 -->|YES| A3["✅ Use DxMessaging"]
335
- Q3 -->|NO| A4["❌ Keep it simple"]
336
- ```
337
-
338
- **Rule of thumb:** If you're reading this README and thinking "this could address several challenges I'm facing," then DxMessaging may be a good fit. If you're thinking "this seems complicated," start with the [Visual Guide](docs/getting-started/visual-guide.md) or stick with simpler patterns.
339
-
340
- Looking for hard numbers? See OS-specific [Performance Benchmarks](docs/architecture/performance.md).
341
-
342
- ## Why DxMessaging
343
-
344
- ### The Problems You've Probably Hit
345
-
346
- #### Scenario 1: The Memory Leak Nightmare
347
-
348
- You write this innocent-looking code:
349
-
350
- ```csharp
351
- public class GameUI : MonoBehaviour {
352
- void OnEnable() {
353
- GameManager.Instance.OnScoreChanged += UpdateScore;
354
- }
355
- // Oops, forgot OnDisable... leak! 💀
356
- }
357
- ```
358
-
359
- Months later: "Why is our game using 2GB of RAM after an hour?"
360
-
361
- #### Scenario 2: The Spaghetti Mess
362
-
363
- ```csharp
364
- public class GameUI : MonoBehaviour {
365
- [SerializeField] private Player player;
366
- [SerializeField] private EnemySpawner spawner;
367
- [SerializeField] private InventorySystem inventory;
368
- [SerializeField] private QuestSystem quests;
369
- [SerializeField] private AudioManager audio;
370
- // ... 15 more SerializeFields ...
371
-
372
- void Awake() {
373
- player.OnHealthChanged += UpdateHealth;
374
- spawner.OnWaveStart += ShowWave;
375
- inventory.OnItemAdded += RefreshInventory;
376
- quests.OnQuestCompleted += ShowQuestNotification;
377
- // ... 20 more subscriptions ...
378
- }
379
- }
380
- ```
381
-
382
- **Your UI now depends on many systems.** Refactoring becomes more difficult.
383
-
384
- #### Scenario 3: The Debugging Black Hole
385
-
386
- Player reports: "My health bar didn't update!"
387
-
388
- You think: "Okay, which of the 47 events touching health failed? And in what order?"
389
-
390
- **30 minutes later:** Still setting breakpoints everywhere...
391
-
392
- ### Common Challenges
393
-
394
- Some developers encounter these challenges when working with traditional event systems:
395
-
396
- - **Memory leaks** from forgotten unsubscribes
397
- - **Tight coupling** making refactoring difficult
398
- - **No execution order control** leading to unpredictable behavior
399
- - **Limited debugging visibility** for tracking message flow
400
- - **Boilerplate code** when managing many event subscriptions
401
-
402
- ### The DxMessaging Solution
403
-
404
- #### Same scenarios, simplified handling
405
-
406
- ##### Scenario 1: No More Memory Leaks
407
-
408
- ```csharp
409
- public class GameUI : MessageAwareComponent {
410
- protected override void RegisterMessageHandlers() {
411
- base.RegisterMessageHandlers();
412
- _ = Token.RegisterUntargeted<ScoreChanged>(UpdateScore);
413
- }
414
- // No manual cleanup needed.
415
- // Token automatically handles OnEnable/OnDisable/OnDestroy
416
- }
417
- ```
418
-
419
- ###### Automatic lifecycle = leaks are prevented by default
420
-
421
- ##### Scenario 2: No More Coupling
422
-
423
- ```csharp
424
- public class GameUI : MessageAwareComponent {
425
- // Zero SerializeFields! Zero references!
426
-
427
- protected override void RegisterMessageHandlers() {
428
- base.RegisterMessageHandlers();
429
- _ = Token.RegisterUntargeted<HealthChanged>(OnHealth);
430
- _ = Token.RegisterUntargeted<WaveStarted>(OnWave);
431
- _ = Token.RegisterUntargeted<ItemAdded>(OnItem);
432
- // Listen to anything, from anywhere, no coupling
433
- }
434
- }
435
- ```
436
-
437
- **Your UI is now independent.** Swapping systems no longer requires updating UI references.
438
-
439
- ##### Scenario 3: Debugging is Built In
440
-
441
- Open any `MessageAwareComponent` in the Inspector:
442
-
443
- ```text
444
- Message History (last 50):
445
- [12:34:56] HealthChanged (amount: 25) → Priority: 0
446
- [12:34:55] ItemAdded (id: 42, count: 1) → Priority: 5
447
- [12:34:54] WaveStarted (wave: 3) Priority: 0
448
-
449
- Active Registrations:
450
- ✓ HealthChanged (5 handlers)
451
- ItemAdded (2 handlers)
452
- ```
453
-
454
- **See exactly what fired, when, and who handled it.** No guesswork.
455
-
456
- ### How It Transforms Your Code
457
-
458
- ```csharp
459
- // 1. Define messages (typed, discoverable)
460
- [DxTargetedMessage]
461
- [DxAutoConstructor]
462
- public readonly partial struct Heal { public readonly int amount; }
463
-
464
- // 2. Listen (automatic lifecycle - prevents leaks)
465
- public class Player : MessageAwareComponent {
466
- protected override void RegisterMessageHandlers() {
467
- base.RegisterMessageHandlers();
468
- _ = Token.RegisterComponentTargeted<Heal>(this, OnHeal);
469
- }
470
-
471
- void OnHeal(ref Heal m) {
472
- health += m.amount;
473
- Debug.Log($"Healed {m.amount}!");
474
- }
475
- }
476
-
477
- // 3. Send (from anywhere - zero coupling)
478
- var heal = new Heal(50);
479
- heal.EmitComponentTargeted(playerComponent);
480
- ```
481
-
482
- #### What you get
483
-
484
- - **Automatic cleanup** - tokens clean up when components are destroyed
485
- - ✅ **Zero coupling** - no SerializeFields, no GetComponent, no direct references
486
- - **Full visibility** - see message flow in Inspector with timestamps and payloads
487
- - **Predictable order** - priority-based execution (no more mystery race conditions)
488
- - **Type-safe** - compile-time guarantees, refactor with confidence
489
- - **Intercept & validate** - enforce rules before handlers run (clamp damage, block invalid input)
490
- - **Extension points everywhere** - interceptors, handlers, post-processors with priorities
491
-
492
- ## Key Features
493
-
494
- What DxMessaging offers:
495
-
496
- ### Performance: Zero-Allocation Design
497
-
498
- **The problem with normal events:** Boxing allocations, GC spikes, memory leaks from forgotten unsubscribes.
499
-
500
- #### DxMessaging solution
501
-
502
- ```csharp
503
- void OnDamage(ref TookDamage msg) { // Pass by ref = zero allocations
504
- health -= msg.amount; // No boxing, no GC pressure
505
- }
506
- // Automatic cleanup prevents common leak patterns
507
- ```
508
-
509
- **Note:** Struct messages passed by ref avoid GC allocations, which is standard behavior for value types in C#.
510
-
511
- ### Three Message Types
512
-
513
- Most event systems force you into one pattern. DxMessaging gives you the right tool for each job:
514
-
515
- ```csharp
516
- // Untargeted: "Everyone, listen up!" (global announcements)
517
- [DxUntargetedMessage]
518
- public struct GamePaused { }
519
- // ↳ Perfect for: settings, scene transitions, global state
520
-
521
- // Targeted: "Hey Player, do this!" (commands to specific entities)
522
- [DxTargetedMessage]
523
- public struct Heal { public int amount; }
524
- // ↳ Perfect for: UI actions, direct commands, player input
525
-
526
- // Broadcast: "I took damage!" (events others can observe)
527
- [DxBroadcastMessage]
528
- public struct TookDamage { public int amount; }
529
- // ↳ Perfect for: achievements, analytics, UI updates from entities
530
- ```
531
-
532
- **Why this matters:** You're not forcing everything through one generic "Event<T>" pattern. Each message type has clear semantics.
533
-
534
- ### The Message Pipeline
535
-
536
- Every message flows through 3 stages with priority control:
537
-
538
- ```mermaid
539
- flowchart LR
540
- P[Producer] --> I[Interceptors<br/>validate/mutate]
541
- I --> H[Handlers<br/>main logic]
542
- H --> PP[Post-Processors<br/>analytics/logging]
543
- ```
544
-
545
- ### Global Observers: Listen to All Events
546
-
547
- **The problem with normal events:** To track all player damage, enemy damage, and NPC damage, you need 3 separate event subscriptions.
548
-
549
- **DxMessaging approach:** Subscribe once to a message type, receive all instances with source information:
550
-
551
- ```csharp
552
- // Track ALL damage from ANY source (players, enemies, NPCs, environment)
553
- _ = token.RegisterBroadcastWithoutSource<TookDamage>(
554
- (InstanceId source, TookDamage msg) => {
555
- Debug.Log($"{source} took {msg.amount} damage!");
556
- Analytics.LogDamage(source, msg.amount);
557
- CheckAchievements(source, msg.amount);
558
- }
559
- );
560
- ```
561
-
562
- #### Real-world use cases
563
-
564
- - **Achievement system:** Track all kills, deaths, damage across the entire game
565
- - **Combat log:** "Player took 25 damage, Enemy3 took 50 damage, Boss took 100 damage"
566
- - **Analytics:** Aggregate stats from all entities without knowing about them upfront
567
- - **Debug tools:** Watch ALL messages in the Inspector without instrumenting code
568
-
569
- **How this differs:** Some event bus patterns require subscribing to each entity type separately. DxMessaging allows observing all instances of a message type in one registration.
570
-
571
- ### Interceptors: Validate Before Execution
572
-
573
- **The problem with normal events:** Validation logic duplicated in every handler, or bugs when you forget.
574
-
575
- **DxMessaging solution:** Validate ONCE before ANY handler runs:
576
-
577
- ```csharp
578
- // ONE interceptor protects ALL handlers
579
- _ = token.RegisterBroadcastInterceptor<TookDamage>(
580
- (ref InstanceId src, ref TookDamage msg) => {
581
- if (msg.amount <= 0) return false; // Cancel invalid
582
- if (msg.amount > 999) {
583
- msg = new TookDamage(999); // Clamp excessive
584
- }
585
- if (IsGodModeActive(src)) return false; // Block damage
586
- return true;
587
- },
588
- priority: -100 // Run FIRST
589
- );
590
-
591
- // Now ALL handlers receive clean, validated data
592
- _ = token.RegisterComponentTargeted<TookDamage>(player, OnDamage);
593
- void OnDamage(ref TookDamage msg) {
594
- // No validation needed - interceptor guarantees validity!
595
- health -= msg.amount;
596
- }
597
- ```
598
-
599
- #### Real-world use cases
600
-
601
- - Clamp/normalize values (damage, healing, speeds)
602
- - Enforce game rules ("can't heal above max health")
603
- - Block messages during cutscenes
604
- - Log/audit sensitive actions
605
-
606
- ### Built-in Inspector Diagnostics
607
-
608
- **The problem with normal events:** "Which event fired? When? Who handled it? In what order?" = 🤷
609
-
610
- **DxMessaging solution:** Click any `MessageAwareComponent` in the Inspector:
611
-
612
- #### Message History (last 50)
613
-
614
- - `[12:34:56.123] HealthChanged`
615
- - amount: 25
616
- - priority: 0
617
- - handlers: 3
618
- - `[12:34:55.987] ItemAdded`
619
- - itemId: 42, count: 1
620
- - priority: 5
621
- - handlers: 2
622
-
623
- ##### Active Registrations
624
-
625
- - HealthChanged (priority: 0, called: 847 times)
626
- - ✓ ItemAdded (priority: 5, called: 23 times)
627
- - TookDamage (priority: 10, called: 1,203 times)
628
-
629
- #### Real-world debugging scenarios
630
-
631
- - "Did my message fire?" → Check history, see timestamp
632
- - "Why didn't my handler run?" → Check registrations, see if it's active
633
- - "What's firing too often?" Sort by call count
634
- - "What's the execution order?" Sort by priority
635
-
636
- **No more:** Setting 50 breakpoints and stepping through code for 30 minutes.
637
-
638
- ### Local Bus Islands for Testing
639
-
640
- **The problem with normal events:** Global static events contaminate tests. Mock complexity. Flaky tests.
641
-
642
- **DxMessaging solution:** Each test gets its own isolated message bus:
643
-
644
- ```csharp
645
- [Test]
646
- public void TestAchievementSystem() {
647
- // Create isolated bus - zero global state
648
- var testBus = new MessageBus();
649
- var handler = new MessageHandler(new InstanceId(1), testBus) { active = true };
650
- var token = MessageRegistrationToken.Create(handler, testBus);
651
-
652
- // Test in isolation
653
- _ = token.RegisterBroadcastWithoutSource<EnemyKilled>(achievements.OnKill);
654
-
655
- var msg = new EnemyKilled("Boss");
656
- msg.EmitGameObjectBroadcast(enemy, testBus); // Only this test sees it
657
-
658
- Assert.IsTrue(achievements.Unlocked("BossSlayer"));
659
- }
660
- // Bus destroyed, zero cleanup needed
661
- ```
662
-
663
- #### Why this matters
664
-
665
- - Tests don't interfere with each other
666
- - No "arrange/act/cleanup" boilerplate
667
- - No mocking frameworks needed
668
- - Parallel test execution is supported
669
-
670
- ## Documentation
671
-
672
- - **[📖 Documentation Site](https://wallstop.github.io/DxMessaging/)** - Full searchable documentation
673
- - **[📚 Wiki](https://github.com/wallstop/DxMessaging/wiki)** - Quick reference wiki
674
- - **[📋 Changelog](CHANGELOG.md)** - Version history
675
-
676
- ### 🎓 Learn
677
-
678
- - **New here?** Start with [Getting Started Guide](docs/getting-started/getting-started.md) (10 min read)
679
- - **Want patterns?** See [Common Patterns](docs/guides/patterns.md)
680
- - **Deep dive?** Read [Design & Architecture](docs/architecture/design-and-architecture.md)
681
-
682
- ### 📚 Core Concepts
683
-
684
- - [Overview](docs/getting-started/overview.md) — What and why
685
- - [Quick Start](docs/getting-started/quick-start.md) — First message in 5 minutes
686
- - [Message Types](docs/concepts/message-types.md) When to use Untargeted/Targeted/Broadcast
687
- - [Interceptors & Ordering](docs/concepts/interceptors-and-ordering.md) Control execution flow
688
- - [Listening Patterns](docs/concepts/listening-patterns.md) All the ways to receive messages
689
-
690
- ### 🔧 Unity Integration
691
-
692
- - [Unity Integration](docs/guides/unity-integration.md) — MessagingComponent deep dive
693
- - [Targeting & Context](docs/concepts/targeting-and-context.md) — GameObject vs Component
694
- - [Diagnostics](docs/guides/diagnostics.md) Inspector tools and debugging
695
-
696
- Important: Inheritance with MessageAwareComponent
697
-
698
- - If you override lifecycle or registration hooks, call the base method.
699
- - Use `base.RegisterMessageHandlers()` to keep default string‑message registrations.
700
- - Use `base.OnEnable()` / `base.OnDisable()` to preserve token enable/disable.
701
- - If you need to opt out of string demos, override `RegisterForStringMessages => false` instead of skipping the base call.
702
- - Don’t hide Unity methods with `new` (e.g., `new void OnEnable()`); always `override` and call `base.*`.
703
-
704
- ### 🧩 DI Framework Integrations
705
-
706
- DxMessaging works standalone (zero dependencies) or with any major DI framework. For detailed setup guides and code examples:
707
-
708
- - **[Zenject Integration Guide](docs/integrations/zenject.md)** — Full-featured DI with extensive Unity support
709
- - **[VContainer Integration Guide](docs/integrations/vcontainer.md)** Lightweight DI with scoped lifetimes for scene isolation
710
- - **[Reflex Integration Guide](docs/integrations/reflex.md)** — Minimal API, high-performance DI
711
-
712
- #### Core DI concepts
713
-
714
- - **[Runtime Configuration](docs/advanced/runtime-configuration.md)** — Setting and overriding message buses at runtime, re-binding registrations
715
- - **[Message Bus Providers](docs/advanced/message-bus-providers.md)** — Provider system and MessageBusProviderHandle for flexible bus configuration
716
-
717
- Each guide includes:
718
-
719
- - ✅ Complete setup instructions with installers
720
- - Multiple usage patterns (plain classes, MonoBehaviours, direct injection)
721
- - ✅ Testing examples with isolated buses
722
- - Advanced patterns (pooling, scene scopes, signal bridges)
723
-
724
- See the [🔧 DI Compatible section](#-dependency-injection-di-compatible) above for a quick overview.
725
-
726
- ### 🆚 Comparisons
727
-
728
- - [Compare with Other Unity Messaging Frameworks](docs/architecture/comparisons.md) — In-depth comparison with UniRx, MessagePipe, Zenject Signals, C# events, UnityEvents, and more
729
- - [Scriptable Object Architecture (SOA) Compatibility](docs/guides/patterns.md#14-compatibility-with-scriptable-object-architecture-soa) — Migration patterns and interoperability with SOA
730
-
731
- #### Quick Framework Comparison
732
-
733
- | Framework | Best For | Key Strength | Unity Support | Learning Curve |
734
- | ------------------- | --------------------------------- | -------------------------------- | ------------------ | -------------- |
735
- | **DxMessaging** | Unity pub/sub with lifecycle mgmt | Inspector debugging + control | ✅ Built for Unity | ⭐⭐⭐ |
736
- | **UniRx** | Complex event stream transforms | Reactive operators (LINQ) | Built for Unity | ⭐⭐ |
737
- | **MessagePipe** | High-performance DI messaging | Highest throughput (97M ops/sec) | Built for Unity | ⭐⭐⭐⭐ |
738
- | **Zenject Signals** | DI-integrated messaging | Zenject ecosystem | Built for Unity | ⭐⭐ |
739
- | **C# Events** | Simple, local communication | Minimal overhead | Native C# | ⭐⭐⭐⭐⭐ |
740
-
741
- ##### Choose DxMessaging when you want
742
-
743
- - Unity-first design with GameObject/Component targeting
744
- - Automatic lifecycle management (prevents common memory leaks)
745
- - Inspector debugging to see message flow and history
746
- - Execution order control (priority-based handlers)
747
- - Message validation/interception pipeline
748
- - Global observers (listen to all message instances)
749
- - Post-processing stage (analytics, logging after handlers)
750
- - No dependencies, plug-and-play setup
751
-
752
- See [full comparison](docs/architecture/comparisons.md) for detailed analysis with code examples, performance benchmarks, and decision guides.
753
-
754
- > **📦 Using Scriptable Object Architecture (SOA)?**
755
- >
756
- > DxMessaging can work alongside or replace SOA patterns. See [SOA Compatibility Guide](docs/guides/patterns.md#14-compatibility-with-scriptable-object-architecture-soa) for:
757
- >
758
- > - Fair comparison of SOA vs. DxMessaging
759
- > - Migration patterns from GameEvent/FloatVariable to DxMessaging
760
- > - How to use both systems together (SOs for configs, DxMessaging for events)
761
- > - When to keep using ScriptableObjects (immutable design data)
762
-
763
- ### 📖 Reference
764
-
765
- - [Install Guide](docs/getting-started/install.md) — All install options (OpenUPM, Git URL, scoped registry, tarball)
766
- - [Glossary](docs/reference/glossary.md) — All terms explained in plain English
767
- - [Quick Reference](docs/reference/quick-reference.md) — Cheat sheet
768
- - [API Reference](docs/reference/reference.md) Complete API
769
- - [Helpers](docs/reference/helpers.md) Source generators and utilities
770
- - [FAQ](docs/reference/faq.md) Common questions
771
- - [Troubleshooting](docs/reference/troubleshooting.md)
772
-
773
- ### 📦 Full Documentation
774
-
775
- Browse all docs: [Documentation Hub](docs/getting-started/index.md)
776
-
777
- ## Real-World Examples
778
-
779
- ### Scene Transitions
780
-
781
- ```csharp
782
- [DxUntargetedMessage]
783
- [DxAutoConstructor]
784
- public readonly partial struct SceneTransition {
785
- public readonly string sceneName;
786
- }
787
-
788
- // Multiple systems react independently
789
- public class AudioSystem : MessageAwareComponent {
790
- protected override void RegisterMessageHandlers() {
791
- base.RegisterMessageHandlers();
792
- _ = Token.RegisterUntargeted<SceneTransition>(OnScene, priority: 0);
793
- }
794
- void OnScene(ref SceneTransition m) => FadeOutMusic();
795
- }
796
-
797
- public class SaveSystem : MessageAwareComponent {
798
- protected override void RegisterMessageHandlers() {
799
- base.RegisterMessageHandlers();
800
- _ = Token.RegisterUntargeted<SceneTransition>(OnScene, priority: 0);
801
- }
802
- void OnScene(ref SceneTransition m) => SaveGame();
803
- }
804
- ```
805
-
806
- ### Achievement System
807
-
808
- ```csharp
809
- // Listen to ALL events for achievement tracking
810
- public class AchievementTracker : MessageAwareComponent {
811
- protected override void RegisterMessageHandlers() {
812
- base.RegisterMessageHandlers();
813
- _ = Token.RegisterGlobalAcceptAll(
814
- acceptAllUntargeted: m => Check(m),
815
- acceptAllTargeted: (t, m) => Check(m),
816
- acceptAllBroadcast: (s, m) => Check(m)
817
- );
818
- }
819
- }
820
- ```
821
-
822
- ## Performance
823
-
824
- - **Zero GC allocations** for struct messages
825
- - **~10ns overhead** per handler (compared to C# events)
826
- - **Type-indexed caching** for O(1) lookups
827
- - **Optimized for hot paths** with aggressive inlining
828
-
829
- See [Design & Architecture](docs/architecture/design-and-architecture.md#performance-optimizations) for details.
830
-
831
- For OS-specific benchmark tables generated by PlayMode tests, see [Performance Benchmarks](docs/architecture/performance.md).
832
-
833
- ## Comparison Table
834
-
835
- ### Comparison with Unity Messaging Frameworks
836
-
837
- | Feature | DxMessaging | UniRx | MessagePipe | Zenject Signals |
838
- | ------------------------ | ------------------ | ------------------ | ------------------ | ------------------ |
839
- | **Unity Compatibility** | ✅ Built for Unity | ✅ Built for Unity | ✅ Built for Unity | ✅ Built for Unity |
840
- | **Decoupling** | Full | ✅ Full | Full | ✅ Full |
841
- | **Lifecycle Safety** | Auto | ⚠️ Manual | ⚠️ Manual | ⚠️ DI-managed |
842
- | **Execution Order** | Priority | None | None | None |
843
- | **Type Safety** | ✅ Strong | ✅ Strong | ✅ Strong | ✅ Strong |
844
- | **Inspector Debug** | ✅ Built-in | ❌ No | ❌ No | ❌ No |
845
- | **GameObject Targeting** | ✅ Yes | ❌ No | ❌ No | ❌ No |
846
- | **Global Observers** | ✅ Yes | ❌ No | ❌ No | ❌ No |
847
- | **Interceptors** | ✅ Pipeline | No | ⚠️ Filters | No |
848
- | **Post-Processing** | ✅ Dedicated | No | ⚠️ Filters | No |
849
- | **Stream Operators** | No | ✅ Extensive | No | ⚠️ With UniRx |
850
- | **Performance** | ✅ Good (10-17M) | ✅ Good (18M) | ✅ High (97M) | ⚠️ Moderate (2.5M) |
851
- | **Dependencies** | None | ⚠️ UniTask | ✅ None | ⚠️ Zenject |
852
-
853
- ### Comparison with Traditional Approaches
854
-
855
- | Feature | DxMessaging | C# Events | UnityEvents | Static Event Bus |
856
- | ---------------------- | ------------- | ------------ | ------------- | ---------------- |
857
- | **Decoupling** | ✅ Full | ❌ Tight | ⚠️ Hidden | ✅ Yes |
858
- | **Lifecycle Safety** | Auto | Manual | ⚠️ Unity-only | ❌ Manual |
859
- | **Execution Order** | Priority | Undefined | Undefined | Undefined |
860
- | **Type Safety** | ✅ Strong | ✅ Strong | ⚠️ Weak | ⚠️ Weak |
861
- | **Context (Who/What)** | ✅ Rich | ❌ None | ❌ None | ❌ None |
862
- | **Interception** | ✅ Yes | No | ❌ No | ❌ No |
863
- | **Observability** | ✅ Built-in | ❌ No | ❌ No | ❌ No |
864
- | **Performance** | ✅ Zero-alloc | ✅ Good | ⚠️ Boxing | ✅ Good |
865
-
866
- ## Samples
867
-
868
- Import samples from Package Manager:
869
-
870
- - **[Mini Combat](Samples~/Mini%20Combat/README.md)** — Simple combat with Heal/Damage messages
871
- - **[UI Buttons + Inspector](Samples~/UI%20Buttons%20%2B%20Inspector/README.md)** — Interactive diagnostics demo
872
-
873
- ## Requirements
874
-
875
- - Unity 2021.3 or later
876
- - .NET Standard 2.1
877
- - Works with all render pipelines (URP, HDRP, Built-in)
878
-
879
- See [Compatibility](docs/reference/compatibility.md) for details.
880
-
881
- ## Contributing
882
-
883
- Contributions welcome! See [Contributing](CONTRIBUTING.md).
884
-
885
- ## License
886
-
887
- MIT License - see [License](LICENSE.md)
888
-
889
- ## Credits
890
-
891
- Created and maintained by [wallstop studios](https://wallstopstudios.com)
892
-
893
- ## Links
894
-
895
- - 📦 [Package on GitHub](https://github.com/wallstop/DxMessaging)
896
- - 🐛 [Report Issues](https://github.com/wallstop/DxMessaging/issues)
897
- - 📖 [Documentation Site](https://wallstop.github.io/DxMessaging/)
898
- - 📚 [Wiki](https://github.com/wallstop/DxMessaging/wiki)
899
-
900
- ---
1
+ # DxMessaging for Unity
2
+
3
+ <p align="center">
4
+ <img src="docs/images/DxMessaging-banner.svg" alt="DxMessaging - Type-safe messaging system for Unity" width="800"/>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://ambiguous-interactive.github.io/DxMessaging/">
9
+ <img src="https://img.shields.io/badge/Full_Documentation-Visit_the_Docs_Site-2ea44f?style=for-the-badge" alt="Full Documentation" />
10
+ </a>
11
+ </p>
12
+
13
+ [![Unity](https://img.shields.io/badge/Unity-2021.3+-black.svg)](https://unity.com/releases/editor/archive)<br/>
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)<br/>
15
+ [![openupm](https://img.shields.io/npm/v/com.wallstop-studios.dxmessaging?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.wallstop-studios.dxmessaging/)<br/>
16
+ [![Version](https://img.shields.io/npm/v/com.wallstop-studios.dxmessaging.svg)](https://www.npmjs.com/package/com.wallstop-studios.dxmessaging)<br/>
17
+ [![Performance: Standalone (IL2CPP)](<https://img.shields.io/badge/Performance-Standalone%20(IL2CPP)-blueviolet.svg>)](docs/architecture/performance.md)<br/>
18
+ [![Markdown Link Validity](https://github.com/Ambiguous-Interactive/DxMessaging/actions/workflows/markdown-link-validity.yml/badge.svg)](https://github.com/Ambiguous-Interactive/DxMessaging/actions/workflows/markdown-link-validity.yml)
19
+
20
+ > **🤖 AI Assistance Disclosure:**
21
+ >
22
+ > This project has been actively developed and maintained by human authors for over a decade. Recent versions have utilized AI assistance for documentation, test coverage, and performance optimizations.
23
+
24
+ **DxMessaging is a type-safe messaging system** that replaces sprawling C# events, brittle UnityEvents, and global static event buses with an observable and lifecycle-managed communication pattern.
25
+
26
+ Think of it as: A messaging system designed for decoupled game systems.
27
+
28
+ Need install instructions? Try [OpenUPM](https://openupm.com/packages/com.wallstop-studios.dxmessaging/) (recommended) or see the [Install Guide](docs/getting-started/install.md) for Git URLs, scoped registries, and more.
29
+
30
+ ## Table of Contents
31
+
32
+ - [30-Second Elevator Pitch](#30-second-elevator-pitch)
33
+ - [Mental Model: How to Think About DxMessaging](#mental-model-how-to-think-about-dxmessaging)
34
+ - [Quick Start (5 Minutes)](#quick-start-5-minutes)
35
+ - [Dependency Injection (DI) Compatible](#dependency-injection-di-compatible)
36
+ - [Is DxMessaging Right for You?](#is-dxmessaging-right-for-you)
37
+ - [Why DxMessaging](#why-dxmessaging)
38
+ - [Key Features](#key-features)
39
+ - [The DxMessaging Solution](#the-dxmessaging-solution)
40
+ - [Real-World Examples](#real-world-examples)
41
+ - [Performance](#performance)
42
+ - [Comparison Table](#comparison-table)
43
+ - [Samples](#samples)
44
+ - [Documentation](#documentation)
45
+ - [Requirements](#requirements)
46
+ - [Contributing](#contributing)
47
+ - [Links](#links)
48
+ - [AI Agent Integration](#ai-agent-integration)
49
+
50
+ ---
51
+
52
+ ## 30-Second Elevator Pitch
53
+
54
+ ### If you've ever
55
+
56
+ - Forgotten to unsubscribe from an event and spent hours debugging memory leaks
57
+ - Had UI code tangled with 15 different game systems
58
+ - Wondered "which event fired when?" with no way to see message flow
59
+ - Copy-pasted event boilerplate dozens of times
60
+
61
+ #### Then DxMessaging can help address these challenges
62
+
63
+ - **Automatic lifecycle management** - manages subscription lifecycle automatically, no manual unsubscribe
64
+ - **Zero coupling** - systems communicate without knowing each other exist
65
+ - **Full visibility** - see every message in the Inspector with timestamps and payloads
66
+ - **Complete control** - priority-based ordering, validation, and interception
67
+
68
+ ##### Three simple message types
69
+
70
+ 1. **Untargeted** - Global announcements
71
+ 1. **Targeted** - Commands to specific entities
72
+ 1. **Broadcast** - Observable facts from a source
73
+
74
+ See [Mental Model](#mental-model-how-to-think-about-dxmessaging) for how to choose the right type.
75
+
76
+ **One line:** It's a type-safe messaging system with automatic lifecycle management and built-in inspection tools.
77
+
78
+ ---
79
+
80
+ ## Mental Model: How to Think About DxMessaging
81
+
82
+ ### The Core Idea
83
+
84
+ DxMessaging is built around one principle: **it gets out of your way**.
85
+
86
+ You have data. You need to pass it around. That's the problem. DxMessaging provides fast, simple primitives as building blocks. You model changes as message types with optional context, using game primitives (GameObjects, components) as that context.
87
+
88
+ **You don't build your game INTO the messaging system.** It's opt-in and optional -- a tool you reach for when it helps.
89
+
90
+ ### The Three Message Types: Real-World Analogies
91
+
92
+ > 💡 _Diagrams below require Mermaid support. If they don't render, try viewing this file directly on [GitHub](https://github.com/Ambiguous-Interactive/DxMessaging)._
93
+
94
+ Each message type maps to a real-world communication pattern:
95
+
96
+ #### 1. Untargeted = PA System
97
+
98
+ ```mermaid
99
+ flowchart LR
100
+ S[Someone] -->|announces| PA[PA System]
101
+ PA --> L1[Listener A]
102
+ PA --> L2[Listener B]
103
+ PA --> L3[Listener C]
104
+ ```
105
+
106
+ Announcements with no specific recipient. Everyone who cares can hear it.
107
+
108
+ **Examples:** "The game is paused", "Settings changed", "Scene finished loading"
109
+
110
+ #### 2. Targeted = Addressed Letter
111
+
112
+ ```mermaid
113
+ flowchart LR
114
+ S[Sender] -->|"To: Player"| Letter[Message Bus]
115
+ Letter --> Player[Player receives]
116
+ Other1[Enemy A] -.->|ignores| Letter
117
+ Other2[Enemy B] -.->|ignores| Letter
118
+ ```
119
+
120
+ Commands to a specific recipient. Only that entity receives them.
121
+
122
+ **Examples:** "Player, heal for 10 HP", "Door #7, open", "This enemy, take 25 damage"
123
+
124
+ #### 3. Broadcast = Radio Station
125
+
126
+ ```mermaid
127
+ flowchart LR
128
+ Source[Enemy] -->|"I took damage!"| Radio[Message Bus]
129
+ Radio --> L1[Damage Numbers UI]
130
+ Radio --> L2[Achievement Tracker]
131
+ Radio --> L3[Analytics]
132
+ Radio --> L4[Combat Log]
133
+ ```
134
+
135
+ Facts emitted by a specific source. No intended recipient -- just an origin. Anyone can tune in.
136
+
137
+ **Examples:** "This enemy took 25 damage", "The player picked up item X", "This chest opened"
138
+
139
+ ### Choosing the Right Message Type
140
+
141
+ ```mermaid
142
+ flowchart TD
143
+ Start([I need to send a message])
144
+ Start --> Q1{Does it matter<br/>WHO sent it?}
145
+
146
+ Q1 -->|No| Q2{Does it matter<br/>WHO receives it?}
147
+ Q2 -->|No| Untargeted[Use UNTARGETED<br/>Global announcement]
148
+ Q2 -->|Yes| Targeted[Use TARGETED<br/>Directed command]
149
+
150
+ Q1 -->|Yes| Q3{Am I commanding<br/>someone to act?}
151
+ Q3 -->|Yes| Targeted
152
+ Q3 -->|No| Broadcast[Use BROADCAST<br/>Observable fact]
153
+ ```
154
+
155
+ | Question | Untargeted | Targeted | Broadcast |
156
+ | ----------------------------------- | :--------: | :------: | :-------: |
157
+ | Has a specific sender that matters? | No | No | Yes |
158
+ | Has a specific recipient? | No | Yes | No |
159
+ | Is it a command? | No | Yes | No |
160
+ | Is it an observable fact? | Maybe | No | Yes |
161
+ | Is it a global announcement? | Yes | No | No |
162
+
163
+ > **Common Mistakes:**
164
+ >
165
+ > - **Forgetting to enable the token** -- Messages won't be received. Use `MessageAwareComponent` (auto-enables) or call `Token.Enable()` manually.
166
+ > - **Targeting Component when you meant GameObject** -- These are distinct registration paths. Component-targeted messages won't reach GameObject-level handlers.
167
+ > - **Using Broadcast when you need Targeted** -- Broadcasts have no recipient, just an origin. Use Targeted when commanding a specific entity.
168
+ > - **Missing `[Dx*Message]` attribute** -- The source generator won't process the struct without the marker attribute.
169
+ >
170
+ > 📖 See [Troubleshooting](docs/reference/troubleshooting.md) for solutions to these and other issues.
171
+
172
+ **Want more depth?** See the full [Mental Model documentation](docs/concepts/mental-model.md) for detailed examples, lifecycle patterns, and edge cases.
173
+
174
+ **Ready to code?** Jump to [Quick Start](#quick-start-5-minutes) to send your first message!
175
+
176
+ ---
177
+
178
+ ## Quick Start (5 Minutes)
179
+
180
+ **New to messaging?** Start with the [Visual Guide](docs/getting-started/visual-guide.md) (5 min) for a beginner-friendly introduction!
181
+
182
+ ### 1. Install
183
+
184
+ #### Via OpenUPM (Recommended)
185
+
186
+ ```bash
187
+ openupm add com.wallstop-studios.dxmessaging
188
+ ```
189
+
190
+ #### Or via Git URL
191
+
192
+ ```bash
193
+ # Unity Package Manager > Add package from git URL...
194
+ https://github.com/Ambiguous-Interactive/DxMessaging.git
195
+ ```
196
+
197
+ See the [Install Guide](docs/getting-started/install.md) for all options including NPM scoped registries and local tarballs.
198
+
199
+ ### 2. Define Your First Message
200
+
201
+ ```csharp
202
+ using DxMessaging.Core.Attributes;
203
+
204
+ [DxTargetedMessage]
205
+ [DxAutoConstructor] // Auto-generates constructor
206
+ public readonly partial struct OpenChest {
207
+ public readonly int chestId;
208
+ [DxOptionalParameter(true)] // Optional with custom default
209
+ public readonly bool playSound;
210
+ }
211
+ ```
212
+
213
+ ### 3. Listen for It
214
+
215
+ ```csharp
216
+ using DxMessaging.Unity;
217
+
218
+ public class ChestController : MessageAwareComponent {
219
+ protected override void RegisterMessageHandlers() {
220
+ base.RegisterMessageHandlers();
221
+ _ = Token.RegisterComponentTargeted<OpenChest>(this, OnOpen);
222
+ }
223
+
224
+ void OnOpen(ref OpenChest msg) {
225
+ Debug.Log($"Opening chest {msg.chestId}");
226
+ }
227
+ }
228
+ ```
229
+
230
+ > **Heads-up:** When you override `Awake`, `OnEnable`, `OnDisable`, `OnDestroy`, or `RegisterMessageHandlers`, call the base method first or handlers fail silently. See [DXMSG006](docs/reference/analyzers.md#dxmsg006-missing-base-call).
231
+
232
+ ### 4. Send It
233
+
234
+ ```csharp
235
+ // From anywhere:
236
+ var msg = new OpenChest(chestId: 42);
237
+ msg.EmitComponentTargeted(chestComponent);
238
+ ```
239
+
240
+ No manual unsubscribe needed. Subscriptions are type-safe and lifecycle-managed.
241
+
242
+ **Stuck?** See [Troubleshooting](docs/reference/troubleshooting.md) or [FAQ](docs/reference/faq.md)
243
+
244
+ ---
245
+
246
+ ## Dependency Injection (DI) Compatible
247
+
248
+ **Using Zenject, VContainer, or Reflex?** DxMessaging is fully DI-compatible out of the box!
249
+
250
+ ```csharp
251
+ // Inject IMessageBus in any class
252
+ public class PlayerService : IInitializable, IDisposable
253
+ {
254
+ private readonly MessageRegistrationLease _lease;
255
+
256
+ public PlayerService(IMessageRegistrationBuilder builder)
257
+ {
258
+ // Builder automatically resolves your container-managed bus
259
+ _lease = builder.Build(new MessageRegistrationBuildOptions
260
+ {
261
+ Configure = token => token.RegisterUntargeted<PlayerSpawned>(OnSpawn)
262
+ });
263
+ }
264
+
265
+ public void Initialize() => _lease.Activate();
266
+ public void Dispose() => _lease.Dispose();
267
+ }
268
+ ```
269
+
270
+ ### Why use DI + Messaging?
271
+
272
+ - **DI for construction** -- Inject services, repositories, managers via constructors
273
+ - **Messaging for events** -- Reactive, decoupled communication for gameplay events
274
+ - **Combined approach** -- Clean architecture with testable, isolated buses
275
+
276
+ #### Automatic integration for
277
+
278
+ - [x] **Zenject/Extenject** -- Full-featured DI with extensive Unity support
279
+ - [x] **VContainer** -- Lightweight, high-performance DI with scoped lifetimes
280
+ - [x] **Reflex** -- Minimal API, high-performance dependency injection
281
+
282
+ ##### Get started
283
+
284
+ - [Zenject Integration Guide](docs/integrations/zenject.md) -- Complete setup with examples
285
+ - [VContainer Integration Guide](docs/integrations/vcontainer.md) -- Scoped buses for scene isolation
286
+ - [Reflex Integration Guide](docs/integrations/reflex.md) -- Minimal, lightweight patterns
287
+
288
+ ##### Core DI concepts
289
+
290
+ - [Runtime Configuration](docs/advanced/runtime-configuration.md) -- Setting message buses at runtime, re-binding registrations
291
+ - [Message Bus Providers](docs/advanced/message-bus-providers.md) -- Provider system for design-time and runtime bus configuration
292
+
293
+ **Not using DI?** No problem. DxMessaging works standalone with zero dependencies.
294
+
295
+ ---
296
+
297
+ ## Is DxMessaging Right for You?
298
+
299
+ ### Use DxMessaging When
300
+
301
+ - **You have cross-system communication** - UI needs to react to gameplay, achievements track events, analytics observe everything
302
+ - **You're building for scale** - 10+ systems that need to communicate, or growing from prototype to production
303
+ - **Memory leaks are a concern** - You've been bitten by forgotten event unsubscribes before
304
+ - **You value observability** - Need to debug "what fired when?" or track message flow
305
+ - **Teams/long-term maintenance** - Multiple developers, or you'll maintain this code for years
306
+ - **You want decoupling** - When UI classes need references to many game systems
307
+ - **You're using DI frameworks** - Compatible with Zenject/VContainer/Reflex (see [DI Compatible](#dependency-injection-di-compatible))
308
+
309
+ ### Don't Use DxMessaging When
310
+
311
+ - **Tiny prototypes/game jams** - If your game is <1000 lines and will be done in a week, C# events are fine
312
+ - **Simple, local communication** - A single button calling a single method? Just use UnityEvents or direct references
313
+ - **Performance is THE constraint** - Building a physics engine or ECS with millions of events/frame? Raw delegates are faster
314
+ - **Team is unfamiliar** - Learning curve exists; if the team isn't on board, it won't be used correctly
315
+ - **You need synchronous return values** - DxMessaging is fire-and-forget; if you need bidirectional request/response, consider other patterns
316
+
317
+ ### Maybe Use DxMessaging (Start Small)
318
+
319
+ - **Existing large codebase** - Migrate incrementally: start with new features, refactor old code gradually (see [Migration Guide](docs/guides/migration-guide.md))
320
+ - **Small team learning** - Try it for one system (e.g., achievements) before going all-in
321
+ - **Mid-size projects (5-20k lines)** - Evaluate after trying it for one complex interaction (e.g., combat or scene transitions)
322
+
323
+ ### Decision Flow
324
+
325
+ ```mermaid
326
+ flowchart TD
327
+ Q1{Does your project have 3+<br/>systems that need to talk to each other?}
328
+ Q1 -->|NO| A1[Stick with C# events or direct references]
329
+ Q1 -->|YES| Q2
330
+
331
+ Q2{Are you okay with a small<br/>upfront learning investment?}
332
+ Q2 -->|NO| A2[Stick with what you know]
333
+ Q2 -->|YES| Q3
334
+
335
+ Q3{Do you need observable, decoupled,<br/>lifecycle-safe messaging?}
336
+ Q3 -->|YES| A3[" Use DxMessaging"]
337
+ Q3 -->|NO| A4[" Keep it simple"]
338
+ ```
339
+
340
+ **Rule of thumb:** If decoupling, lifecycle leaks, or handler-ordering bugs match what you're hitting today, DxMessaging fits. If none of those describe your project, start with the [Visual Guide](docs/getting-started/visual-guide.md) or stick with simpler patterns.
341
+
342
+ Looking for hard numbers? See the Standalone IL2CPP dispatch and library-comparison [Performance Benchmarks](docs/architecture/performance.md).
343
+
344
+ ## Why DxMessaging
345
+
346
+ ### The Problems You've Probably Hit
347
+
348
+ #### Scenario 1: The Memory Leak Nightmare
349
+
350
+ You write this innocent-looking code:
351
+
352
+ ```csharp
353
+ public class GameUI : MonoBehaviour {
354
+ void OnEnable() {
355
+ GameManager.Instance.OnScoreChanged += UpdateScore;
356
+ }
357
+ // Oops, forgot OnDisable... leak!
358
+ }
359
+ ```
360
+
361
+ Months later: "Why is our game using 2GB of RAM after an hour?"
362
+
363
+ #### Scenario 2: The Spaghetti Mess
364
+
365
+ ```csharp
366
+ public class GameUI : MonoBehaviour {
367
+ [SerializeField] private Player player;
368
+ [SerializeField] private EnemySpawner spawner;
369
+ [SerializeField] private InventorySystem inventory;
370
+ [SerializeField] private QuestSystem quests;
371
+ [SerializeField] private AudioManager audio;
372
+ // ... 15 more SerializeFields ...
373
+
374
+ void Awake() {
375
+ player.OnHealthChanged += UpdateHealth;
376
+ spawner.OnWaveStart += ShowWave;
377
+ inventory.OnItemAdded += RefreshInventory;
378
+ quests.OnQuestCompleted += ShowQuestNotification;
379
+ // ... 20 more subscriptions ...
380
+ }
381
+ }
382
+ ```
383
+
384
+ **Your UI now depends on many systems.** Refactoring becomes more difficult.
385
+
386
+ #### Scenario 3: The Debugging Black Hole
387
+
388
+ Player reports: "My health bar didn't update!"
389
+
390
+ You think: "Okay, which of the 47 events touching health failed? And in what order?"
391
+
392
+ **30 minutes later:** Still setting breakpoints everywhere...
393
+
394
+ ### Common Challenges
395
+
396
+ Some developers encounter these challenges when working with traditional event systems:
397
+
398
+ - **Memory leaks** from forgotten unsubscribes
399
+ - **Tight coupling** making refactoring difficult
400
+ - **No execution order control** leading to unpredictable behavior
401
+ - **Limited debugging visibility** for tracking message flow
402
+ - **Boilerplate code** when managing many event subscriptions
403
+
404
+ ### The DxMessaging Solution
405
+
406
+ #### Same scenarios, simplified handling
407
+
408
+ ##### Scenario 1: No More Memory Leaks
409
+
410
+ ```csharp
411
+ public class GameUI : MessageAwareComponent {
412
+ protected override void RegisterMessageHandlers() {
413
+ base.RegisterMessageHandlers();
414
+ _ = Token.RegisterUntargeted<ScoreChanged>(UpdateScore);
415
+ }
416
+ // No manual cleanup needed.
417
+ // Token automatically handles OnEnable/OnDisable/OnDestroy
418
+ }
419
+ ```
420
+
421
+ ###### Automatic lifecycle = leaks are prevented by default
422
+
423
+ ##### Scenario 2: No More Coupling
424
+
425
+ ```csharp
426
+ public class GameUI : MessageAwareComponent {
427
+ // Zero SerializeFields! Zero references!
428
+
429
+ protected override void RegisterMessageHandlers() {
430
+ base.RegisterMessageHandlers();
431
+ _ = Token.RegisterUntargeted<HealthChanged>(OnHealth);
432
+ _ = Token.RegisterUntargeted<WaveStarted>(OnWave);
433
+ _ = Token.RegisterUntargeted<ItemAdded>(OnItem);
434
+ // Listen to anything, from anywhere, no coupling
435
+ }
436
+ }
437
+ ```
438
+
439
+ **Your UI is now independent.** Swapping systems no longer requires updating UI references.
440
+
441
+ ##### Scenario 3: Debugging is Built In
442
+
443
+ Open any `MessageAwareComponent` in the Inspector:
444
+
445
+ ```text
446
+ Message History (last 50):
447
+ [12:34:56] HealthChanged (amount: 25) -> Priority: 0
448
+ [12:34:55] ItemAdded (id: 42, count: 1) -> Priority: 5
449
+ [12:34:54] WaveStarted (wave: 3) -> Priority: 0
450
+
451
+ Active Registrations:
452
+ HealthChanged (5 handlers)
453
+ ItemAdded (2 handlers)
454
+ ```
455
+
456
+ **See exactly what fired, when, and who handled it.** No guesswork.
457
+
458
+ ### How It Transforms Your Code
459
+
460
+ ```csharp
461
+ // 1. Define messages (typed, discoverable)
462
+ [DxTargetedMessage]
463
+ [DxAutoConstructor]
464
+ public readonly partial struct Heal { public readonly int amount; }
465
+
466
+ // 2. Listen (automatic lifecycle - prevents leaks)
467
+ public class Player : MessageAwareComponent {
468
+ protected override void RegisterMessageHandlers() {
469
+ base.RegisterMessageHandlers();
470
+ _ = Token.RegisterComponentTargeted<Heal>(this, OnHeal);
471
+ }
472
+
473
+ void OnHeal(ref Heal m) {
474
+ health += m.amount;
475
+ Debug.Log($"Healed {m.amount}!");
476
+ }
477
+ }
478
+
479
+ // 3. Send (from anywhere - zero coupling)
480
+ var heal = new Heal(50);
481
+ heal.EmitComponentTargeted(playerComponent);
482
+ ```
483
+
484
+ #### What you get
485
+
486
+ - [x] **Automatic cleanup** - tokens clean up when components are destroyed
487
+ - [x] **Zero coupling** - no SerializeFields, no GetComponent, no direct references
488
+ - [x] **Full visibility** - see message flow in Inspector with timestamps and payloads
489
+ - [x] **Predictable order** - priority-based execution (no more mystery race conditions)
490
+ - [x] **Type-safe** - compile-time guarantees, refactor with confidence
491
+ - [x] **Intercept & validate** - enforce rules before handlers run (clamp damage, block invalid input)
492
+ - [x] **Extension points everywhere** - interceptors, handlers, post-processors with priorities
493
+
494
+ ## Key Features
495
+
496
+ What DxMessaging offers:
497
+
498
+ ### Performance: Zero-Allocation Design
499
+
500
+ **The problem with normal events:** Boxing allocations, GC spikes, memory leaks from forgotten unsubscribes.
501
+
502
+ #### DxMessaging solution
503
+
504
+ ```csharp
505
+ void OnDamage(ref TookDamage msg) { // Pass by ref = zero allocations
506
+ health -= msg.amount; // No boxing, no GC pressure
507
+ }
508
+ // Automatic cleanup prevents common leak patterns
509
+ ```
510
+
511
+ **Note:** Struct messages passed by ref avoid GC allocations, which is standard behavior for value types in C#.
512
+
513
+ ### Three Message Types
514
+
515
+ Most event systems force you into one pattern. DxMessaging gives you the right tool for each job:
516
+
517
+ ```csharp
518
+ // Untargeted: "Everyone, listen up!" (global announcements)
519
+ [DxUntargetedMessage]
520
+ public struct GamePaused { }
521
+ // -> Perfect for: settings, scene transitions, global state
522
+
523
+ // Targeted: "Hey Player, do this!" (commands to specific entities)
524
+ [DxTargetedMessage]
525
+ public struct Heal { public int amount; }
526
+ // -> Perfect for: UI actions, direct commands, player input
527
+
528
+ // Broadcast: "I took damage!" (events others can observe)
529
+ [DxBroadcastMessage]
530
+ public struct TookDamage { public int amount; }
531
+ // -> Perfect for: achievements, analytics, UI updates from entities
532
+ ```
533
+
534
+ **Why this matters:** You're not forcing everything through one generic "Event<T>" pattern. Each message type has clear semantics.
535
+
536
+ ### The Message Pipeline
537
+
538
+ Every message flows through 3 stages with priority control:
539
+
540
+ ```mermaid
541
+ flowchart LR
542
+ P[Producer] --> I[Interceptors<br/>validate/mutate]
543
+ I --> H[Handlers<br/>main logic]
544
+ H --> PP[Post-Processors<br/>analytics/logging]
545
+ ```
546
+
547
+ ### Global Observers: Listen to All Events
548
+
549
+ **The problem with normal events:** To track all player damage, enemy damage, and NPC damage, you need 3 separate event subscriptions.
550
+
551
+ **DxMessaging approach:** Subscribe once to a message type, receive all instances with source information:
552
+
553
+ ```csharp
554
+ // Track ALL damage from ANY source (players, enemies, NPCs, environment)
555
+ _ = token.RegisterBroadcastWithoutSource<TookDamage>(
556
+ (InstanceId source, TookDamage msg) => {
557
+ Debug.Log($"{source} took {msg.amount} damage!");
558
+ Analytics.LogDamage(source, msg.amount);
559
+ CheckAchievements(source, msg.amount);
560
+ }
561
+ );
562
+ ```
563
+
564
+ #### Real-world use cases
565
+
566
+ - **Achievement system:** Track all kills, deaths, damage across the entire game
567
+ - **Combat log:** "Player took 25 damage, Enemy3 took 50 damage, Boss took 100 damage"
568
+ - **Analytics:** Aggregate stats from all entities without knowing about them upfront
569
+ - **Debug tools:** Watch ALL messages in the Inspector without instrumenting code
570
+
571
+ **How this differs:** Some event bus patterns require subscribing to each entity type separately. DxMessaging allows observing all instances of a message type in one registration.
572
+
573
+ ### Interceptors: Validate Before Execution
574
+
575
+ **The problem with normal events:** Validation logic duplicated in every handler, or bugs when you forget.
576
+
577
+ **DxMessaging solution:** Validate ONCE before ANY handler runs:
578
+
579
+ ```csharp
580
+ // ONE interceptor protects ALL handlers
581
+ _ = token.RegisterBroadcastInterceptor<TookDamage>(
582
+ (ref InstanceId src, ref TookDamage msg) => {
583
+ if (msg.amount <= 0) return false; // Cancel invalid
584
+ if (msg.amount > 999) {
585
+ msg = new TookDamage(999); // Clamp excessive
586
+ }
587
+ if (IsGodModeActive(src)) return false; // Block damage
588
+ return true;
589
+ },
590
+ priority: -100 // Run FIRST
591
+ );
592
+
593
+ // Now ALL handlers receive clean, validated data
594
+ _ = token.RegisterComponentTargeted<TookDamage>(player, OnDamage);
595
+ void OnDamage(ref TookDamage msg) {
596
+ // No validation needed - interceptor guarantees validity!
597
+ health -= msg.amount;
598
+ }
599
+ ```
600
+
601
+ #### Real-world use cases
602
+
603
+ - Clamp/normalize values (damage, healing, speeds)
604
+ - Enforce game rules ("can't heal above max health")
605
+ - Block messages during cutscenes
606
+ - Log/audit sensitive actions
607
+
608
+ ### Built-in Inspector Diagnostics
609
+
610
+ **The problem with normal events:** "Which event fired? When? Who handled it? In what order?" = unknown
611
+
612
+ **DxMessaging solution:** Click any `MessageAwareComponent` in the Inspector:
613
+
614
+ #### Message History (last 50)
615
+
616
+ - `[12:34:56.123] HealthChanged`
617
+ - amount: 25
618
+ - priority: 0
619
+ - handlers: 3
620
+ - `[12:34:55.987] ItemAdded`
621
+ - itemId: 42, count: 1
622
+ - priority: 5
623
+ - handlers: 2
624
+
625
+ ##### Active Registrations
626
+
627
+ - [x] HealthChanged (priority: 0, called: 847 times)
628
+ - [x] ItemAdded (priority: 5, called: 23 times)
629
+ - [x] TookDamage (priority: 10, called: 1,203 times)
630
+
631
+ #### Real-world debugging scenarios
632
+
633
+ - "Did my message fire?" -> Check history, see timestamp
634
+ - "Why didn't my handler run?" -> Check registrations, see if it's active
635
+ - "What's firing too often?" -> Sort by call count
636
+ - "What's the execution order?" -> Sort by priority
637
+
638
+ **No more:** Setting 50 breakpoints and stepping through code for 30 minutes.
639
+
640
+ ### Local Bus Islands for Testing
641
+
642
+ **The problem with normal events:** Global static events contaminate tests. Mock complexity. Flaky tests.
643
+
644
+ **DxMessaging solution:** Each test gets its own isolated message bus:
645
+
646
+ ```csharp
647
+ [Test]
648
+ public void TestAchievementSystem() {
649
+ // Create isolated bus - zero global state
650
+ var testBus = new MessageBus();
651
+ var handler = new MessageHandler(new InstanceId(1), testBus) { active = true };
652
+ var token = MessageRegistrationToken.Create(handler, testBus);
653
+
654
+ // Test in isolation
655
+ _ = token.RegisterBroadcastWithoutSource<EnemyKilled>(achievements.OnKill);
656
+
657
+ var msg = new EnemyKilled("Boss");
658
+ msg.EmitGameObjectBroadcast(enemy, testBus); // Only this test sees it
659
+
660
+ Assert.IsTrue(achievements.Unlocked("BossSlayer"));
661
+ }
662
+ // Bus destroyed, zero cleanup needed
663
+ ```
664
+
665
+ #### Why this matters
666
+
667
+ - Tests don't interfere with each other
668
+ - No "arrange/act/cleanup" boilerplate
669
+ - No mocking frameworks needed
670
+ - Parallel test execution is supported
671
+
672
+ ## Documentation
673
+
674
+ - **[Documentation Site](https://ambiguous-interactive.github.io/DxMessaging/)** - Full searchable documentation
675
+ - **[Wiki](https://github.com/Ambiguous-Interactive/DxMessaging/wiki)** - Quick reference wiki
676
+ - **[Changelog](CHANGELOG.md)** - Version history
677
+
678
+ ### Learn
679
+
680
+ - **New here?** Start with [Getting Started Guide](docs/getting-started/getting-started.md) (10 min read)
681
+ - **Want patterns?** See [Common Patterns](docs/guides/patterns.md)
682
+ - **Deep dive?** Read [Design & Architecture](docs/architecture/design-and-architecture.md)
683
+
684
+ ### Core Concepts
685
+
686
+ - [Overview](docs/getting-started/overview.md) -- What and why
687
+ - [Quick Start](docs/getting-started/quick-start.md) -- First message in 5 minutes
688
+ - [Message Types](docs/concepts/message-types.md) -- When to use Untargeted/Targeted/Broadcast
689
+ - [Interceptors & Ordering](docs/concepts/interceptors-and-ordering.md) -- Control execution flow
690
+ - [Listening Patterns](docs/concepts/listening-patterns.md) -- All the ways to receive messages
691
+
692
+ ### Unity Integration
693
+
694
+ - [Unity Integration](docs/guides/unity-integration.md) -- MessagingComponent deep dive
695
+ - [Targeting & Context](docs/concepts/targeting-and-context.md) -- GameObject vs Component
696
+ - [Diagnostics](docs/guides/diagnostics.md) -- Inspector tools and debugging
697
+ - [Memory Reclamation](docs/guides/memory-reclamation.md) -- idle eviction, explicit Trim, and tuning the runtime settings asset
698
+
699
+ Important: Inheritance with MessageAwareComponent
700
+
701
+ - If you override lifecycle or registration hooks, call the base method.
702
+ - Use `base.RegisterMessageHandlers()` to keep default string-message registrations.
703
+ - Use `base.OnEnable()` / `base.OnDisable()` to preserve token enable/disable.
704
+ - If you need to opt out of string demos, override `RegisterForStringMessages => false` instead of skipping the base call.
705
+ - Don't hide Unity methods with `new` (e.g., `new void OnEnable()`); always `override` and call `base.*`.
706
+
707
+ ### DI Framework Integrations
708
+
709
+ DxMessaging works standalone (zero dependencies) or with any major DI framework. For detailed setup guides and code examples:
710
+
711
+ - **[Zenject Integration Guide](docs/integrations/zenject.md)** -- Full-featured DI with extensive Unity support
712
+ - **[VContainer Integration Guide](docs/integrations/vcontainer.md)** -- Lightweight DI with scoped lifetimes for scene isolation
713
+ - **[Reflex Integration Guide](docs/integrations/reflex.md)** -- Minimal API, high-performance DI
714
+
715
+ #### Core DI concepts
716
+
717
+ - **[Runtime Configuration](docs/advanced/runtime-configuration.md)** -- Setting and overriding message buses at runtime, re-binding registrations
718
+ - **[Message Bus Providers](docs/advanced/message-bus-providers.md)** -- Provider system and MessageBusProviderHandle for flexible bus configuration
719
+
720
+ Each guide includes:
721
+
722
+ - [x] Complete setup instructions with installers
723
+ - [x] Multiple usage patterns (plain classes, MonoBehaviours, direct injection)
724
+ - [x] Testing examples with isolated buses
725
+ - [x] Advanced patterns (pooling, scene scopes, signal bridges)
726
+
727
+ See the [DI Compatible section](#dependency-injection-di-compatible) above for a quick overview.
728
+
729
+ ### Comparisons
730
+
731
+ - [Compare with Other Unity Messaging Frameworks](docs/architecture/comparisons.md) -- In-depth comparison with UniRx, MessagePipe, Zenject Signals, C# events, UnityEvents, and more
732
+ - [Scriptable Object Architecture (SOA) Compatibility](docs/guides/patterns.md#14-compatibility-with-scriptable-object-architecture-soa) -- Migration patterns and interoperability with SOA
733
+
734
+ #### Quick Framework Comparison
735
+
736
+ | Framework | Best For | Key Strength | Unity Support | Learning Curve |
737
+ | ------------------- | --------------------------------- | -------------------------------- | --------------- | -------------- |
738
+ | **DxMessaging** | Unity pub/sub with lifecycle mgmt | Inspector debugging + control | Built for Unity | Moderate |
739
+ | **UniRx** | Complex event stream transforms | Reactive operators (LINQ) | Built for Unity | Easy |
740
+ | **MessagePipe** | High-performance DI messaging | Highest throughput (74M ops/sec) | Built for Unity | Steep |
741
+ | **Zenject Signals** | DI-integrated messaging | Zenject ecosystem | Built for Unity | Easy |
742
+ | **C# Events** | Simple, local communication | Minimal overhead | Native C# | Steepest |
743
+
744
+ ##### Choose DxMessaging when you want
745
+
746
+ - Unity-first design with GameObject/Component targeting
747
+ - Automatic lifecycle management (prevents common memory leaks)
748
+ - Inspector debugging to see message flow and history
749
+ - Execution order control (priority-based handlers)
750
+ - Message validation/interception pipeline
751
+ - Global observers (listen to all message instances)
752
+ - Post-processing stage (analytics, logging after handlers)
753
+ - No dependencies, plug-and-play setup
754
+
755
+ See [full comparison](docs/architecture/comparisons.md) for detailed analysis with code examples, performance benchmarks, and decision guides.
756
+
757
+ > **📦 Using Scriptable Object Architecture (SOA)?**
758
+ >
759
+ > DxMessaging can work alongside or replace SOA patterns. See [SOA Compatibility Guide](docs/guides/patterns.md#14-compatibility-with-scriptable-object-architecture-soa) for:
760
+ >
761
+ > - Fair comparison of SOA vs. DxMessaging
762
+ > - Migration patterns from GameEvent/FloatVariable to DxMessaging
763
+ > - How to use both systems together (SOs for configs, DxMessaging for events)
764
+ > - When to keep using ScriptableObjects (immutable design data)
765
+
766
+ ### Reference
767
+
768
+ - [Install Guide](docs/getting-started/install.md) -- All install options (OpenUPM, Git URL, scoped registry, tarball)
769
+ - [Glossary](docs/reference/glossary.md) -- All terms explained in plain English
770
+ - [Quick Reference](docs/reference/quick-reference.md) -- Cheat sheet
771
+ - [API Reference](docs/reference/reference.md) -- Complete API
772
+ - [Helpers](docs/reference/helpers.md) -- Source generators and utilities
773
+ - [FAQ](docs/reference/faq.md) -- Common questions
774
+ - [Troubleshooting](docs/reference/troubleshooting.md)
775
+
776
+ ### Full Documentation
777
+
778
+ Browse all docs: [Documentation Hub](docs/getting-started/index.md)
779
+
780
+ ## Real-World Examples
781
+
782
+ ### Scene Transitions
783
+
784
+ ```csharp
785
+ [DxUntargetedMessage]
786
+ [DxAutoConstructor]
787
+ public readonly partial struct SceneTransition {
788
+ public readonly string sceneName;
789
+ }
790
+
791
+ // Multiple systems react independently
792
+ public class AudioSystem : MessageAwareComponent {
793
+ protected override void RegisterMessageHandlers() {
794
+ base.RegisterMessageHandlers();
795
+ _ = Token.RegisterUntargeted<SceneTransition>(OnScene, priority: 0);
796
+ }
797
+ void OnScene(ref SceneTransition m) => FadeOutMusic();
798
+ }
799
+
800
+ public class SaveSystem : MessageAwareComponent {
801
+ protected override void RegisterMessageHandlers() {
802
+ base.RegisterMessageHandlers();
803
+ _ = Token.RegisterUntargeted<SceneTransition>(OnScene, priority: 0);
804
+ }
805
+ void OnScene(ref SceneTransition m) => SaveGame();
806
+ }
807
+ ```
808
+
809
+ ### Achievement System
810
+
811
+ ```csharp
812
+ // Listen to ALL events for achievement tracking
813
+ public class AchievementTracker : MessageAwareComponent {
814
+ protected override void RegisterMessageHandlers() {
815
+ base.RegisterMessageHandlers();
816
+ _ = Token.RegisterGlobalAcceptAll(
817
+ acceptAllUntargeted: m => Check(m),
818
+ acceptAllTargeted: (t, m) => Check(m),
819
+ acceptAllBroadcast: (s, m) => Check(m)
820
+ );
821
+ }
822
+ }
823
+ ```
824
+
825
+ ## Performance
826
+
827
+ - **Zero GC allocations** for struct messages
828
+ - **~10ns overhead** per handler (compared to C# events)
829
+ - **Type-indexed caching** for O(1) lookups
830
+ - **Optimized for hot paths** with aggressive inlining
831
+
832
+ See [Design & Architecture](docs/architecture/design-and-architecture.md#performance-optimizations) for details.
833
+
834
+ For live, CI-generated Standalone IL2CPP dispatch tables and a cross-library comparison matrix, see [Performance Benchmarks](docs/architecture/performance.md). Those numbers are auto-generated on every push with the .NET Standard 2.1 profile and a Release build; the comparison table below is a static summary, not the live data.
835
+
836
+ ## Comparison Table
837
+
838
+ ### Comparison with Unity Messaging Frameworks
839
+
840
+ | Feature | DxMessaging | UniRx | MessagePipe | Zenject Signals |
841
+ | ------------------------ | --------------- | --------------- | --------------- | --------------- |
842
+ | **Unity Compatibility** | Built for Unity | Built for Unity | Built for Unity | Built for Unity |
843
+ | **Decoupling** | Full | Full | Full | Full |
844
+ | **Lifecycle Safety** | Auto | Manual | Manual | DI-managed |
845
+ | **Execution Order** | Priority | None | None | None |
846
+ | **Type Safety** | Strong | Strong | Strong | Strong |
847
+ | **Inspector Debug** | Built-in | No | No | No |
848
+ | **GameObject Targeting** | Yes | No | No | No |
849
+ | **Global Observers** | Yes | No | No | No |
850
+ | **Interceptors** | Pipeline | No | Filters | No |
851
+ | **Post-Processing** | Dedicated | No | Filters | No |
852
+ | **Stream Operators** | No | Extensive | No | With UniRx |
853
+ | **Performance** | Good (27M) | Moderate (4M) | High (74M) | Moderate (2M) |
854
+ | **Dependencies** | None | UniTask | None | Zenject |
855
+
856
+ ### Comparison with Traditional Approaches
857
+
858
+ | Feature | DxMessaging | C# Events | UnityEvents | Static Event Bus |
859
+ | ---------------------- | ----------- | --------- | ----------- | ---------------- |
860
+ | **Decoupling** | Full | Tight | Hidden | Yes |
861
+ | **Lifecycle Safety** | Auto | Manual | Unity-only | Manual |
862
+ | **Execution Order** | Priority | Undefined | Undefined | Undefined |
863
+ | **Type Safety** | Strong | Strong | Weak | Weak |
864
+ | **Context (Who/What)** | Rich | None | None | None |
865
+ | **Interception** | Yes | No | No | No |
866
+ | **Observability** | Built-in | No | No | No |
867
+ | **Performance** | Zero-alloc | Good | Boxing | Good |
868
+
869
+ ## Samples
870
+
871
+ Import samples from Package Manager:
872
+
873
+ - **[Mini Combat](Samples~/Mini%20Combat/README.md)** -- Simple combat with Heal/Damage messages
874
+ - **[UI Buttons + Inspector](Samples~/UI%20Buttons%20%2B%20Inspector/README.md)** -- Interactive diagnostics demo
875
+
876
+ ## Requirements
877
+
878
+ - Unity 2021.3 or later
879
+ - .NET Standard 2.1
880
+ - Works with all render pipelines (URP, HDRP, Built-in)
881
+
882
+ See [Compatibility](docs/reference/compatibility.md) for details.
883
+
884
+ ## Contributing
885
+
886
+ Contributions welcome! See [Contributing](CONTRIBUTING.md).
887
+
888
+ ## License
889
+
890
+ MIT License - see [License](LICENSE.md)
891
+
892
+ ## Credits
893
+
894
+ Created and maintained by [wallstop studios](https://wallstopstudios.com)
895
+
896
+ ## Links
897
+
898
+ - [Package on GitHub](https://github.com/Ambiguous-Interactive/DxMessaging)
899
+ - [Report Issues](https://github.com/Ambiguous-Interactive/DxMessaging/issues)
900
+ - [Documentation Site](https://ambiguous-interactive.github.io/DxMessaging/)
901
+ - [Wiki](https://github.com/Ambiguous-Interactive/DxMessaging/wiki)
902
+
903
+ ## AI Agent Integration
904
+
905
+ DxMessaging publishes machine-readable context for AI agents through [llms.txt](llms.txt), following the [llmstxt.org](https://llmstxt.org/) standard. The file collects the project overview, API surface, conventions, and links into one document.
906
+
907
+ ### For AI Agents
908
+
909
+ - **[llms.txt](llms.txt)** -- Complete project overview, API reference, and context in a single file
910
+ - **[Repository Guidelines](.llm/context.md)** -- Coding standards and development workflows
911
+ - **[AI Agent Skills](.llm/skills/)** -- 155+ specialized skill documents covering documentation, testing, GitHub Actions, and more
912
+
913
+ The `llms.txt` file is automatically updated via CI/CD to stay current with project changes. It includes:
914
+
915
+ - Project overview and quick facts
916
+ - Core concepts and architecture
917
+ - Complete documentation links
918
+ - Development commands and standards
919
+ - Common pitfalls and solutions
920
+ - Performance characteristics
921
+ - Code examples and patterns
922
+
923
+ ### Keeping llms.txt Updated
924
+
925
+ The automation script ensures llms.txt stays current:
926
+
927
+ ```bash
928
+ # Update llms.txt
929
+ npm run update:llms-txt
930
+
931
+ # Verify llms.txt is current
932
+ npm run check:llms-txt
933
+ ```
934
+
935
+ GitHub Actions automatically validates and updates llms.txt:
936
+
937
+ - **Validation:** Runs on PRs to ensure llms.txt is up-to-date
938
+ - **Auto-update:** Runs weekly and on relevant file changes
939
+
940
+ ---