com.wallstop-studios.dxmessaging 2.1.4 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.artifacts/SourceGenerators.Tests/TestResults/0e800bba-2376-4062-bb19-cf916d3083a1/coverage.cobertura.xml +2624 -0
- package/.artifacts/SourceGenerators.Tests/TestResults/260ab294-e640-4674-9ae9-29b5344c0540/coverage.cobertura.xml +2624 -0
- package/.artifacts/SourceGenerators.Tests/TestResults/31c02772-7d85-4466-a2c4-93742725076e/coverage.cobertura.xml +2624 -0
- package/.artifacts/SourceGenerators.Tests/TestResults/ca4d2ef2-0c53-49dc-91b5-039938571ed8/coverage.cobertura.xml +2624 -0
- package/.artifacts/SourceGenerators.Tests/TestResults/f6cadd7e-1bf5-44b0-9351-47426600fc69/coverage.cobertura.xml +2624 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/.msCoverageSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/CoverletSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.ApplicationInsights.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.CodeAnalysis.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.AdapterUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.CommunicationUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.CoreUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.CrossPlatEngine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.PlatformAbstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.TestPlatform.Utilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.Testing.Extensions.MSBuild.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.Testing.Extensions.Telemetry.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.Testing.Extensions.TrxReport.Abstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.Testing.Extensions.VSTestBridge.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.Testing.Platform.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.VisualStudio.TestPlatform.Common.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Microsoft.VisualStudio.TestPlatform.ObjectModel.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/NUnit3.TestAdapter.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/Newtonsoft.Json.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.deps.json +872 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.runtimeconfig.json +13 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/cs/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/de/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/es/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/fr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/it/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ja/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ko/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/nunit.engine.api.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/nunit.engine.core.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/nunit.engine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/nunit.framework.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/nunit.framework.legacy.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pl/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/ru/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/testcentric.engine.metadata.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/testhost.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/testhost.exe +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/tr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Debug/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/.msCoverageSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/CoverletSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.ApplicationInsights.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.CodeAnalysis.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Extensions.DependencyModel.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.AdapterUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.CommunicationUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.CoreUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.CrossPlatEngine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.PlatformAbstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.TestPlatform.Utilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Testing.Extensions.MSBuild.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Testing.Extensions.Telemetry.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Testing.Extensions.TrxReport.Abstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Testing.Extensions.VSTestBridge.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.Testing.Platform.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.VisualStudio.TestPlatform.Common.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Microsoft.VisualStudio.TestPlatform.ObjectModel.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/NUnit3.TestAdapter.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/Newtonsoft.Json.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.deps.json +817 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.runtimeconfig.json +14 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/cs/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/de/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/es/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/fr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/it/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ja/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ko/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/nunit.engine.api.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/nunit.engine.core.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/nunit.engine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/nunit.framework.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/nunit.framework.legacy.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pl/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/ru/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/testcentric.engine.metadata.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/testhost.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/testhost.exe +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/tr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/Release/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/.msCoverageSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/CoverletSourceRootsMapping_WallstopStudios.DxMessaging.SourceGenerators.Tests +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.ApplicationInsights.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.CodeAnalysis.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Extensions.DependencyModel.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.AdapterUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.CommunicationUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.CoreUtilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.CrossPlatEngine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.PlatformAbstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.TestPlatform.Utilities.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Testing.Extensions.MSBuild.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Testing.Extensions.Telemetry.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Testing.Extensions.TrxReport.Abstractions.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Testing.Extensions.VSTestBridge.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.Testing.Platform.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.VisualStudio.TestPlatform.Common.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Microsoft.VisualStudio.TestPlatform.ObjectModel.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/NUnit3.TestAdapter.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/Newtonsoft.Json.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.deps.json +872 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.runtimeconfig.json +13 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/cs/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/de/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/es/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/fr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/it/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ja/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ko/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/nunit.engine.api.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/nunit.engine.core.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/nunit.engine.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/nunit.framework.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/nunit.framework.legacy.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pl/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/pt-BR/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/ru/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/testcentric.engine.metadata.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/testhost.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/testhost.exe +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/tr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hans/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.TestPlatform.AdapterUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.TestPlatform.CommunicationUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.TestPlatform.CoreUtilities.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.TestPlatform.CrossPlatEngine.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.Testing.Extensions.MSBuild.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.Testing.Extensions.Telemetry.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.Testing.Extensions.VSTestBridge.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.Testing.Platform.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.Common.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/bin/net9.0/zh-Hant/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs +4 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/Wallstop.9ADB0525.Up2Date +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.AssemblyInfo.cs +22 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GeneratedMSBuildEditorConfig.editorconfig +25 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GlobalUsings.g.cs +8 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj.FileListAbsolute.txt +28 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.sourcelink.json +1 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/ref/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/refint/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs +4 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/Wallstop.9ADB0525.Up2Date +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.AssemblyInfo.cs +23 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GeneratedMSBuildEditorConfig.editorconfig +17 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GlobalUsings.g.cs +8 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj.BuildWithSkipAnalyzers +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj.FileListAbsolute.txt +84 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.sourcelink.json +1 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/ref/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/Release/net9.0/refint/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs +4 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/Wallstop.9ADB0525.Up2Date +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.AssemblyInfo.cs +22 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GeneratedMSBuildEditorConfig.editorconfig +23 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.GlobalUsings.g.cs +8 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj.FileListAbsolute.txt +55 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.sourcelink.json +1 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/ref/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.artifacts/SourceGenerators.Tests/obj/net9.0/refint/WallstopStudios.DxMessaging.SourceGenerators.Tests.dll +0 -0
- package/.config/dotnet-tools.json +1 -1
- package/.csharpierignore +1 -1
- package/.cspell.json +222 -0
- package/.cursorrules +3 -0
- package/.devcontainer/.dockerignore +92 -0
- package/.devcontainer/Dockerfile +475 -0
- package/.devcontainer/devcontainer.json +54 -0
- package/.devcontainer/verify-tools.sh +183 -0
- package/.editorconfig +6 -0
- package/.gitattributes +12 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +95 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
- package/.github/copilot-instructions.md +3 -0
- package/.github/dependabot.yml +36 -2
- package/.github/pull_request_template.md +30 -0
- package/.github/release-drafter.yml +92 -0
- package/.github/scripts/check-markdown-links.ps1 +27 -3
- package/.github/scripts/check_markdown_links.py +132 -34
- package/.github/scripts/test_check_markdown_links.py +545 -0
- package/.github/workflows/actionlint.yml +47 -0
- package/.github/workflows/csharpier-autofix.yml +46 -6
- package/.github/workflows/deploy-docs.yml +118 -0
- package/.github/workflows/dotnet-tests.yml +15 -2
- package/.github/workflows/format-on-demand.yml +60 -56
- package/.github/workflows/json-format-check.yml +26 -1
- package/.github/workflows/lint-doc-links.yml +15 -1
- package/.github/workflows/markdown-json.yml +23 -5
- package/.github/workflows/markdown-link-text-check.yml +12 -1
- package/.github/workflows/markdown-link-validity.yml +12 -1
- package/.github/workflows/markdownlint.yml +29 -3
- package/.github/workflows/prettier-autofix.yml +86 -15
- package/.github/workflows/release-drafter.yml +135 -0
- package/.github/workflows/spellcheck.yml +56 -0
- package/.github/workflows/sync-wiki.yml +139 -0
- package/.github/workflows/update-dotnet-tools.yml +7 -3
- package/.github/workflows/validate-docs.yml +196 -0
- package/.github/workflows/validate-skills.yml +46 -0
- package/.github/workflows/yaml-format-lint.yml +28 -1
- package/.llm/context.md +379 -0
- package/.llm/skills/documentation/changelog-entry-writing.md +277 -0
- package/.llm/skills/documentation/changelog-management.md +228 -0
- package/.llm/skills/documentation/changelog-release-workflow.md +249 -0
- package/.llm/skills/documentation/documentation-code-samples.md +262 -0
- package/.llm/skills/documentation/documentation-style-guide.md +203 -0
- package/.llm/skills/documentation/documentation-update-workflow.md +148 -0
- package/.llm/skills/documentation/documentation-updates.md +148 -0
- package/.llm/skills/documentation/documentation-xml-docs.md +190 -0
- package/.llm/skills/documentation/external-url-fragment-validation.md +181 -0
- package/.llm/skills/documentation/github-actions-version-consistency.md +203 -0
- package/.llm/skills/documentation/link-quality-guidelines.md +267 -0
- package/.llm/skills/documentation/markdown-compatibility.md +476 -0
- package/.llm/skills/documentation/mermaid-theming.md +326 -0
- package/.llm/skills/documentation/mkdocs-navigation.md +290 -0
- package/.llm/skills/documentation/skill-file-sizing.md +260 -0
- package/.llm/skills/github-actions/git-renormalize-patterns.md +231 -0
- package/.llm/skills/github-actions/workflow-consistency.md +346 -0
- package/.llm/skills/index.md +257 -0
- package/.llm/skills/performance/aggressive-inlining-performance-notes.md +94 -0
- package/.llm/skills/performance/aggressive-inlining.md +342 -0
- package/.llm/skills/performance/array-pooling-usage-examples.md +145 -0
- package/.llm/skills/performance/array-pooling.md +315 -0
- package/.llm/skills/performance/cache-eviction-builder.md +203 -0
- package/.llm/skills/performance/cache-eviction-implementation.md +340 -0
- package/.llm/skills/performance/cache-eviction-policies.md +176 -0
- package/.llm/skills/performance/collection-pooling.md +313 -0
- package/.llm/skills/performance/object-pooling-anti-patterns.md +144 -0
- package/.llm/skills/performance/object-pooling-usage-examples.md +114 -0
- package/.llm/skills/performance/object-pooling-variations.md +147 -0
- package/.llm/skills/performance/object-pooling.md +319 -0
- package/.llm/skills/performance/readonly-struct-cached-hash-performance-notes.md +91 -0
- package/.llm/skills/performance/readonly-struct-cached-hash.md +337 -0
- package/.llm/skills/performance/serializable-dictionary-property-drawer.md +147 -0
- package/.llm/skills/performance/serializable-dictionary-usage-examples.md +135 -0
- package/.llm/skills/performance/serializable-dictionary.md +281 -0
- package/.llm/skills/performance/singleton-autoload.md +111 -0
- package/.llm/skills/performance/singleton-patterns.md +165 -0
- package/.llm/skills/performance/singleton-runtime.md +187 -0
- package/.llm/skills/performance/singleton-scriptableobject.md +177 -0
- package/.llm/skills/performance/singleton-usage-examples.md +139 -0
- package/.llm/skills/performance/stringbuilder-pooling.md +319 -0
- package/.llm/skills/performance/yield-instruction-pooling.md +347 -0
- package/.llm/skills/scripting/cross-platform-compatibility.md +224 -0
- package/{Docs/Comparisons.md.meta → .llm/skills/scripting/cross-platform-compatibility.md.meta} +1 -1
- package/.llm/skills/scripting/javascript-code-quality.md +417 -0
- package/.llm/skills/scripting/powershell-best-practices.md +386 -0
- package/.llm/skills/scripting/regex-documentation.md +461 -0
- package/.llm/skills/scripting/shell-best-practices.md +386 -0
- package/.llm/skills/scripting/validation-patterns.md +418 -0
- package/.llm/skills/solid/collection-extensions-accessors.md +229 -0
- package/.llm/skills/solid/collection-extensions-shuffle.md +144 -0
- package/.llm/skills/solid/collection-extensions-type-specialization.md +168 -0
- package/.llm/skills/solid/collection-extensions.md +144 -0
- package/.llm/skills/solid/fluent-builder-pattern-templates.md +117 -0
- package/.llm/skills/solid/fluent-builder-pattern-usage-examples.md +128 -0
- package/.llm/skills/solid/fluent-builder-pattern.md +322 -0
- package/.llm/skills/solid/iequatable-implementation-usage.md +111 -0
- package/.llm/skills/solid/iequatable-implementation-variants.md +201 -0
- package/.llm/skills/solid/iequatable-implementation.md +239 -0
- package/.llm/skills/solid/try-pattern-apis-usage.md +136 -0
- package/.llm/skills/solid/try-pattern-apis-variants.md +233 -0
- package/.llm/skills/solid/try-pattern-apis.md +220 -0
- package/.llm/skills/specification.md +468 -0
- package/.llm/skills/templates/skill-template.md +143 -0
- package/.llm/skills/testing/comprehensive-test-coverage.md +141 -0
- package/.llm/skills/testing/data-driven-tests-sources.md +255 -0
- package/.llm/skills/testing/data-driven-tests-usage.md +107 -0
- package/.llm/skills/testing/data-driven-tests.md +197 -0
- package/.llm/skills/testing/git-workflow-robustness.md +369 -0
- package/.llm/skills/testing/script-test-coverage.md +259 -0
- package/{Docs/DesignAndArchitecture.md.meta → .llm/skills/testing/script-test-coverage.md.meta} +1 -1
- package/.llm/skills/testing/shared-test-fixtures-generic-base.md +185 -0
- package/.llm/skills/testing/shared-test-fixtures-reference-counting.md +252 -0
- package/.llm/skills/testing/shared-test-fixtures.md +165 -0
- package/.llm/skills/testing/test-base-class-cleanup-usage.md +218 -0
- package/.llm/skills/testing/test-base-class-cleanup.md +323 -0
- package/.llm/skills/testing/test-categories-execution.md +142 -0
- package/.llm/skills/testing/test-categories.md +287 -0
- package/.llm/skills/testing/test-code-quality.md +243 -0
- package/.llm/skills/testing/test-coverage-data-driven.md +172 -0
- package/.llm/skills/testing/test-coverage-organization-assertions.md +171 -0
- package/.llm/skills/testing/test-coverage-scenario-categories.md +223 -0
- package/.llm/skills/testing/test-coverage-unity-anti-patterns.md +232 -0
- package/.llm/skills/testing/test-diagnostics-patterns.md +189 -0
- package/.llm/skills/testing/test-diagnostics-usage.md +196 -0
- package/.llm/skills/testing/test-diagnostics.md +247 -0
- package/.llm/skills/testing/test-failure-investigation-procedure.md +216 -0
- package/.llm/skills/testing/test-failure-investigation-root-causes.md +186 -0
- package/.llm/skills/testing/test-failure-investigation.md +119 -0
- package/.llm/skills/testing/test-invalid-skill.md +26 -0
- package/.llm/skills/testing/test-production-code.md +348 -0
- package/.lychee.toml +3 -1
- package/.markdownlint.json +2 -0
- package/.markdownlint.jsonc +2 -0
- package/.markdownlintignore +7 -7
- package/.pre-commit-config.yaml +80 -3
- package/.prettierignore +21 -21
- package/.vs/ProjectEvaluation/com.wallstop-studios.dxmessaging.metadata.v10.bin +0 -0
- package/.vs/ProjectEvaluation/com.wallstop-studios.dxmessaging.projects.v10.bin +0 -0
- package/.vs/ProjectEvaluation/com.wallstop-studios.dxmessaging.strings.v10.bin +0 -0
- package/.vs/ProjectSettings.json +3 -0
- package/.vs/VSWorkspaceState.json +9 -0
- package/.vs/com.wallstop-studios.dxmessaging/CopilotIndices/17.14.1347.20289/CodeChunks.db +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/CopilotIndices/17.14.1347.20289/SemanticSymbols.db +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/DesignTimeBuild/.dtbcache.v2 +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/FileContentIndex/41b4588f-fa0f-484b-a29b-733b6ce84c26.vsidx +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/FileContentIndex/9a73875f-5693-4296-95fd-0672bf61c5ac.vsidx +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/FileContentIndex/fd44ab3e-7015-4c20-918a-a0a916be8934.vsidx +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/.futdcache.v2 +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/.wsuo +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/DocumentLayout.backup.json +82 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/DocumentLayout.json +82 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/TestStore/0/000.testlog +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/TestStore/0/testlog.manifest +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v17/csharpier.json +1 -0
- package/.vs/com.wallstop-studios.dxmessaging/v18/.futdcache.v2 +0 -0
- package/.vs/com.wallstop-studios.dxmessaging/v18/DocumentLayout.backup.json +86 -0
- package/.vs/com.wallstop-studios.dxmessaging/v18/DocumentLayout.json +86 -0
- package/.vs/slnx.sqlite +0 -0
- package/AGENTS.md +1 -50
- package/CHANGELOG.md +42 -0
- package/CLAUDE.md +3 -0
- package/{Docs/Compatibility.md.meta → CLAUDE.md.meta} +1 -1
- package/CONTRIBUTING.md +8 -4
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/Editor/Settings/DxMessagingSettings.cs +2 -3
- package/README.md +136 -112
- package/Runtime/Core/MessageBus/IMessageBus.cs +20 -1
- package/Runtime/Core/MessageBus/MessageBus.cs +2267 -1061
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +4 -2
- package/Runtime/Core/MessageHandler.cs +519 -6
- package/Samples~/DI/README.md +2 -2
- package/Samples~/Mini Combat/README.md +7 -7
- package/Samples~/Mini Combat/Walkthrough.md +12 -12
- package/Samples~/UI Buttons + Inspector/README.md +8 -8
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/.vs/WallstopStudios.DxMessaging.SourceGenerators/CopilotIndices/17.14.1231.31060/CodeChunks.db +0 -0
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/.vs/WallstopStudios.DxMessaging.SourceGenerators/CopilotIndices/17.14.1231.31060/SemanticSymbols.db +0 -0
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/.vs/WallstopStudios.DxMessaging.SourceGenerators/DesignTimeBuild/.dtbcache.v2 +0 -0
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/.vs/WallstopStudios.DxMessaging.SourceGenerators/FileContentIndex/c90afd9b-d899-4546-bd92-ab874e448437.vsidx +0 -0
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/.vs/WallstopStudios.DxMessaging.SourceGenerators/v17/.futdcache.v2 +0 -0
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DocsSnippetCompilationTests.cs +355 -4
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj +2 -2
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +6 -3
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +1 -6
- package/Tests/Runtime/Core/BroadcastTests.cs +80 -66
- package/Tests/Runtime/Core/CyclicBufferTests.cs +284 -14
- package/Tests/Runtime/Core/DiagnosticsTests.cs +72 -0
- package/Tests/Runtime/Core/DxMessagingStaticStateTests.cs +1 -1
- package/Tests/Runtime/Core/GlobalAcceptAllTests.cs +26 -26
- package/Tests/Runtime/Core/MutationDedupeTests.cs +24 -24
- package/Tests/Runtime/Core/MutationDuringEmissionTests.cs +126 -9
- package/Tests/Runtime/Core/MutationHighVolumeStressTests.cs +512 -0
- package/Tests/Runtime/Core/MutationHighVolumeStressTests.cs.meta +11 -0
- package/Tests/Runtime/Core/NominalTests.cs +82 -81
- package/Tests/Runtime/Core/PostProcessorTests.cs +108 -99
- package/Tests/Runtime/Core/RegistrationTests.cs +159 -159
- package/Tests/Runtime/Core/TargetedTests.cs +80 -66
- package/Tests/Runtime/Core/UntargetedTests.cs +25 -20
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +1 -1
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +1 -1
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +1 -1
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +0 -1
- package/coverage/clover.xml +219 -0
- package/{Docs/Advanced.md.meta → coverage/clover.xml.meta} +1 -1
- package/coverage/coverage-final.json +2 -0
- package/coverage/coverage-final.json.meta +7 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/base.css.meta +7 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/block-navigation.js.meta +7 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/favicon.png.meta +127 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/index.html.meta +7 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.css.meta +7 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/prettify.js.meta +7 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sort-arrow-sprite.png.meta +127 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/sorter.js.meta +7 -0
- package/coverage/lcov-report/transform-docs-to-wiki.js.html +1312 -0
- package/coverage/lcov-report/transform-docs-to-wiki.js.html.meta +7 -0
- package/coverage/lcov-report/vendor.meta +8 -0
- package/coverage/lcov-report.meta +8 -0
- package/coverage/lcov.info +365 -0
- package/coverage/lcov.info.meta +7 -0
- package/{Docs/EmitShorthands.md → docs/advanced/emit-shorthands.md} +17 -17
- package/docs/advanced/emit-shorthands.md.meta +7 -0
- package/{Docs/MessageBusProviders.md → docs/advanced/message-bus-providers.md} +5 -4
- package/docs/advanced/message-bus-providers.md.meta +7 -0
- package/docs/advanced/registration-builders.md +444 -0
- package/docs/advanced/registration-builders.md.meta +7 -0
- package/{Docs/RuntimeConfiguration.md → docs/advanced/runtime-configuration.md} +5 -4
- package/docs/advanced/runtime-configuration.md.meta +7 -0
- package/{Docs/StringMessages.md → docs/advanced/string-messages.md} +2 -2
- package/docs/advanced/string-messages.md.meta +7 -0
- package/docs/advanced.meta +8 -0
- package/{Docs/Comparisons.md → docs/architecture/comparisons.md} +71 -66
- package/docs/architecture/comparisons.md.meta +7 -0
- package/{Docs/DesignAndArchitecture.md → docs/architecture/design-and-architecture.md} +16 -16
- package/docs/architecture/design-and-architecture.md.meta +7 -0
- package/docs/architecture/performance.md +53 -0
- package/docs/architecture/performance.md.meta +7 -0
- package/docs/architecture.meta +8 -0
- package/docs/concepts/index.md +37 -0
- package/docs/concepts/index.md.meta +7 -0
- package/{Docs/InterceptorsAndOrdering.md → docs/concepts/interceptors-and-ordering.md} +56 -55
- package/docs/concepts/interceptors-and-ordering.md.meta +7 -0
- package/{Docs/ListeningPatterns.md → docs/concepts/listening-patterns.md} +17 -15
- package/docs/concepts/listening-patterns.md.meta +7 -0
- package/docs/concepts/mental-model.md +390 -0
- package/docs/concepts/mental-model.md.meta +7 -0
- package/{Docs/MessageTypes.md → docs/concepts/message-types.md} +18 -13
- package/docs/concepts/message-types.md.meta +7 -0
- package/{Docs/TargetingAndContext.md → docs/concepts/targeting-and-context.md} +10 -10
- package/docs/concepts/targeting-and-context.md.meta +7 -0
- package/docs/concepts.meta +8 -0
- package/docs/examples/end-to-end-scene-transitions.md.meta +7 -0
- package/{Docs/EndToEnd.md → docs/examples/end-to-end.md} +1 -1
- package/docs/examples/end-to-end.md.meta +7 -0
- package/docs/examples.meta +8 -0
- package/{Docs/GettingStarted.md → docs/getting-started/getting-started.md} +31 -28
- package/docs/getting-started/getting-started.md.meta +7 -0
- package/docs/getting-started/index.md +317 -0
- package/docs/getting-started/index.md.meta +7 -0
- package/docs/getting-started/install.md +89 -0
- package/docs/getting-started/install.md.meta +7 -0
- package/{Docs/Overview.md → docs/getting-started/overview.md} +37 -35
- package/docs/getting-started/overview.md.meta +7 -0
- package/docs/getting-started/quick-start.md +175 -0
- package/docs/getting-started/quick-start.md.meta +7 -0
- package/{Docs/VisualGuide.md → docs/getting-started/visual-guide.md} +114 -111
- package/docs/getting-started/visual-guide.md.meta +7 -0
- package/docs/getting-started.meta +8 -0
- package/{Docs/Advanced.md → docs/guides/advanced.md} +4 -4
- package/docs/guides/advanced.md.meta +7 -0
- package/docs/guides/diagnostics.md +300 -0
- package/docs/guides/diagnostics.md.meta +7 -0
- package/{Docs/MigrationGuide.md → docs/guides/migration-guide.md} +3 -3
- package/docs/guides/migration-guide.md.meta +7 -0
- package/{Docs/Patterns.md → docs/guides/patterns.md} +22 -22
- package/docs/guides/patterns.md.meta +7 -0
- package/docs/guides/testing.md +243 -0
- package/docs/guides/testing.md.meta +7 -0
- package/{Docs/UnityIntegration.md → docs/guides/unity-integration.md} +13 -13
- package/docs/guides/unity-integration.md.meta +7 -0
- package/docs/guides.meta +8 -0
- package/docs/hooks.py +177 -0
- package/docs/hooks.py.meta +7 -0
- package/docs/images/DxMessaging-banner.svg +119 -0
- package/docs/images/DxMessaging-banner.svg.meta +7 -0
- package/docs/images.meta +8 -0
- package/docs/index.md +61 -0
- package/docs/integrations/index.md +66 -0
- package/docs/integrations/index.md.meta +7 -0
- package/{Docs/Integrations/Reflex.md → docs/integrations/reflex.md} +43 -18
- package/{Docs/Integrations/VContainer.md → docs/integrations/vcontainer.md} +18 -9
- package/{Docs/Integrations/Zenject.md → docs/integrations/zenject.md} +6 -6
- package/docs/javascripts/csharp-highlight.js +246 -0
- package/docs/javascripts/csharp-highlight.js.meta +7 -0
- package/docs/javascripts/mermaid-config.js +387 -0
- package/docs/javascripts/mermaid-config.js.meta +7 -0
- package/docs/javascripts.meta +8 -0
- package/{Docs/Compatibility.md → docs/reference/compatibility.md} +7 -7
- package/docs/reference/compatibility.md.meta +7 -0
- package/{Docs/FAQ.md → docs/reference/faq.md} +21 -26
- package/docs/reference/faq.md.meta +7 -0
- package/{Docs/Glossary.md → docs/reference/glossary.md} +18 -13
- package/docs/reference/glossary.md.meta +7 -0
- package/{Docs/Helpers.md → docs/reference/helpers.md} +397 -93
- package/docs/reference/helpers.md.meta +7 -0
- package/{Docs/QuickReference.md → docs/reference/quick-reference.md} +132 -58
- package/docs/reference/quick-reference.md.meta +7 -0
- package/docs/reference/reference.md +365 -0
- package/docs/reference/reference.md.meta +7 -0
- package/{Docs/Troubleshooting.md → docs/reference/troubleshooting.md} +12 -17
- package/docs/reference/troubleshooting.md.meta +7 -0
- package/docs/reference.meta +8 -0
- package/docs/stylesheets/extra.css +485 -0
- package/docs/stylesheets/extra.css.meta +7 -0
- package/docs/stylesheets.meta +8 -0
- package/mkdocs.yml +200 -0
- package/mkdocs.yml.meta +7 -0
- package/package.json +8 -6
- package/requirements-docs.txt +23 -0
- package/requirements-docs.txt.meta +7 -0
- package/scripts/__tests__/generate-skills-index.test.js +397 -0
- package/scripts/__tests__/generate-skills-index.test.js.meta +7 -0
- package/scripts/__tests__/mermaid-config.test.js +467 -0
- package/scripts/__tests__/mermaid-config.test.js.meta +7 -0
- package/scripts/__tests__/sync-banner-version.test.js +606 -0
- package/scripts/__tests__/sync-banner-version.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-optional-fields.test.js +1474 -0
- package/scripts/__tests__/validate-skills-optional-fields.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-required-fields.test.js +188 -0
- package/scripts/__tests__/validate-skills-required-fields.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-tags.test.js +353 -0
- package/scripts/__tests__/validate-skills-tags.test.js.meta +7 -0
- package/scripts/__tests__/validate-workflows.test.js +188 -0
- package/scripts/__tests__/validate-workflows.test.js.meta +7 -0
- package/scripts/__tests__.meta +8 -0
- package/scripts/check-eol.js +290 -19
- package/scripts/check-eol.ps1 +103 -15
- package/scripts/fix-eol.js +40 -8
- package/scripts/generate-skills-index.js +597 -0
- package/scripts/generate-skills-index.js.meta +7 -0
- package/scripts/generate-skills-index.ps1 +38 -0
- package/scripts/generate-skills-index.ps1.meta +7 -0
- package/scripts/hooks/pre-commit +59 -0
- package/scripts/hooks/pre-commit.meta +7 -0
- package/scripts/hooks.meta +8 -0
- package/scripts/sync-banner-version.ps1 +133 -0
- package/scripts/sync-banner-version.ps1.meta +7 -0
- package/scripts/sync-banner-version.sh +16 -0
- package/scripts/sync-banner-version.sh.meta +7 -0
- package/scripts/validate-skills.js +757 -0
- package/scripts/validate-skills.js.meta +7 -0
- package/scripts/validate-workflows.js +272 -0
- package/scripts/validate-workflows.js.meta +7 -0
- package/scripts/wiki/__tests__/transform.test.js +380 -0
- package/scripts/wiki/__tests__/transform.test.js.meta +7 -0
- package/scripts/wiki/__tests__.meta +7 -0
- package/scripts/wiki/generate-wiki-sidebar.js +122 -0
- package/{Docs/EmitShorthands.md.meta → scripts/wiki/generate-wiki-sidebar.js.meta} +8 -2
- package/scripts/wiki/transform-docs-to-wiki.js +409 -0
- package/scripts/wiki/transform-docs-to-wiki.js.meta +7 -0
- package/scripts/wiki.meta +8 -0
- package/site/404.html +1 -0
- package/site/404.html.meta +7 -0
- package/site/advanced/emit-shorthands/index.html +212 -0
- package/site/advanced/emit-shorthands/index.html.meta +7 -0
- package/site/advanced/emit-shorthands.meta +8 -0
- package/site/advanced/message-bus-providers/index.html +241 -0
- package/site/advanced/message-bus-providers/index.html.meta +7 -0
- package/site/advanced/message-bus-providers.meta +8 -0
- package/site/advanced/registration-builders/index.html +228 -0
- package/site/advanced/registration-builders/index.html.meta +7 -0
- package/site/advanced/registration-builders.meta +8 -0
- package/site/advanced/runtime-configuration/index.html +196 -0
- package/site/advanced/runtime-configuration/index.html.meta +7 -0
- package/site/advanced/runtime-configuration.meta +8 -0
- package/site/advanced/string-messages/index.html +29 -0
- package/site/advanced/string-messages/index.html.meta +7 -0
- package/site/advanced/string-messages.meta +8 -0
- package/site/advanced.meta +8 -0
- package/site/architecture/comparisons/index.html +356 -0
- package/site/architecture/comparisons/index.html.meta +7 -0
- package/site/architecture/comparisons.meta +8 -0
- package/site/architecture/design-and-architecture/index.html +34 -0
- package/site/architecture/design-and-architecture/index.html.meta +7 -0
- package/site/architecture/design-and-architecture.meta +8 -0
- package/site/architecture/performance/index.html +1 -0
- package/site/architecture/performance/index.html.meta +7 -0
- package/site/architecture/performance.meta +8 -0
- package/site/architecture.meta +8 -0
- package/site/assets/images/favicon.png +0 -0
- package/site/assets/images/favicon.png.meta +127 -0
- package/site/assets/images.meta +8 -0
- package/site/assets/javascripts/bundle.79ae519e.min.js +16 -0
- package/site/assets/javascripts/bundle.79ae519e.min.js.map +7 -0
- package/site/assets/javascripts/bundle.79ae519e.min.js.map.meta +7 -0
- package/site/assets/javascripts/bundle.79ae519e.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.ar.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.da.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.de.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.du.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.el.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.es.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.fi.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.fr.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.he.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.hi.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.hu.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.hy.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.it.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.ja.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.jp.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.kn.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.ko.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.multi.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.nl.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.no.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.pt.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.ro.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.ru.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.sa.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.sv.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.ta.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.te.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.th.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- package/site/assets/javascripts/lunr/min/lunr.tr.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.vi.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- package/site/assets/javascripts/lunr/min/lunr.zh.min.js.meta +7 -0
- package/site/assets/javascripts/lunr/min.meta +8 -0
- package/site/assets/javascripts/lunr/tinyseg.js +206 -0
- package/site/assets/javascripts/lunr/tinyseg.js.meta +7 -0
- package/site/assets/javascripts/lunr/wordcut.js +6708 -0
- package/site/assets/javascripts/lunr/wordcut.js.meta +7 -0
- package/site/assets/javascripts/lunr.meta +8 -0
- package/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
- package/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
- package/site/assets/javascripts/workers/search.2c215733.min.js.map.meta +7 -0
- package/site/assets/javascripts/workers/search.2c215733.min.js.meta +7 -0
- package/site/assets/javascripts/workers.meta +8 -0
- package/site/assets/javascripts.meta +8 -0
- package/site/assets/stylesheets/main.484c7ddc.min.css +1 -0
- package/site/assets/stylesheets/main.484c7ddc.min.css.map +1 -0
- package/site/assets/stylesheets/main.484c7ddc.min.css.map.meta +7 -0
- package/site/assets/stylesheets/main.484c7ddc.min.css.meta +7 -0
- package/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
- package/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
- package/site/assets/stylesheets/palette.ab4e12ef.min.css.map.meta +7 -0
- package/site/assets/stylesheets/palette.ab4e12ef.min.css.meta +7 -0
- package/site/assets/stylesheets.meta +8 -0
- package/site/assets.meta +8 -0
- package/site/concepts/index.html +1 -0
- package/site/concepts/index.html.meta +7 -0
- package/site/concepts/interceptors-and-ordering/index.html +352 -0
- package/site/concepts/interceptors-and-ordering/index.html.meta +7 -0
- package/site/concepts/interceptors-and-ordering.meta +8 -0
- package/site/concepts/listening-patterns/index.html +193 -0
- package/site/concepts/listening-patterns/index.html.meta +7 -0
- package/site/concepts/listening-patterns.meta +8 -0
- package/site/concepts/mental-model/index.html +146 -0
- package/site/concepts/mental-model/index.html.meta +7 -0
- package/site/concepts/mental-model.meta +8 -0
- package/site/concepts/message-types/index.html +111 -0
- package/site/concepts/message-types/index.html.meta +7 -0
- package/site/concepts/message-types.meta +8 -0
- package/site/concepts/targeting-and-context/index.html +201 -0
- package/site/concepts/targeting-and-context/index.html.meta +7 -0
- package/site/concepts/targeting-and-context.meta +8 -0
- package/site/concepts.meta +8 -0
- package/site/css/timeago.css +15 -0
- package/site/css/timeago.css.meta +7 -0
- package/site/css.meta +8 -0
- package/site/examples/end-to-end/index.html +85 -0
- package/site/examples/end-to-end/index.html.meta +7 -0
- package/site/examples/end-to-end-scene-transitions/index.html +84 -0
- package/site/examples/end-to-end-scene-transitions/index.html.meta +7 -0
- package/site/examples/end-to-end-scene-transitions.meta +8 -0
- package/site/examples/end-to-end.meta +8 -0
- package/site/examples.meta +8 -0
- package/site/getting-started/getting-started/index.html +66 -0
- package/site/getting-started/getting-started/index.html.meta +7 -0
- package/site/getting-started/getting-started.meta +8 -0
- package/site/getting-started/index.html +29 -0
- package/site/getting-started/index.html.meta +7 -0
- package/site/getting-started/install/index.html +8 -0
- package/site/getting-started/install/index.html.meta +7 -0
- package/site/getting-started/install.meta +8 -0
- package/site/getting-started/overview/index.html +33 -0
- package/site/getting-started/overview/index.html.meta +7 -0
- package/site/getting-started/overview.meta +8 -0
- package/site/getting-started/quick-start/index.html +81 -0
- package/site/getting-started/quick-start/index.html.meta +7 -0
- package/site/getting-started/quick-start.meta +8 -0
- package/site/getting-started/visual-guide/index.html +256 -0
- package/site/getting-started/visual-guide/index.html.meta +7 -0
- package/site/getting-started/visual-guide.meta +8 -0
- package/site/getting-started.meta +8 -0
- package/site/guides/advanced/index.html +119 -0
- package/site/guides/advanced/index.html.meta +7 -0
- package/site/guides/advanced.meta +8 -0
- package/site/guides/diagnostics/index.html +110 -0
- package/site/guides/diagnostics/index.html.meta +7 -0
- package/site/guides/diagnostics.meta +8 -0
- package/site/guides/migration-guide/index.html +199 -0
- package/site/guides/migration-guide/index.html.meta +7 -0
- package/site/guides/migration-guide.meta +8 -0
- package/site/guides/patterns/index.html +545 -0
- package/site/guides/patterns/index.html.meta +7 -0
- package/site/guides/patterns.meta +8 -0
- package/site/guides/testing/index.html +153 -0
- package/site/guides/testing/index.html.meta +7 -0
- package/site/guides/testing.meta +8 -0
- package/site/guides/unity-integration/index.html +67 -0
- package/site/guides/unity-integration/index.html.meta +7 -0
- package/site/guides/unity-integration.meta +8 -0
- package/site/guides.meta +8 -0
- package/site/hooks.py +168 -0
- package/site/hooks.py.meta +7 -0
- package/site/images/DxMessaging-banner.svg +119 -0
- package/site/images/DxMessaging-banner.svg.meta +7 -0
- package/site/images.meta +8 -0
- package/site/index.html +17 -0
- package/site/index.html.meta +7 -0
- package/site/integrations/index.html +8 -0
- package/site/integrations/index.html.meta +7 -0
- package/site/integrations/reflex/index.html +186 -0
- package/site/integrations/reflex/index.html.meta +7 -0
- package/site/integrations/reflex.meta +8 -0
- package/site/integrations/vcontainer/index.html +192 -0
- package/site/integrations/vcontainer/index.html.meta +7 -0
- package/site/integrations/vcontainer.meta +8 -0
- package/site/integrations/zenject/index.html +197 -0
- package/site/integrations/zenject/index.html.meta +7 -0
- package/site/integrations/zenject.meta +8 -0
- package/site/integrations.meta +8 -0
- package/site/javascripts/csharp-highlight.js +246 -0
- package/site/javascripts/csharp-highlight.js.meta +7 -0
- package/site/javascripts/mermaid-config.js +350 -0
- package/site/javascripts/mermaid-config.js.meta +7 -0
- package/site/javascripts.meta +8 -0
- package/site/js/timeago.min.js +2 -0
- package/site/js/timeago.min.js.meta +7 -0
- package/site/js/timeago_mkdocs_material.js +33 -0
- package/site/js/timeago_mkdocs_material.js.meta +7 -0
- package/site/js.meta +8 -0
- package/site/reference/compatibility/index.html +1 -0
- package/site/reference/compatibility/index.html.meta +7 -0
- package/site/reference/compatibility.meta +8 -0
- package/site/reference/faq/index.html +1 -0
- package/site/reference/faq/index.html.meta +7 -0
- package/site/reference/faq.meta +8 -0
- package/site/reference/glossary/index.html +55 -0
- package/site/reference/glossary/index.html.meta +7 -0
- package/site/reference/glossary.meta +8 -0
- package/site/reference/helpers/index.html +475 -0
- package/site/reference/helpers/index.html.meta +7 -0
- package/site/reference/helpers.meta +8 -0
- package/site/reference/quick-reference/index.html +138 -0
- package/site/reference/quick-reference/index.html.meta +7 -0
- package/site/reference/quick-reference.meta +8 -0
- package/site/reference/reference/index.html +194 -0
- package/site/reference/reference/index.html.meta +7 -0
- package/site/reference/reference.meta +8 -0
- package/site/reference/troubleshooting/index.html +1 -0
- package/site/reference/troubleshooting/index.html.meta +7 -0
- package/site/reference/troubleshooting.meta +8 -0
- package/site/reference.meta +8 -0
- package/site/search/search_index.json +1 -0
- package/site/search/search_index.json.meta +7 -0
- package/site/search.meta +8 -0
- package/site/sitemap.xml +163 -0
- package/site/sitemap.xml.gz +0 -0
- package/site/sitemap.xml.gz.meta +7 -0
- package/site/sitemap.xml.meta +7 -0
- package/site/stylesheets/extra.css +485 -0
- package/site/stylesheets/extra.css.meta +7 -0
- package/site/stylesheets.meta +8 -0
- package/.github/workflows/npm-publish.yml +0 -75
- package/Docs/Diagnostics.md +0 -61
- package/Docs/Diagnostics.md.meta +0 -7
- package/Docs/EndToEnd.md.meta +0 -7
- package/Docs/EndToEndSceneTransitions.md.meta +0 -7
- package/Docs/FAQ.md.meta +0 -7
- package/Docs/GettingStarted.md.meta +0 -7
- package/Docs/Glossary.md.meta +0 -7
- package/Docs/Helpers.md.meta +0 -7
- package/Docs/Index.md +0 -305
- package/Docs/Install.md +0 -60
- package/Docs/Install.md.meta +0 -7
- package/Docs/InterceptorsAndOrdering.md.meta +0 -7
- package/Docs/ListeningPatterns.md.meta +0 -7
- package/Docs/MessageBusProviders.md.meta +0 -7
- package/Docs/MessageTypes.md.meta +0 -7
- package/Docs/MigrationGuide.md.meta +0 -7
- package/Docs/Overview.md.meta +0 -7
- package/Docs/Patterns.md.meta +0 -7
- package/Docs/Performance.md +0 -35
- package/Docs/Performance.md.meta +0 -7
- package/Docs/QuickReference.md.meta +0 -7
- package/Docs/QuickStart.md +0 -179
- package/Docs/QuickStart.md.meta +0 -7
- package/Docs/Reference.md +0 -88
- package/Docs/Reference.md.meta +0 -7
- package/Docs/RuntimeConfiguration.md.meta +0 -7
- package/Docs/StringMessages.md.meta +0 -7
- package/Docs/TargetingAndContext.md.meta +0 -7
- package/Docs/Troubleshooting.md.meta +0 -7
- package/Docs/UnityIntegration.md.meta +0 -7
- package/Docs/VisualGuide.md.meta +0 -7
- /package/{Docs/EndToEndSceneTransitions.md → docs/examples/end-to-end-scene-transitions.md} +0 -0
- /package/{Docs/Index.md.meta → docs/index.md.meta} +0 -0
- /package/{Docs/Integrations/Reflex.md.meta → docs/integrations/reflex.md.meta} +0 -0
- /package/{Docs/Integrations/VContainer.md.meta → docs/integrations/vcontainer.md.meta} +0 -0
- /package/{Docs/Integrations/Zenject.md.meta → docs/integrations/zenject.md.meta} +0 -0
- /package/{Docs/Integrations.meta → docs/integrations.meta} +0 -0
- /package/{Docs.meta → docs.meta} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"config":{"lang":["en"],"separator":"[\\s\\-,:!=\\[\\]()\"/]+|(?!\\b)(?=[A-Z][a-z])|\\.(?!\\d)|&[lg]t;","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"DxMessaging Documentation","text":"<p>DxMessaging is a high-performance, type-safe messaging library for Unity that provides a clean, decoupled communication pattern between game components.</p> <p>Get Started | View on GitHub</p>"},{"location":"#why-dxmessaging","title":"Why DxMessaging?","text":"<ul> <li>\u26a1 High Performance \u2014 Zero-allocation message dispatch with pooled handlers</li> <li>\ud83d\udee1\ufe0f Type-Safe \u2014 Compile-time message type checking prevents runtime errors</li> <li>\ud83d\udce6 Decoupled Architecture \u2014 Components communicate without direct references</li> <li>\ud83c\udfaf Flexible Targeting \u2014 Untargeted, Targeted, and Broadcast message patterns</li> <li>\ud83d\udd27 Unity-Native \u2014 Built specifically for Unity with MonoBehaviour integration</li> </ul>"},{"location":"#quick-links","title":"Quick Links","text":"<ul> <li>Mental Model \u2014 How to think about DxMessaging</li> <li>Visual Guide \u2014 Beginner-friendly introduction with diagrams</li> <li>Quick Start \u2014 Your first message in 5 minutes</li> <li>Message Types \u2014 Untargeted, Targeted, Broadcast patterns</li> <li>API Reference \u2014 Complete API documentation</li> </ul>"},{"location":"#installation","title":"Installation","text":""},{"location":"#via-openupm-recommended","title":"Via OpenUPM (Recommended)","text":"Bash<pre><code>openupm add com.wallstop-studios.dxmessaging\n</code></pre>"},{"location":"#or-via-git-url","title":"Or via Git URL","text":"Text Only<pre><code>https://github.com/wallstop/DxMessaging.git\n</code></pre> <p>See the Install Guide for all options including NPM scoped registries and local tarballs.</p>"},{"location":"#quick-example","title":"Quick Example","text":"C#<pre><code>// Define a message\npublic readonly struct DamageMessage : IUntargetedMessage\n{\n public readonly int amount;\n public DamageMessage(int amount) => this.amount = amount;\n}\n\n// Subscribe and handle (using a MessageRegistrationToken)\n_ = Token.RegisterUntargeted<DamageMessage>(msg =>\n Debug.Log($\"Received {msg.amount} damage!\"));\n\n// Emit the message\nDamageMessage damage = new DamageMessage(25);\ndamage.EmitUntargeted();\n</code></pre>"},{"location":"advanced/emit-shorthands/","title":"Emit Shorthands (<code>Emit</code>, <code>EmitAt</code>, <code>EmitFrom</code>)","text":"<p>Quick, readable ways to send messages without reaching for <code>MessageHandler.MessageBus</code> directly.</p> <p>These shorthands provide concise syntax for sending messages, but they come with important Unity-specific gotchas around GameObject vs Component targeting. Read the Pitfalls section to avoid silent bugs.</p>"},{"location":"advanced/emit-shorthands/#what-you-get","title":"What You Get","text":"<p>Three methods that work on any message:</p> Method Purpose Message Type Example <code>Emit()</code> Send globally to everyone <code>IUntargetedMessage</code> <code>new SceneLoaded(1).Emit();</code> <code>EmitAt(target)</code> Send to a specific target <code>ITargetedMessage</code> <code>new Heal(10).EmitAt(playerId);</code> <code>EmitFrom(source)</code> Broadcast from a source <code>IBroadcastMessage</code> <code>new TookDamage(5).EmitFrom(enemyId);</code>"},{"location":"advanced/emit-shorthands/#quick-start-examples","title":"Quick Start Examples","text":"C#<pre><code>using DxMessaging.Core; // InstanceId\nusing DxMessaging.Core.Attributes; // Dx* attributes\nusing DxMessaging.Core.Extensions; // Emit/EmitAt/EmitFrom\n\n// Define your messages\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneLoaded { public readonly int buildIndex; }\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\n// Emit them\nvar scene = new SceneLoaded(1);\nscene.Emit(); // Global: everyone listening receives this\n\nvar heal = new Heal(10);\nheal.EmitAt(player); // Targeted: only the player receives this\n\nvar damage = new TookDamage(5);\ndamage.EmitFrom(enemy); // Broadcast: listeners interested in enemy damage receive this\n</code></pre>"},{"location":"advanced/emit-shorthands/#bus-first-helpers","title":"Bus-First Helpers","text":"<p>Prefer injecting <code>IMessageBus</code> (or <code>IMessageRegistrationBuilder</code>) in DI scenarios and use the bus-first extensions for parity with the shorthands:</p> C#<pre><code>using DxMessaging.Core.Extensions;\nusing DxMessaging.Core.Attributes;\n\npublic sealed class ScoreReporter\n{\n private readonly IMessageBus messageBus;\n\n public ScoreReporter(IMessageBus messageBus)\n {\n this.messageBus = messageBus;\n }\n\n public void Report(int value)\n {\n ScoreChanged message = new ScoreChanged(value);\n messageBus.EmitUntargeted(ref message);\n }\n}\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct ScoreChanged\n{\n public readonly int Value;\n}\n</code></pre> <p>These helpers mirror the struct/class/targeted/broadcast overloads available on message instances. They keep DI-friendly services aligned with the same dispatch path as Unity shorthands.</p>"},{"location":"advanced/emit-shorthands/#understanding-each-shorthand","title":"Understanding Each Shorthand","text":""},{"location":"advanced/emit-shorthands/#emit--global-broadcast-untargeted","title":"<code>Emit()</code> \u2014 Global Broadcast (Untargeted)","text":"<p>When to use: Scene-wide notifications that everyone should know about.</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct GamePaused { }\n\nvar msg = new GamePaused();\nmsg.Emit(); // Every listener receives this\n</code></pre>"},{"location":"advanced/emit-shorthands/#equivalent-to","title":"Equivalent to","text":"C#<pre><code>msg.EmitUntargeted();\n// or\nMessageHandler.MessageBus.UntargetedBroadcast(ref msg);\n</code></pre>"},{"location":"advanced/emit-shorthands/#examples","title":"Examples","text":"<ul> <li>Scene loaded/unloaded</li> <li>Game state changes (paused, resumed)</li> <li>Settings changed</li> <li>Level-up notifications</li> </ul>"},{"location":"advanced/emit-shorthands/#emitattarget--targeted-message","title":"<code>EmitAt(target)</code> \u2014 Targeted Message","text":"<p>When to use: Commands or notifications for a specific GameObject or Component.</p> C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\nvar heal = new Heal(10);\nheal.EmitAt(player); // Only the player receives this\n</code></pre> <p>Caution: <code>InstanceId</code> has implicit conversion from both <code>GameObject</code> and <code>Component</code>. Make sure your target type matches how listeners registered!</p>"},{"location":"advanced/emit-shorthands/#examples_1","title":"Examples","text":"<ul> <li>Heal/damage commands to specific entities</li> <li>UI updates for specific panels</li> <li>State changes for specific objects</li> </ul>"},{"location":"advanced/emit-shorthands/#emitfromsource--broadcast-from-source","title":"<code>EmitFrom(source)</code> \u2014 Broadcast from Source","text":"<p>When to use: Announcements where the source identity matters.</p> C#<pre><code>[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\nvar damage = new TookDamage(5);\ndamage.EmitFrom(enemy); // Anyone interested in this enemy receives it\n</code></pre>"},{"location":"advanced/emit-shorthands/#use-cases","title":"Use cases","text":"<ul> <li>Combat logging (\"Enemy X took damage\")</li> <li>Analytics (\"Track damage from specific boss\")</li> <li>Achievements (\"Listen for player kills\")</li> <li>Observer patterns (watch specific objects)</li> </ul>"},{"location":"advanced/emit-shorthands/#critical-gameobject-vs-component-targeting","title":"Critical: GameObject vs Component Targeting","text":"<p>In Unity, GameObject and Component are separate channels. This is the #1 cause of \"handler not firing\" bugs!</p>"},{"location":"advanced/emit-shorthands/#quick-example-the-this-trap","title":"Quick Example: The <code>this</code> Trap","text":"C#<pre><code>// \u274c COMMON MISTAKE\n_ = token.RegisterGameObjectTargeted<Heal>(gameObject, OnHeal);\nheal.EmitAt(this); // Won't work! 'this' is a Component, not a GameObject\n\n// \u2705 FIXES\n// Option 1: Both use GameObject\nheal.EmitAt(gameObject);\n\n// Option 2: Both use Component\n_ = token.RegisterComponentTargeted<Heal>(this, OnHeal);\nheal.EmitAt(this);\n</code></pre> <p>Remember: In Unity, <code>this</code> inside a <code>MonoBehaviour</code> is always a Component, not a GameObject!</p>"},{"location":"advanced/emit-shorthands/#global-observers-listen-to-all-events","title":"Global Observers: Listen to All Events","text":"<p>Feature: DxMessaging allows listening to all targeted or broadcast messages without knowing specific targets/sources.</p> C#<pre><code>// Listen to ALL heals, regardless of target\n_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\nvoid OnAnyHeal(ref InstanceId target, ref Heal msg)\n{\n Debug.Log($\"Someone healed {target} for {msg.amount}\");\n // You get WHO was healed as a parameter!\n}\n\n// Listen to ALL damage, regardless of source\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\nvoid OnAnyDamage(ref InstanceId source, ref TookDamage msg)\n{\n Debug.Log($\"{source} took {msg.amount} damage\");\n // You get WHO took damage as a parameter!\n}\n</code></pre>"},{"location":"advanced/emit-shorthands/#how-this-differs-from-some-event-bus-patterns","title":"How this differs from some event bus patterns","text":"<ul> <li>Per-entity subscription: Subscribe to PlayerDamaged, EnemyDamaged, NPCDamaged separately</li> <li>DxMessaging approach: Subscribe to all damage events with one registration</li> </ul> <p>Use cases: Analytics, debugging, achievements, global UI, combat logs</p>"},{"location":"advanced/emit-shorthands/#-for-the-full-deep-dive-on-gameobject-vs-component-targeting-and-global-observers-see-targeting--context","title":"\ud83d\udcd6 For the full deep dive on GameObject vs Component targeting and global observers, see Targeting & Context","text":""},{"location":"advanced/emit-shorthands/#best-practices","title":"Best Practices","text":""},{"location":"advanced/emit-shorthands/#use-explicit-helpers-recommended-for-unity","title":"Use Explicit Helpers (Recommended for Unity)","text":"<p>When clarity matters more than brevity, use the explicit methods:</p> C#<pre><code>// Clear intent: targeting GameObject\nheal.EmitGameObjectTargeted(gameObject);\n\n// Clear intent: targeting Component\nheal.EmitComponentTargeted(this);\n\n// Clear intent: broadcasting from GameObject\ndamage.EmitGameObjectBroadcast(gameObject);\n\n// Clear intent: broadcasting from Component\ndamage.EmitComponentBroadcast(this);\n</code></pre> <p>Pros: Self-documenting, impossible to mix GameObject/Component, great for code reviews</p> <p>Cons: More verbose</p>"},{"location":"advanced/emit-shorthands/#use-shorthands-recommended-for-non-unity","title":"Use Shorthands (Recommended for Non-Unity)","text":"<p>When you're working with <code>InstanceId</code> directly (tests, non-Unity systems):</p> C#<pre><code>InstanceId targetId = GetTarget();\nheal.EmitAt(targetId); // Clean and clear\n\nInstanceId sourceId = GetSource();\ndamage.EmitFrom(sourceId); // Clean and clear\n</code></pre> <p>Pros: Concise and readable, no GameObject/Component ambiguity</p> <p>Cons: In Unity code, requires explicit attention to GameObject vs Component</p>"},{"location":"advanced/emit-shorthands/#string-message-shorthands","title":"String Message Shorthands","text":"<p>DxMessaging includes three built-in string message types for rapid prototyping:</p> String Type Message Class Shorthand Use Case Global <code>GlobalStringMessage</code> <code>\"text\".Emit()</code> Debug notifications Targeted <code>StringMessage</code> <code>\"text\".EmitAt(target)</code> Commands to specific objects Broadcast <code>SourcedStringMessage</code> <code>\"text\".EmitFrom(source)</code> Announcements from specific objects"},{"location":"advanced/emit-shorthands/#examples_2","title":"Examples","text":"C#<pre><code>// Global notification\n\"Game Saved\".Emit();\n\n// Targeted message to GameObject\n\"Hello\".EmitAt(gameObject);\n\n// Or use explicit helpers\n\"Hello\".EmitGameObjectTargeted(gameObject); // More explicit\n\n// Broadcast from GameObject\n\"Died\".EmitFrom(gameObject);\n\n// Or use explicit helpers\n\"Died\".EmitGameObjectBroadcast(gameObject); // More explicit\n</code></pre>"},{"location":"advanced/emit-shorthands/#when-to-use-string-messages","title":"When to Use String Messages","text":""},{"location":"advanced/emit-shorthands/#good-for","title":"Good for","text":"<ul> <li>Rapid prototyping</li> <li>Debug logging</li> <li>Test utilities</li> <li>Tool scripts</li> </ul>"},{"location":"advanced/emit-shorthands/#not-good-for","title":"Not good for","text":"<ul> <li>Production gameplay code (use typed messages instead)</li> <li>Performance-critical paths</li> <li>Public APIs</li> </ul> <p>See String Messages for more details.</p>"},{"location":"advanced/emit-shorthands/#advanced-optional-bus-parameter","title":"Advanced: Optional Bus Parameter","text":"<p>All shorthands accept an optional <code>IMessageBus</code> parameter:</p> C#<pre><code>var localBus = new MessageBus();\n\nmsg.Emit(localBus); // Emit to specific bus\nmsg.EmitAt(target, localBus); // Target on specific bus\nmsg.EmitFrom(source, localBus); // Broadcast on specific bus\n</code></pre>"},{"location":"advanced/emit-shorthands/#when-to-use","title":"When to use","text":"<ul> <li>Testing with isolated buses</li> <li>Subsystems with their own message domains</li> <li>Advanced architecture patterns</li> </ul> <p>Default: If you don't provide a bus, <code>MessageHandler.MessageBus</code> (the global bus) is used.</p>"},{"location":"advanced/emit-shorthands/#unity-targeting-pitfalls","title":"Unity Targeting Pitfalls","text":"<p>Unity distinguishes between GameObjects and Components. Targeted and Broadcast messages must use the same context for both registration and emission.</p> <ul> <li>Registering on a GameObject requires emitting to a GameObject target</li> <li>Registering on a Component requires emitting to a Component target</li> <li>Prefer explicit helpers to avoid confusion:</li> <li><code>EmitGameObjectTargeted</code> / <code>EmitComponentTargeted</code></li> <li><code>EmitGameObjectBroadcast</code> / <code>EmitComponentBroadcast</code></li> </ul> <p>If a handler isn\u2019t firing, first suspect a GameObject vs Component mismatch. See the Troubleshooting checklist below.</p>"},{"location":"advanced/emit-shorthands/#troubleshooting","title":"Troubleshooting","text":""},{"location":"advanced/emit-shorthands/#my-handler-isnt-firing","title":"\"My handler isn't firing!\"","text":""},{"location":"advanced/emit-shorthands/#check-these-in-order","title":"Check these in order","text":"<ol> <li>GameObject vs Component mismatch?</li> <li>Did you register for a GameObject but emit to a Component (or vice versa)?</li> <li> <p>Use explicit helpers (<code>EmitGameObjectTargeted</code> vs <code>EmitComponentTargeted</code>) to eliminate this issue</p> </li> <li> <p>Is the handler enabled?</p> </li> <li>Check that <code>token.Enable()</code> was called (usually in <code>OnEnable</code>)</li> <li> <p>Verify the component is active</p> </li> <li> <p>Correct message type?</p> </li> <li>Untargeted messages use <code>Emit()</code></li> <li>Targeted messages use <code>EmitAt(target)</code></li> <li> <p>Broadcast messages use <code>EmitFrom(source)</code></p> </li> <li> <p>Registration succeeded?</p> </li> <li>Check that you assigned the return value (even <code>_</code>) to ensure registration happened</li> <li>Verify registration happened before the emission</li> </ol>"},{"location":"advanced/emit-shorthands/#how-do-i-debug-message-flow","title":"\"How do I debug message flow?\"","text":"<p>Use the built-in diagnostics:</p> C#<pre><code>// Add to any Unity component\nusing DxMessaging.Unity;\n\npublic class DebugListener : MessagingComponent\n{\n // Inspector will show recent emissions and registrations\n}\n</code></pre> <p>See Diagnostics for more debugging tools.</p>"},{"location":"advanced/emit-shorthands/#comparison-shorthands-vs-explicit-helpers","title":"Comparison: Shorthands vs Explicit Helpers","text":"Scenario Shorthand Explicit Helper Untargeted/Global <code>msg.Emit()</code> <code>msg.EmitUntargeted()</code> Targeted to GameObject <code>msg.EmitAt(gameObject)</code> <code>msg.EmitGameObjectTargeted(gameObject)</code> Targeted to Component <code>msg.EmitAt(component)</code> <code>msg.EmitComponentTargeted(component)</code> Broadcast from GameObject <code>msg.EmitFrom(gameObject)</code> <code>msg.EmitGameObjectBroadcast(gameObject)</code> Broadcast from Component <code>msg.EmitFrom(component)</code> <code>msg.EmitComponentBroadcast(component)</code> Non-Unity InstanceId <code>msg.EmitAt(id)</code> <code>msg.EmitTargeted(id)</code>"},{"location":"advanced/emit-shorthands/#our-recommendation","title":"Our recommendation","text":"<ul> <li>In Unity gameplay code: Use explicit helpers for clarity and safety</li> <li>In tests and non-Unity code: Use shorthands for brevity</li> <li>In examples and documentation: Use shorthands for readability</li> </ul>"},{"location":"advanced/emit-shorthands/#complete-example","title":"Complete Example","text":"<p>Here's a full example showing all three shorthand types:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing UnityEngine;\n\n// Message definitions\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct WaveStarted { public readonly int waveNumber; }\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SpawnEnemy { public readonly string enemyType; }\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct EnemyDied { public readonly int score; }\n\n// Producer\npublic class GameManager : MonoBehaviour\n{\n void StartWave(int wave)\n {\n // Global broadcast: everyone should know\n var waveMsg = new WaveStarted(wave);\n waveMsg.Emit();\n\n // Targeted: tell specific spawner what to spawn\n var spawnMsg = new SpawnEnemy(\"Goblin\");\n spawnMsg.EmitGameObjectTargeted(spawnerObject);\n }\n}\n\n// Consumer\npublic class Enemy : MonoBehaviour\n{\n void Die()\n {\n // Broadcast from this enemy: anyone watching can react\n var deathMsg = new EnemyDied(100);\n deathMsg.EmitGameObjectBroadcast(gameObject);\n }\n}\n\n// Listeners\npublic class UIManager : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n _ = Token.RegisterUntargeted<WaveStarted>(OnWaveStarted);\n }\n\n void OnWaveStarted(ref WaveStarted msg)\n {\n Debug.Log($\"Wave {msg.waveNumber} started!\");\n }\n}\n\npublic class AchievementTracker : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n // Listen to ALL enemy deaths (no specific source)\n _ = Token.RegisterBroadcastWithoutSource<EnemyDied>(OnAnyEnemyDied);\n }\n\n void OnAnyEnemyDied(ref InstanceId source, ref EnemyDied msg)\n {\n Debug.Log($\"Enemy {source} died for {msg.score} points\");\n }\n}\n</code></pre>"},{"location":"advanced/emit-shorthands/#see-also","title":"See Also","text":"<ul> <li>Quick Reference \u2014 API cheat sheet for all emit methods</li> <li>Message Types \u2014 Understand Untargeted, Targeted, and Broadcast messages</li> <li>Targeting & Context \u2014 Deep dive into GameObject vs Component</li> <li>String Messages \u2014 More about string message helpers</li> <li>Diagnostics \u2014 Debugging tools and Inspector integration</li> </ul>"},{"location":"advanced/message-bus-providers/","title":"Message Bus Providers","text":"<p>DxMessaging provides a flexible provider system that lets you configure which message bus drives your components. This works both at design time (with ScriptableObject providers) and at runtime (with provider handles).</p> <p>This guide covers:</p> <ul> <li>The <code>IMessageBusProvider</code> interface</li> <li>Built-in ScriptableObject providers</li> <li>The <code>MessageBusProviderHandle</code> system</li> <li>How to create custom providers</li> <li>Practical usage patterns</li> </ul>"},{"location":"advanced/message-bus-providers/#table-of-contents","title":"Table of Contents","text":"<ul> <li>Overview</li> <li>IMessageBusProvider Interface</li> <li>Built-in Providers</li> <li>Current Global Message Bus Provider</li> <li>Initial Global Message Bus Provider</li> <li>MessageBusProviderHandle</li> <li>Using Providers with Components</li> <li>Creating Custom Providers</li> <li>Common Patterns</li> </ul>"},{"location":"advanced/message-bus-providers/#overview","title":"Overview","text":"<p>Providers abstract away the details of how a message bus is resolved. This lets you:</p> <ul> <li>Swap buses at design time \u2014 Change a ScriptableObject reference without modifying code</li> <li>Integrate with DI containers \u2014 Resolve buses from your container</li> <li>Support runtime reconfiguration \u2014 Change which bus a component uses dynamically</li> <li>Isolate scenes or features \u2014 Use different buses for different parts of your game</li> </ul> <p>If you're using the default global bus everywhere, you probably don't need providers. They're most useful when you need flexibility or are integrating with DI frameworks.</p>"},{"location":"advanced/message-bus-providers/#imessagebusprovider-interface","title":"IMessageBusProvider Interface","text":"<p>All providers implement this simple interface:</p> C#<pre><code>public interface IMessageBusProvider\n{\n IMessageBus Resolve();\n}\n</code></pre> <p>When a component needs a bus, it calls <code>Resolve()</code> on its provider. The provider returns the appropriate bus instance.</p> <p>Key insight: By abstracting bus resolution behind this interface, components don't need to know whether they're using the global bus, a container-managed bus, or something completely custom.</p>"},{"location":"advanced/message-bus-providers/#built-in-providers","title":"Built-in Providers","text":"<p>DxMessaging ships with two ScriptableObject-based providers. These are assets you can create in the editor and reference in your scenes.</p>"},{"location":"advanced/message-bus-providers/#current-global-message-bus-provider","title":"Current Global Message Bus Provider","text":"<p><code>CurrentGlobalMessageBusProvider</code> returns whatever bus is currently set as the global bus via <code>MessageHandler.MessageBus</code>. If you override the global bus at runtime, this provider reflects that change.</p>"},{"location":"advanced/message-bus-providers/#creating-the-asset","title":"Creating the asset","text":"<p>Right-click in the Project window:</p> Text Only<pre><code>Create > Wallstop Studios > DxMessaging > Message Bus Providers > Current Global Message Bus\n</code></pre>"},{"location":"advanced/message-bus-providers/#when-to-use-this","title":"When to use this","text":"<ul> <li>When you want components to follow the global bus, even if it changes</li> <li>In DI scenarios where the global bus is replaced during bootstrap</li> <li>For components that should always use the \"active\" bus</li> </ul>"},{"location":"advanced/message-bus-providers/#example","title":"Example","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing UnityEngine;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class MessagingComponentConfigurator : MonoBehaviour\n{\n [SerializeField]\n private CurrentGlobalMessageBusProvider provider;\n\n private void Awake()\n {\n MessagingComponent component = GetComponent<MessagingComponent>();\n component.Configure(provider, MessageBusRebindMode.RebindActive);\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#initial-global-message-bus-provider","title":"Initial Global Message Bus Provider","text":"<p><code>InitialGlobalMessageBusProvider</code> always returns the original startup bus that was created during static initialization. It ignores any calls to <code>SetGlobalMessageBus()</code> or <code>OverrideGlobalMessageBus()</code>.</p>"},{"location":"advanced/message-bus-providers/#creating-the-asset_1","title":"Creating the asset","text":"<p>Right-click in the Project window:</p> Text Only<pre><code>Create > Wallstop Studios > DxMessaging > Message Bus Providers > Initial Global Message Bus\n</code></pre>"},{"location":"advanced/message-bus-providers/#when-to-use-this_1","title":"When to use this","text":"<ul> <li>In diagnostic tools that need a stable reference point</li> <li>When testing with temporary bus overrides but need access to the original</li> <li>For debugging scenarios where you want to compare against the startup bus</li> </ul>"},{"location":"advanced/message-bus-providers/#example_1","title":"Example","text":"C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\npublic class DiagnosticLogger : MonoBehaviour\n{\n [SerializeField]\n private InitialGlobalMessageBusProvider initialProvider;\n\n private void Start()\n {\n // Always logs to the original bus, even if global bus is overridden\n IMessageBus startupBus = initialProvider.Resolve();\n\n // Can compare with current global bus\n bool busWasReplaced = startupBus != MessageHandler.MessageBus;\n Debug.Log($\"Bus replaced: {busWasReplaced}\");\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#messagebusproviderhandle","title":"MessageBusProviderHandle","text":"<p><code>MessageBusProviderHandle</code> is a serializable struct that can reference either:</p> <ul> <li>A ScriptableObject provider (design-time configuration)</li> <li>A runtime provider instance (runtime configuration)</li> </ul> <p>This gives you the best of both worlds: editor-friendly assets and runtime flexibility.</p>"},{"location":"advanced/message-bus-providers/#key-methods","title":"Key Methods","text":"C#<pre><code>// Create from a runtime provider\nMessageBusProviderHandle handle = MessageBusProviderHandle.FromProvider(myProvider);\n\n// Associate a runtime provider with an existing handle\nhandle = handle.WithRuntimeProvider(myRuntimeProvider);\n\n// Resolve the provider (runtime takes precedence over asset)\nif (handle.TryGetProvider(out IMessageBusProvider provider))\n{\n IMessageBus bus = provider.Resolve();\n}\n\n// Or resolve the bus directly\nIMessageBus bus = handle.ResolveBus();\n</code></pre>"},{"location":"advanced/message-bus-providers/#how-it-works","title":"How It Works","text":"<p>The handle has two fields:</p> <ol> <li><code>Provider</code> \u2014 A serialized reference to a ScriptableObject provider (visible in Inspector)</li> <li>A runtime provider instance (not serialized)</li> </ol> <p>When you call <code>TryGetProvider()</code> or <code>ResolveBus()</code>, it checks:</p> <ol> <li>Is there a runtime provider? Use that first.</li> <li>Otherwise, use the serialized ScriptableObject provider.</li> <li>If neither exists, return null or the global default.</li> </ol> <p>Why this matters: You can design your prefabs with ScriptableObject references, then override them at runtime with DI-provided buses. No code changes needed.</p>"},{"location":"advanced/message-bus-providers/#example-design-time-configuration","title":"Example: Design Time Configuration","text":"C#<pre><code>using DxMessaging.Unity;\nusing UnityEngine;\n\npublic class PlayerController : MessageAwareComponent\n{\n [SerializeField]\n private MessageBusProviderHandle providerHandle;\n\n protected override void Awake()\n {\n // Configure with the handle before base.Awake()\n if (providerHandle.TryGetProvider(out var provider))\n {\n ConfigureMessageBus(provider, MessageBusRebindMode.RebindActive);\n }\n\n base.Awake();\n }\n}\n</code></pre> <p>In the Inspector, you can drag a <code>CurrentGlobalMessageBusProvider</code> or <code>InitialGlobalMessageBusProvider</code> asset onto the <code>providerHandle</code> field.</p>"},{"location":"advanced/message-bus-providers/#example-runtime-override","title":"Example: Runtime Override","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\n\npublic class DynamicConfigurator\n{\n public void ConfigureWithRuntimeBus(MessageAwareComponent component, IMessageBus runtimeBus)\n {\n // Create a runtime provider\n var provider = new RuntimeMessageBusProvider(runtimeBus);\n\n // Wrap it in a handle\n var handle = MessageBusProviderHandle.FromProvider(provider);\n\n // Configure the component\n component.ConfigureMessageBus(handle, MessageBusRebindMode.RebindActive);\n }\n}\n\n// Simple runtime provider implementation\npublic class RuntimeMessageBusProvider : IMessageBusProvider\n{\n private readonly IMessageBus _bus;\n\n public RuntimeMessageBusProvider(IMessageBus bus)\n {\n _bus = bus;\n }\n\n public IMessageBus Resolve() => _bus;\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#using-providers-with-components","title":"Using Providers with Components","text":""},{"location":"advanced/message-bus-providers/#messagingcomponent","title":"MessagingComponent","text":"<p><code>MessagingComponent</code> has three <code>Configure()</code> overloads:</p> C#<pre><code>// Direct bus reference\ncomponent.Configure(messageBus, MessageBusRebindMode.RebindActive);\n\n// Provider interface\ncomponent.Configure(provider, MessageBusRebindMode.RebindActive);\n\n// Provider handle (design-time or runtime)\ncomponent.Configure(providerHandle, MessageBusRebindMode.RebindActive);\n</code></pre>"},{"location":"advanced/message-bus-providers/#messageawarecomponent","title":"MessageAwareComponent","text":"<p><code>MessageAwareComponent</code> exposes <code>ConfigureMessageBus()</code> with the same three overloads:</p> C#<pre><code>// Direct bus\nConfigureMessageBus(messageBus, MessageBusRebindMode.RebindActive);\n\n// Provider\nConfigureMessageBus(provider, MessageBusRebindMode.RebindActive);\n\n// Handle\nConfigureMessageBus(providerHandle, MessageBusRebindMode.RebindActive);\n</code></pre> <p>Best practice: Call <code>ConfigureMessageBus()</code> in <code>Awake()</code> before calling <code>base.Awake()</code> to ensure the bus is set before message handlers are registered.</p>"},{"location":"advanced/message-bus-providers/#messagingcomponentinstaller","title":"MessagingComponentInstaller","text":"<p><code>MessagingComponentInstaller</code> configures all <code>MessagingComponent</code> descendants in a hierarchy:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing UnityEngine;\n\npublic class SceneSetup : MonoBehaviour\n{\n [SerializeField]\n private MessagingComponentInstaller installer;\n\n [SerializeField]\n private CurrentGlobalMessageBusProvider provider;\n\n private void Awake()\n {\n // Configure installer with provider\n installer.SetProvider(MessageBusProviderHandle.FromProvider(provider));\n\n // Apply to all child MessagingComponents\n installer.ApplyConfiguration();\n }\n}\n</code></pre> <p>This is useful when you want to configure an entire prefab hierarchy or scene section with a single provider.</p>"},{"location":"advanced/message-bus-providers/#creating-custom-providers","title":"Creating Custom Providers","text":"<p>You can create your own providers for advanced scenarios:</p>"},{"location":"advanced/message-bus-providers/#example-container-managed-provider","title":"Example: Container-Managed Provider","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\n\npublic class ContainerMessageBusProvider : IMessageBusProvider\n{\n private readonly IDependencyContainer _container;\n\n public ContainerMessageBusProvider(IDependencyContainer container)\n {\n _container = container;\n }\n\n public IMessageBus Resolve()\n {\n // Resolve from container each time\n return _container.Resolve<IMessageBus>();\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#example-scriptableobject-provider-for-specific-bus","title":"Example: ScriptableObject Provider for Specific Bus","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\n[CreateAssetMenu(menuName = \"Game/Messaging/Custom Bus Provider\")]\npublic class CustomBusProvider : ScriptableObject, IMessageBusProvider\n{\n [SerializeField]\n private bool useTestBus;\n\n private static IMessageBus _testBus;\n private static IMessageBus _productionBus;\n\n public IMessageBus Resolve()\n {\n if (useTestBus)\n {\n return _testBus ??= new MessageBus();\n }\n return _productionBus ??= new MessageBus();\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#example-lazy-initialized-provider","title":"Example: Lazy-Initialized Provider","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\n\npublic class LazyMessageBusProvider : IMessageBusProvider\n{\n private IMessageBus _cachedBus;\n\n public IMessageBus Resolve()\n {\n // Only create the bus when first requested\n return _cachedBus ??= new MessageBus();\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#common-patterns","title":"Common Patterns","text":""},{"location":"advanced/message-bus-providers/#pattern-1-design-time-configuration","title":"Pattern 1: Design-Time Configuration","text":"<p>Set up providers in the editor, no code needed:</p> <ol> <li>Create a <code>CurrentGlobalMessageBusProvider</code> asset in your project</li> <li>Add a <code>MessagingComponent</code> to your prefab</li> <li>Add a <code>MessagingComponentConfigurator</code> script that references the provider</li> <li>In Awake, call <code>component.Configure(provider, ...)</code></li> </ol> <p>This lets designers swap providers without touching code.</p>"},{"location":"advanced/message-bus-providers/#pattern-2-di-container-integration","title":"Pattern 2: DI Container Integration","text":"<p>Use a provider to resolve buses from your container:</p> C#<pre><code>// In your DI installer\ncontainer.RegisterInstance<IMessageBus>(new MessageBus());\n\n// In a bootstrap script\nvar provider = new ContainerMessageBusProvider(container);\nMessageHandler.SetGlobalMessageBus(provider.Resolve());\n\n// Now all components use the container bus\n</code></pre>"},{"location":"advanced/message-bus-providers/#pattern-3-scene-scoped-buses","title":"Pattern 3: Scene-Scoped Buses","text":"<p>Create a provider that returns a scene-specific bus:</p> C#<pre><code>public class SceneMessageBusManager : MonoBehaviour\n{\n private IMessageBus _sceneBus;\n\n private void Awake()\n {\n // Create scene-local bus\n _sceneBus = new MessageBus();\n\n // Configure all components in this scene\n var components = GetComponentsInChildren<MessagingComponent>();\n var provider = new RuntimeMessageBusProvider(_sceneBus);\n\n foreach (var component in components)\n {\n component.Configure(provider, MessageBusRebindMode.RebindActive);\n }\n }\n}\n</code></pre> <p>Messages sent in this scene stay in this scene.</p>"},{"location":"advanced/message-bus-providers/#pattern-4-runtime-provider-override","title":"Pattern 4: Runtime Provider Override","text":"<p>Start with a design-time provider, override at runtime:</p> C#<pre><code>public class RuntimeReconfiguration : MonoBehaviour\n{\n [SerializeField]\n private MessageAwareComponent component;\n\n [SerializeField]\n private MessageBusProviderHandle designTimeHandle; // Set in Inspector\n\n private void Start()\n {\n // Use design-time provider initially\n component.ConfigureMessageBus(designTimeHandle, MessageBusRebindMode.RebindActive);\n }\n\n public void SwitchToRuntimeBus(IMessageBus runtimeBus)\n {\n // Override with runtime provider\n var runtimeProvider = new RuntimeMessageBusProvider(runtimeBus);\n var handle = MessageBusProviderHandle.FromProvider(runtimeProvider);\n component.ConfigureMessageBus(handle, MessageBusRebindMode.RebindActive);\n }\n}\n</code></pre>"},{"location":"advanced/message-bus-providers/#quick-reference","title":"Quick Reference","text":"Type Purpose Use Case <code>IMessageBusProvider</code> Interface for bus resolution Create custom providers <code>CurrentGlobalMessageBusProvider</code> ScriptableObject returning current global bus Follow the active global bus <code>InitialGlobalMessageBusProvider</code> ScriptableObject returning startup bus Diagnostics, stable reference <code>MessageBusProviderHandle</code> Serializable wrapper for providers Design-time + runtime flexibility <code>handle.FromProvider(provider)</code> Create handle from runtime provider Runtime configuration <code>handle.WithRuntimeProvider(provider)</code> Add runtime provider to handle Override design-time config <code>handle.TryGetProvider(out provider)</code> Resolve provider from handle Get the actual provider <code>handle.ResolveBus()</code> Resolve bus directly Shortcut to get bus"},{"location":"advanced/message-bus-providers/#see-also","title":"See Also","text":"<ul> <li>Runtime Configuration \u2014 Setting and overriding global buses, re-binding registrations</li> <li>Registration Builders \u2014 Fluent API for building message registrations with priority and lifecycle control</li> <li>DI Integration Guides \u2014 Zenject, VContainer, and Reflex integration patterns</li> <li>Unity Integration \u2014 MessagingComponent and MessageAwareComponent deep dive</li> <li>Back to Documentation Hub \u2014 Browse all docs</li> </ul>"},{"location":"advanced/registration-builders/","title":"Registration Builders","text":"<p>The <code>MessageRegistrationBuilder</code> provides a structured way to create message registrations with fine-grained control over lifecycle, configuration, and resource cleanup.</p> <p>This guide covers:</p> <ul> <li>When to use the builder pattern</li> <li>Configuring registrations with options</li> <li>Managing lifecycle with leases</li> <li>Practical usage examples</li> </ul>"},{"location":"advanced/registration-builders/#table-of-contents","title":"Table of Contents","text":"<ul> <li>Overview</li> <li>MessageRegistrationBuilder</li> <li>MessageRegistrationBuildOptions</li> <li>MessageRegistrationLease</li> <li>MessageRegistrationLifecycle</li> <li>Code Examples</li> <li>See Also</li> </ul>"},{"location":"advanced/registration-builders/#overview","title":"Overview","text":"<p>DxMessaging offers two approaches to message registration:</p> Approach Use Case Direct token creation Simple scenarios with manual lifecycle management Builder pattern Complex scenarios requiring DI integration, lifecycle hooks, or structured configuration"},{"location":"advanced/registration-builders/#when-to-use-the-builder-pattern","title":"When to use the builder pattern","text":"<ul> <li>You need lifecycle callbacks (build, activate, deactivate, dispose)</li> <li>You want to integrate with dependency injection containers</li> <li>You prefer a more declarative configuration style</li> <li>You need to manage multiple registrations as a single unit</li> </ul>"},{"location":"advanced/registration-builders/#when-direct-token-creation-is-sufficient","title":"When direct token creation is sufficient","text":"<ul> <li>Simple MonoBehaviour-based messaging</li> <li>No need for lifecycle hooks</li> <li>Using <code>MessagingComponent</code> or similar built-in components</li> </ul>"},{"location":"advanced/registration-builders/#messageregistrationbuilder","title":"MessageRegistrationBuilder","text":"<p>The <code>MessageRegistrationBuilder</code> class creates <code>MessageRegistrationLease</code> instances based on configuration options.</p>"},{"location":"advanced/registration-builders/#creating-a-builder","title":"Creating a Builder","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Builder that uses global bus resolution\nMessageRegistrationBuilder builder = new MessageRegistrationBuilder();\n\n// Builder with a custom provider\nIMessageBusProvider provider = new FixedMessageBusProvider(myMessageBus);\nMessageRegistrationBuilder builderWithProvider = new MessageRegistrationBuilder(provider);\n</code></pre>"},{"location":"advanced/registration-builders/#building-leases","title":"Building Leases","text":"<p>Call <code>Build()</code> with options to create a lease:</p> C#<pre><code>MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions\n{\n ActivateOnBuild = true,\n Configure = token =>\n {\n _ = token.RegisterUntargeted<PlayerDamaged>(OnPlayerDamaged);\n }\n};\n\nusing MessageRegistrationLease lease = builder.Build(options);\n</code></pre>"},{"location":"advanced/registration-builders/#messageregistrationbuildoptions","title":"MessageRegistrationBuildOptions","text":"<p>Configure how the builder creates registrations using <code>MessageRegistrationBuildOptions</code>.</p>"},{"location":"advanced/registration-builders/#owner-configuration","title":"Owner Configuration","text":"<p>Specify who owns the registration:</p> C#<pre><code>// Explicit InstanceId owner\noptions.Owner = new InstanceId(42);\n\n// Unity Object owner (Unity 2021.3+)\noptions.UnityOwner = gameObject; // or any Component\n</code></pre> <p>When omitted, a synthetic owner ID is generated automatically. <code>UnityOwner</code> takes precedence over <code>Owner</code> when both are set.</p>"},{"location":"advanced/registration-builders/#message-bus-selection","title":"Message Bus Selection","text":"<p>Control which bus handles registrations:</p> C#<pre><code>// Use a specific bus directly\noptions.PreferredMessageBus = myMessageBus;\n\n// Use a provider (falls back if PreferredMessageBus is null)\noptions.MessageBusProvider = new FixedMessageBusProvider(otherBus);\n</code></pre> <p>Resolution order:</p> <ol> <li><code>PreferredMessageBus</code> if set</li> <li><code>MessageBusProvider.Resolve()</code> if provider is set</li> <li>Builder's provider (from constructor) if set</li> <li><code>null</code> (uses global bus)</li> </ol>"},{"location":"advanced/registration-builders/#handler-state","title":"Handler State","text":"<p>Control the initial state of handlers:</p> C#<pre><code>// Whether the underlying MessageHandler starts active (default: true)\noptions.HandlerStartsActive = true;\n\n// Whether to call Enable() immediately after building (default: false)\noptions.ActivateOnBuild = true;\n</code></pre>"},{"location":"advanced/registration-builders/#diagnostics","title":"Diagnostics","text":"<p>Enable diagnostic mode for debugging:</p> C#<pre><code>options.EnableDiagnostics = true;\n</code></pre> <p>This sets <code>MessageRegistrationToken.DiagnosticMode</code> to <code>true</code>, enabling detailed logging.</p>"},{"location":"advanced/registration-builders/#configure-callback","title":"Configure Callback","text":"<p>Register handlers immediately after token creation:</p> C#<pre><code>options.Configure = token =>\n{\n _ = token.RegisterUntargeted<GameStarted>(OnGameStarted);\n // For targeted messages, provide the target InstanceId\n _ = token.RegisterTargeted<DamageMessage>(targetInstanceId, OnDamage);\n};\n</code></pre> <p>This callback runs after the token is created but before lifecycle hooks and activation.</p> <p>Note: <code>RegisterTargeted<T></code> requires an <code>InstanceId</code> target parameter specifying which entity should receive the message. For Unity objects, use <code>RegisterGameObjectTargeted<T></code> or <code>RegisterComponentTargeted<T></code> instead.</p>"},{"location":"advanced/registration-builders/#lifecycle-hooks","title":"Lifecycle Hooks","text":"<p>Add callbacks for lifecycle events:</p> C#<pre><code>options.Lifecycle = new MessageRegistrationLifecycle(\n onBuild: token => Debug.Log(\"Token created\"),\n onActivate: token => Debug.Log(\"Token enabled\"),\n onDeactivate: token => Debug.Log(\"Token disabled\"),\n onDispose: token => Debug.Log(\"Token disposed\")\n);\n</code></pre>"},{"location":"advanced/registration-builders/#messageregistrationlease","title":"MessageRegistrationLease","text":"<p>A <code>MessageRegistrationLease</code> wraps the created token and provides lifecycle management.</p>"},{"location":"advanced/registration-builders/#properties","title":"Properties","text":"Property Type Description <code>Token</code> <code>MessageRegistrationToken</code> The underlying registration token <code>Handler</code> <code>MessageHandler</code> The handler hosting registrations <code>MessageBus</code> <code>IMessageBus</code> The bus used for registrations <code>Owner</code> <code>InstanceId</code> The owner identifier <code>IsActive</code> <code>bool</code> Whether the lease is currently active"},{"location":"advanced/registration-builders/#activation-methods","title":"Activation Methods","text":"C#<pre><code>// Enable the token and invoke OnActivate callback\nlease.Activate();\n\n// Disable the token and invoke OnDeactivate callback\nlease.Deactivate();\n</code></pre> <ul> <li><code>Activate()</code> throws <code>ObjectDisposedException</code> if called after disposal</li> <li><code>Deactivate()</code> is safe to call multiple times or after disposal</li> </ul>"},{"location":"advanced/registration-builders/#dispose-pattern","title":"Dispose Pattern","text":"<p>Leases implement <code>IDisposable</code>:</p> C#<pre><code>using MessageRegistrationLease lease = builder.Build(options);\n// ... use the lease\n// Automatically deactivated and disposed at end of scope\n</code></pre> <p>Disposal sequence:</p> <ol> <li>Calls <code>Deactivate()</code> if active (triggers <code>OnDeactivate</code>)</li> <li>Invokes <code>OnDispose</code> callback</li> <li>Marks lease as disposed</li> </ol>"},{"location":"advanced/registration-builders/#messageregistrationlifecycle","title":"MessageRegistrationLifecycle","text":"<p>The <code>MessageRegistrationLifecycle</code> struct holds callbacks for each lifecycle stage:</p> C#<pre><code>public readonly struct MessageRegistrationLifecycle\n{\n public MessageRegistrationLifecycle(\n Action<MessageRegistrationToken> onBuild,\n Action<MessageRegistrationToken> onActivate,\n Action<MessageRegistrationToken> onDeactivate,\n Action<MessageRegistrationToken> onDispose);\n\n public Action<MessageRegistrationToken> OnBuild { get; }\n public Action<MessageRegistrationToken> OnActivate { get; }\n public Action<MessageRegistrationToken> OnDeactivate { get; }\n public Action<MessageRegistrationToken> OnDispose { get; }\n}\n</code></pre>"},{"location":"advanced/registration-builders/#callback-order","title":"Callback Order","text":"<ol> <li><code>OnBuild</code> \u2014 Immediately after lease creation, before activation</li> <li><code>OnActivate</code> \u2014 When <code>Activate()</code> is called (or automatically if <code>ActivateOnBuild = true</code>)</li> <li><code>OnDeactivate</code> \u2014 When <code>Deactivate()</code> is called or during disposal while active</li> <li><code>OnDispose</code> \u2014 During <code>Dispose()</code>, after deactivation</li> </ol> <p>All callbacks receive the <code>MessageRegistrationToken</code> as a parameter.</p>"},{"location":"advanced/registration-builders/#code-examples","title":"Code Examples","text":""},{"location":"advanced/registration-builders/#basic-builder-usage","title":"Basic Builder Usage","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\npublic sealed class BasicBuilderExample : MonoBehaviour\n{\n private MessageRegistrationBuilder builder;\n private MessageRegistrationLease lease;\n\n private void Awake()\n {\n builder = new MessageRegistrationBuilder();\n\n MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions\n {\n UnityOwner = this,\n ActivateOnBuild = true,\n Configure = token =>\n {\n _ = token.RegisterUntargeted<GameEvent>(OnGameEvent);\n }\n };\n\n lease = builder.Build(options);\n }\n\n private void OnDestroy()\n {\n lease?.Dispose();\n }\n\n // Action<T> handler signature (used with RegisterUntargeted<T>(..., Action<T>))\n private void OnGameEvent(GameEvent message)\n {\n Debug.Log($\"Received: {message}\");\n }\n\n // Alternative: FastHandler<T> signature for better performance (avoids boxing)\n // private void OnGameEvent(ref GameEvent message)\n // {\n // Debug.Log($\"Received: {message}\");\n // }\n}\n</code></pre>"},{"location":"advanced/registration-builders/#di-integration-pattern","title":"DI Integration Pattern","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\npublic sealed class DIIntegrationExample : MonoBehaviour\n{\n // Injected by your DI container\n private IMessageBusProvider messageBusProvider;\n private MessageRegistrationLease lease;\n\n public void Initialize(IMessageBusProvider provider)\n {\n messageBusProvider = provider;\n\n MessageRegistrationBuilder builder = new MessageRegistrationBuilder(provider);\n\n MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions\n {\n UnityOwner = this,\n ActivateOnBuild = true,\n Configure = ConfigureRegistrations\n };\n\n lease = builder.Build(options);\n }\n\n private void ConfigureRegistrations(MessageRegistrationToken token)\n {\n _ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);\n _ = token.RegisterUntargeted<PlayerDied>(OnPlayerDied);\n }\n\n private void OnDestroy()\n {\n lease?.Dispose();\n }\n\n // Action<T> handler signatures\n private void OnPlayerSpawned(PlayerSpawned message)\n {\n Debug.Log($\"Player spawned: {message.PlayerId}\");\n }\n\n private void OnPlayerDied(PlayerDied message)\n {\n Debug.Log($\"Player died: {message.PlayerId}\");\n }\n}\n</code></pre>"},{"location":"advanced/registration-builders/#custom-lifecycle-hooks","title":"Custom Lifecycle Hooks","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\npublic sealed class LifecycleHooksExample : MonoBehaviour\n{\n private MessageRegistrationBuilder builder;\n private MessageRegistrationLease lease;\n\n private void Awake()\n {\n builder = new MessageRegistrationBuilder();\n\n MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions\n {\n UnityOwner = this,\n HandlerStartsActive = true,\n ActivateOnBuild = false, // We'll activate manually\n EnableDiagnostics = true,\n Configure = token =>\n {\n _ = token.RegisterUntargeted<LevelLoaded>(OnLevelLoaded);\n },\n Lifecycle = new MessageRegistrationLifecycle(\n onBuild: token => Debug.Log(\"[Lifecycle] Token built\"),\n onActivate: token => Debug.Log(\"[Lifecycle] Activated - now receiving messages\"),\n onDeactivate: token => Debug.Log(\"[Lifecycle] Deactivated - paused\"),\n onDispose: token => Debug.Log(\"[Lifecycle] Disposed - cleanup complete\")\n )\n };\n\n lease = builder.Build(options);\n }\n\n private void OnEnable()\n {\n // Activate when the component is enabled\n lease?.Activate();\n }\n\n private void OnDisable()\n {\n // Deactivate but don't dispose - we might re-enable\n lease?.Deactivate();\n }\n\n private void OnDestroy()\n {\n lease?.Dispose();\n }\n\n // Action<T> handler signature\n private void OnLevelLoaded(LevelLoaded message)\n {\n Debug.Log($\"Level loaded: {message.LevelName}\");\n }\n}\n</code></pre>"},{"location":"advanced/registration-builders/#fixedmessagebusprovider","title":"FixedMessageBusProvider","text":"<p>For scenarios where you always want to use a specific bus instance:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Create a provider that always returns a specific bus\nIMessageBus myBus = new MessageBus();\nIMessageBusProvider provider = new FixedMessageBusProvider(myBus);\n\n// Use with builder\nMessageRegistrationBuilder builder = new MessageRegistrationBuilder(provider);\n\n// Or override per-build\nMessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions\n{\n MessageBusProvider = provider,\n Configure = token => { /* ... */ }\n};\n</code></pre>"},{"location":"advanced/registration-builders/#see-also","title":"See Also","text":"<ul> <li>Message Bus Providers \u2014 More on the provider system</li> <li>Runtime Configuration \u2014 Dynamic reconfiguration options</li> </ul>"},{"location":"advanced/runtime-configuration/","title":"Runtime Message Bus Configuration","text":"<p>This guide covers how to configure message buses at runtime and retarget existing registrations. These features enable advanced scenarios like dependency injection, testing with isolated buses, and dynamic reconfiguration of messaging hierarchies.</p>"},{"location":"advanced/runtime-configuration/#table-of-contents","title":"Table of Contents","text":"<ul> <li>Runtime Message Bus Configuration</li> <li>When You Need This</li> <li>Global Message Bus Management</li> <li>Re-binding Registrations</li> <li>Common Patterns</li> </ul>"},{"location":"advanced/runtime-configuration/#when-you-need-this","title":"When You Need This","text":"<p>You'll use runtime configuration when you need to:</p> <ul> <li>Integrate with DI containers \u2014 Replace the global bus with a container-managed instance</li> <li>Isolate tests \u2014 Ensure each test uses its own bus to prevent interference</li> <li>Support multiple game modes \u2014 Use different buses for different gameplay contexts (e.g., main game vs. mini-games)</li> <li>Dynamically reconfigure components \u2014 Change which bus a component listens to after it's been created</li> </ul> <p>If you're just getting started with DxMessaging, you probably don't need these features yet. The default global bus works great for most scenarios.</p>"},{"location":"advanced/runtime-configuration/#global-message-bus-management","title":"Global Message Bus Management","text":"<p>By default, DxMessaging creates a single process-wide message bus during static initialization. This bus is available through <code>MessageHandler.MessageBus</code> and works great for most scenarios.</p>"},{"location":"advanced/runtime-configuration/#replacing-the-global-bus","title":"Replacing the Global Bus","text":"<p>Use <code>SetGlobalMessageBus()</code> when you want to permanently replace the default bus:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n// Create a custom bus (often from a DI container)\nIMessageBus customBus = new MessageBus();\n\n// Replace the global bus\nMessageHandler.SetGlobalMessageBus(customBus);\n\n// All new registrations will now use customBus\nvar handler = new MessageHandler(new InstanceId(1)) { active = true };\n// handler.MessageBus is now customBus\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this","title":"When to use this","text":"<ul> <li>During application startup when integrating with DI containers</li> <li>When you want all message traffic to flow through a specific bus instance</li> <li>In integration tests that need complete control over message routing</li> </ul> <p>Important: This permanently replaces the global bus for the lifetime of the application. All subsequent registrations will use the new bus unless explicitly configured otherwise.</p>"},{"location":"advanced/runtime-configuration/#temporarily-overriding-the-global-bus","title":"Temporarily Overriding the Global Bus","text":"<p>Use <code>OverrideGlobalMessageBus()</code> when you want to temporarily replace the bus and automatically restore it later:</p> C#<pre><code>IMessageBus originalBus = MessageHandler.MessageBus;\nIMessageBus temporaryBus = new MessageBus();\n\n// Temporarily override\nusing (MessageHandler.OverrideGlobalMessageBus(temporaryBus))\n{\n // Inside this scope, MessageHandler.MessageBus returns temporaryBus\n Assert.AreSame(temporaryBus, MessageHandler.MessageBus);\n\n // Any message handlers created here use temporaryBus\n var msg = new MyMessage();\n msg.EmitUntargeted(); // Goes to temporaryBus\n}\n\n// Outside the scope, the original bus is restored\nAssert.AreSame(originalBus, MessageHandler.MessageBus);\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_1","title":"When to use this","text":"<ul> <li>In unit tests to isolate message traffic</li> <li>When temporarily redirecting messages for debugging or logging</li> <li>For scoped gameplay features that need their own message channel</li> </ul> <p>Pattern: This returns an <code>IDisposable</code>, so you can use it with <code>using</code> statements for automatic cleanup. When the scope exits (either normally or via exception), the previous bus is restored.</p>"},{"location":"advanced/runtime-configuration/#resetting-to-the-default-bus","title":"Resetting to the Default Bus","text":"<p>Use <code>ResetGlobalMessageBus()</code> to restore the original startup bus:</p> C#<pre><code>// Replace the global bus\nMessageHandler.SetGlobalMessageBus(containerBus);\n\n// Later, restore the original default bus\nMessageHandler.ResetGlobalMessageBus();\n\n// MessageHandler.MessageBus is now back to the original startup bus\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_2","title":"When to use this","text":"<ul> <li>After tests that called <code>SetGlobalMessageBus()</code></li> <li>When tearing down a DI container and reverting to default behavior</li> <li>In development/debug scenarios where you want to reset state</li> </ul>"},{"location":"advanced/runtime-configuration/#accessing-the-original-startup-bus","title":"Accessing the Original Startup Bus","text":"<p><code>MessageHandler.InitialGlobalMessageBus</code> always returns the original bus that was created during static initialization, regardless of any calls to <code>SetGlobalMessageBus()</code>:</p> C#<pre><code>IMessageBus startupBus = MessageHandler.InitialGlobalMessageBus;\n\n// Replace the global bus\nMessageHandler.SetGlobalMessageBus(new MessageBus());\n\n// InitialGlobalMessageBus is unchanged\nAssert.AreSame(startupBus, MessageHandler.InitialGlobalMessageBus);\n\n// But MessageBus returns the new bus\nAssert.AreNotSame(startupBus, MessageHandler.MessageBus);\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_3","title":"When to use this","text":"<ul> <li>In diagnostic tools that need a stable reference point</li> <li>When you want to compare against the original bus for debugging</li> <li>In tests that need to verify bus isolation while still accessing the original</li> </ul>"},{"location":"advanced/runtime-configuration/#example-bootstrapping-a-di-container","title":"Example: Bootstrapping a DI Container","text":"<p>Here's a complete example of integrating DxMessaging with a DI container:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\npublic class GameBootstrapper\n{\n public void Bootstrap()\n {\n // Create container-managed bus\n IMessageBus containerBus = new MessageBus();\n\n // Register with container\n container.RegisterInstance<IMessageBus>(containerBus);\n\n // Replace global bus so all subsequent registrations use it\n MessageHandler.SetGlobalMessageBus(containerBus);\n\n // Now when MessagingComponents are created, they automatically\n // use the container-managed bus\n }\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#re-binding-registrations","title":"Re-binding Registrations","text":"<p>Sometimes you need to change which bus a component listens to after it's already been set up. This is called \"re-binding.\"</p>"},{"location":"advanced/runtime-configuration/#the-messagebusrebindmode-enum","title":"The MessageBusRebindMode Enum","text":"<p>When re-binding, you have two options:</p>"},{"location":"advanced/runtime-configuration/#preserveregistrations","title":"PreserveRegistrations","text":"<p>Keeps existing active registrations on their current bus, but sets the specified bus for future registrations:</p> C#<pre><code>// Component is currently using busA\ncomponent.Configure(busA, MessageBusRebindMode.RebindActive);\n\n// Later, switch to busB but preserve existing registrations\ncomponent.Configure(busB, MessageBusRebindMode.PreserveRegistrations);\n\n// Existing handlers still listen on busA\n// New registrations will use busB\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_4","title":"When to use this","text":"<ul> <li>When you want to gradually migrate a component to a new bus</li> <li>If you need to maintain backwards compatibility during refactoring</li> <li>When existing registrations must continue processing messages from their original source</li> </ul>"},{"location":"advanced/runtime-configuration/#rebindactive","title":"RebindActive","text":"<p>Immediately moves all active registrations to the new bus:</p> C#<pre><code>// Component is listening on busA\ncomponent.Configure(busA, MessageBusRebindMode.RebindActive);\n\n// Switch everything to busB\ncomponent.Configure(busB, MessageBusRebindMode.RebindActive);\n\n// All handlers now listen on busB\n// busA handlers are deregistered and re-registered on busB\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_5","title":"When to use this","text":"<ul> <li>When you want complete, immediate migration to a new bus</li> <li>In DI scenarios where the container provides a new bus instance</li> <li>When you need atomic switching without mixed-bus behavior</li> </ul>"},{"location":"advanced/runtime-configuration/#retarget-a-token-directly","title":"Retarget a Token Directly","text":"<p>You can also retarget individual <code>MessageRegistrationToken</code> instances:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nIMessageBus busA = new MessageBus();\nIMessageBus busB = new MessageBus();\n\nvar handler = new MessageHandler(new InstanceId(1), busA) { active = true };\nvar token = MessageRegistrationToken.Create(handler, busA);\ntoken.RegisterUntargeted<MyMessage>(OnMessage);\ntoken.Enable();\n\n// Later, retarget to busB\ntoken.RetargetMessageBus(busB, MessageBusRebindMode.RebindActive);\n\n// All registrations are now on busB\n</code></pre>"},{"location":"advanced/runtime-configuration/#when-to-use-this_6","title":"When to use this","text":"<ul> <li>When you have direct access to a token and want fine-grained control</li> <li>In advanced scenarios like dynamic message routing or pooling</li> <li>When building custom messaging abstractions</li> </ul>"},{"location":"advanced/runtime-configuration/#example-scene-specific-buses","title":"Example: Scene-Specific Buses","text":"<p>Here's a practical example of using re-binding for scene isolation:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing UnityEngine;\n\npublic class SceneMessagingManager : MonoBehaviour\n{\n private IMessageBus _sceneLocalBus;\n\n private void Awake()\n {\n // Create a bus specific to this scene\n _sceneLocalBus = new MessageBus();\n\n // Find all messaging components in this scene\n MessagingComponent[] components = FindObjectsOfType<MessagingComponent>();\n\n // Retarget them to the scene-local bus\n foreach (var component in components)\n {\n component.Configure(_sceneLocalBus, MessageBusRebindMode.RebindActive);\n }\n }\n\n private void OnDestroy()\n {\n // When scene unloads, components are destroyed automatically\n // No need to manually clean up registrations\n }\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#why-this-is-useful","title":"Why this is useful","text":"<ul> <li>Messages sent in this scene won't leak to other scenes</li> <li>When the scene unloads, all message traffic stops cleanly</li> <li>Perfect for additive scene workflows or mini-games</li> </ul>"},{"location":"advanced/runtime-configuration/#common-patterns","title":"Common Patterns","text":""},{"location":"advanced/runtime-configuration/#pattern-1-test-isolation","title":"Pattern 1: Test Isolation","text":"<p>Ensure each test has its own isolated bus:</p> C#<pre><code>using NUnit.Framework;\nusing DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n[TestFixture]\npublic class MySystemTests\n{\n private IMessageBus _testBus;\n\n [SetUp]\n public void SetUp()\n {\n // Create a fresh bus for each test\n _testBus = new MessageBus();\n MessageHandler.SetGlobalMessageBus(_testBus);\n }\n\n [TearDown]\n public void TearDown()\n {\n // Restore the default bus\n MessageHandler.ResetGlobalMessageBus();\n }\n\n [Test]\n public void MyTest()\n {\n // This test uses _testBus exclusively\n // No interference from other tests\n }\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#pattern-2-temporary-override-with-automatic-cleanup","title":"Pattern 2: Temporary Override with Automatic Cleanup","text":"<p>Use <code>using</code> statements for scoped overrides:</p> C#<pre><code>[Test]\npublic void TestWithIsolation()\n{\n IMessageBus isolatedBus = new MessageBus();\n\n using (MessageHandler.OverrideGlobalMessageBus(isolatedBus))\n {\n // Test code here uses isolatedBus\n var msg = new TestMessage();\n msg.EmitUntargeted();\n\n // Verify only this test's bus received the message\n }\n\n // Original bus automatically restored\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#pattern-3-di-container-integration","title":"Pattern 3: DI Container Integration","text":"<p>Bootstrap with container-managed bus:</p> C#<pre><code>public class DependencyInjectionBootstrap\n{\n public void Initialize(IContainer container)\n {\n // Container creates the bus\n IMessageBus containerBus = container.Resolve<IMessageBus>();\n\n // Make it the global default\n MessageHandler.SetGlobalMessageBus(containerBus);\n\n // Now all MessagingComponents use the container bus by default\n }\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#pattern-4-dynamic-component-reconfiguration","title":"Pattern 4: Dynamic Component Reconfiguration","text":"<p>Change a component's bus at runtime:</p> C#<pre><code>public class DynamicComponentManager\n{\n public void SwitchComponentToBus(MessagingComponent component, IMessageBus newBus)\n {\n // Immediately move all handlers to the new bus\n component.Configure(newBus, MessageBusRebindMode.RebindActive);\n\n // Component now listens on newBus\n }\n}\n</code></pre>"},{"location":"advanced/runtime-configuration/#quick-reference","title":"Quick Reference","text":"API Purpose Use Case <code>MessageHandler.MessageBus</code> Access current global bus Normal message emission <code>MessageHandler.InitialGlobalMessageBus</code> Access original startup bus Diagnostics, debugging <code>MessageHandler.SetGlobalMessageBus(bus)</code> Permanently replace global bus DI integration, test setup <code>MessageHandler.OverrideGlobalMessageBus(bus)</code> Temporarily override (returns IDisposable) Test isolation, scoped features <code>MessageHandler.ResetGlobalMessageBus()</code> Restore original startup bus Test teardown, reset state <code>component.Configure(bus, mode)</code> Set component's bus Component configuration <code>token.RetargetMessageBus(bus, mode)</code> Retarget a token Fine-grained control <code>MessageBusRebindMode.PreserveRegistrations</code> Keep existing registrations on old bus Gradual migration <code>MessageBusRebindMode.RebindActive</code> Move all registrations to new bus Atomic switching"},{"location":"advanced/runtime-configuration/#see-also","title":"See Also","text":"<ul> <li>Message Bus Providers \u2014 ScriptableObject-based provider system for design-time configuration</li> <li>Registration Builders \u2014 Fluent API for building message registrations with priority and lifecycle control</li> <li>DI Integration Guides \u2014 Zenject, VContainer, and Reflex integration patterns</li> <li>Testing Guide \u2014 Comprehensive testing patterns with DxMessaging</li> <li>Back to Documentation Hub \u2014 Browse all docs</li> </ul>"},{"location":"advanced/string-messages/","title":"String Messages (Prototyping & Debugging)","text":"<p>Sometimes you just want to fire a quick string without defining a formal message. DxMessaging provides three built\u2011in types:</p> <ul> <li><code>StringMessage</code>: targeted string message (to a specific recipient)</li> <li><code>GlobalStringMessage</code>: untargeted string message (global broadcast)</li> <li><code>SourcedStringMessage</code>: broadcast string message (from a specific source)</li> </ul> <p>When to use</p> <ul> <li>Rapid prototyping, debugging, tool scripts, or test spikes.</li> <li>Sending textual notifications without defining a struct.</li> </ul> <p>When not to use</p> <ul> <li>Shipping gameplay code that benefits from compile\u2011time safety and structure. Prefer explicit message structs.</li> </ul> <p>Examples</p> <p>Global (untargeted)</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Extensions;\n\nvar msg = new GlobalStringMessage(\"Saved\");\nmsg.Emit();\n</code></pre> <p>Targeted (to a specific GameObject)</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Extensions;\nusing UnityEngine;\n\nvar msg = new StringMessage(\"Hello!\");\nmsg.EmitGameObjectTargeted(gameObject);\n// Or even\n\"Hello!\".EmitGameObjectTargeted(gameObject);\n// Or shorthand by InstanceId shape\n\"Hello!\".EmitAt(gameObject);\n\n// Broadcast (from a specific source)\nvar b = new SourcedStringMessage(\"Hit\");\nb.EmitGameObjectBroadcast(gameObject);\n// Or shorthand by InstanceId shape\n\"Hit\".EmitFrom(gameObject);\n</code></pre> <p>Listening</p> C#<pre><code>using DxMessaging.Core.Messages;\n\n_ = token.RegisterUntargeted<GlobalStringMessage>(OnGlobalText);\n_ = token.RegisterComponentTargeted<StringMessage>(this, OnTextToMe);\n\nvoid OnGlobalText(ref GlobalStringMessage m) => Debug.Log($\"Global: {m.message}\");\nvoid OnTextToMe(ref StringMessage m) => Debug.Log($\"To me: {m.message}\");\n</code></pre> <p>Tips</p> <ul> <li>Strings are great for glue and debugging; convert hot paths to typed messages for performance and clarity.</li> <li>You can combine with interceptors/post\u2011processors for logging and filtering.</li> </ul> <p>Related</p> <ul> <li>Message Types</li> <li>Diagnostics</li> </ul>"},{"location":"architecture/comparisons/","title":"Comparisons: DxMessaging and Alternatives","text":"<p>TL;DR: This guide compares DxMessaging with other messaging approaches. Each has legitimate strengths and trade-offs. DxMessaging takes a different approach with its own trade-offs.</p>"},{"location":"architecture/comparisons/#this-guide-shows","title":"This guide shows","text":"<ul> <li>Characteristics of each approach (with real code examples)</li> <li>How DxMessaging takes a different approach (with real code examples)</li> <li>Trade-offs for each option (what you give up, what you gain)</li> <li>When to use each approach based on your needs</li> </ul>"},{"location":"architecture/comparisons/#table-of-contents","title":"Table of Contents","text":"<ul> <li>Performance Benchmarks</li> <li>Unity Messaging Frameworks</li> <li>UniRx</li> <li>MessagePipe</li> <li>Zenject Signals</li> <li>Scriptable Object Architecture (SOA)</li> <li>Traditional Approaches</li> <li>C# Events/Delegates</li> <li>UnityEvents</li> <li>Unity SendMessage</li> <li>Static Event Buses</li> <li>Trade-offs</li> <li>Feature Matrix</li> <li>Decision Guide</li> </ul>"},{"location":"architecture/comparisons/#performance-benchmarks","title":"Performance Benchmarks","text":"<p>These sections are auto-updated by the PlayMode comparison benchmarks in the Comparison Performance PlayMode tests. Run the suite locally to refresh the tables.</p>"},{"location":"architecture/comparisons/#comparisons-windows","title":"Comparisons (Windows)","text":"Message Tech Operations / Second Allocations? DxMessaging (Untargeted) - No-Copy 19,886,000 No UniRx MessageBroker 18,002,000 No MessagePipe (Global) 97,748,000 No Zenject SignalBus 2,350,000 Yes"},{"location":"architecture/comparisons/#comparisons-macos","title":"Comparisons (macOS)","text":"<p>Run the PlayMode comparison benchmarks on macOS to populate this section.</p>"},{"location":"architecture/comparisons/#comparisons-linux","title":"Comparisons (Linux)","text":"<p>Run the PlayMode comparison benchmarks on Linux to populate this section.</p>"},{"location":"architecture/comparisons/#unity-messaging-frameworks","title":"Unity Messaging Frameworks","text":"<p>This section compares DxMessaging with other popular Unity messaging/eventing libraries. Each offers different approaches to solving communication and decoupling problems in Unity.</p>"},{"location":"architecture/comparisons/#quick-summary-which-framework-to-choose","title":"Quick Summary: Which Framework to Choose?","text":""},{"location":"architecture/comparisons/#tldr-decision-tree","title":"TL;DR Decision Tree","text":"Text Only<pre><code>Need absolute simplest pub/sub setup (zero boilerplate)?\n \u2192 Use UniRx MessageBroker (publish/receive in 2 lines)\n\nNeed complex event stream transformations (debounce, throttle, combine)?\n \u2192 Use UniRx (reactive programming paradigm)\n\nAlready using Dependency Injection (Zenject, VContainer, Reflex)?\n \u2192 Use MessagePipe (DI-first, best performance) or Zenject Signals (if on Zenject)\n \u2192 Or DxMessaging (integrates with DI, see Integrations guides for Zenject/VContainer/Reflex)\n\nNeed Unity-specific features (GameObject targeting, Inspector debugging, global observers)?\n \u2192 Use DxMessaging (Unity-first design)\n\nWant plug-and-play with zero dependencies?\n \u2192 Use DxMessaging (no setup required)\n\nMaximum raw throughput is a top priority?\n \u2192 Use MessagePipe (highest ops/sec in benchmarks)\n\nNeed message validation, interception, or ordered execution?\n \u2192 Use DxMessaging (interceptor pipeline, priority-based ordering)\n\nSimple pub/sub with automatic lifecycle management and debugging?\n \u2192 Use DxMessaging (automatic cleanup, priorities, validation, Inspector)\n</code></pre>"},{"location":"architecture/comparisons/#one-line-summary-for-each","title":"One-Line Summary for Each","text":"<ul> <li>DxMessaging: Unity-first pub/sub with automatic lifecycle and Inspector debugging</li> <li>UniRx: Reactive programming with LINQ-style stream operators for complex event transformations</li> <li>MessagePipe: DI-first, highest throughput for high-frequency messaging in DI architectures</li> <li>Zenject Signals: Decoupled messaging integrated with Zenject dependency injection</li> </ul> <p>\ud83d\udca1 Note: DxMessaging works both standalone (zero dependencies) AND with DI frameworks. See Integration Guides for Zenject, VContainer, and Reflex.</p>"},{"location":"architecture/comparisons/#unirx-reactive-extensions-for-unity","title":"UniRx (Reactive Extensions for Unity)","text":"<p>What It Is: A reactive programming library that treats events as observable streams. Based on .NET Reactive Extensions (Rx), reimplemented for Unity with IL2CPP compatibility.</p> <p>Core Philosophy: Everything is a stream that can be observed, filtered, combined, and transformed using LINQ-style operators.</p>"},{"location":"architecture/comparisons/#key-features","title":"Key Features","text":"<ul> <li>Stream-based programming: Transform events into observable sequences</li> <li>LINQ operators: <code>Where</code>, <code>Select</code>, <code>Merge</code>, <code>CombineLatest</code>, <code>Buffer</code>, etc.</li> <li>Async operations: Convert coroutines to observables with cancellation support</li> <li>Multithreading: Thread-safe operations with main thread synchronization</li> <li>Time operators: Frame-based and time-based event handling</li> <li>UI integration: Observable extensions for Unity UI events</li> </ul>"},{"location":"architecture/comparisons/#code-example","title":"Code Example","text":""},{"location":"architecture/comparisons/#simple-messagebroker-setup-pubsub","title":"Simple MessageBroker Setup (Pub/Sub)","text":"C#<pre><code>using UniRx;\nusing UnityEngine;\n\npublic struct EnemySpawned\n{\n public int EnemyId;\n public Vector3 Position;\n}\n\n// Publisher - straightforward, no setup required\npublic class EnemySpawner : MonoBehaviour\n{\n void SpawnEnemy(int id)\n {\n MessageBroker.Default.Publish(new EnemySpawned\n {\n EnemyId = id,\n Position = transform.position\n });\n }\n}\n\n// Subscriber - also straightforward\npublic class AchievementSystem : MonoBehaviour\n{\n void Start()\n {\n MessageBroker.Default.Receive<EnemySpawned>()\n .Subscribe(msg => Debug.Log($\"Enemy {msg.EnemyId} spawned!\"))\n .AddTo(this); // Automatic cleanup on destroy\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#advanced-stream-transformations-reactive-programming","title":"Advanced Stream Transformations (Reactive Programming)","text":"C#<pre><code>// Double-click detection using reactive operators\nObservable.EveryUpdate()\n .Where(_ => Input.GetMouseButtonDown(0))\n .Buffer(Observable.Timer(TimeSpan.FromMilliseconds(250)))\n .Where(xs => xs.Count >= 2)\n .Subscribe(_ => Debug.Log(\"Double Click!\"));\n\n// Combine multiple input streams\nvar leftClick = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0));\nvar rightClick = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(1));\nleftClick.Merge(rightClick).Subscribe(_ => Debug.Log(\"Any click!\"));\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves","title":"What Problems It Solves","text":"<ul> <li>\u2705 Complex event streams: Chain, filter, combine, and transform events with operators</li> <li>\u2705 Async operations: Better async/await alternative with cancellation</li> <li>\u2705 Temporal logic: Time-based operations (throttle, debounce, sample)</li> <li>\u2705 UI reactivity: Bind UI elements to data streams reactively</li> <li>\u2705 Memory management: Disposable subscriptions prevent leaks</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u26a0\ufe0f Simple pub/sub: MessageBroker handles this well, but reactive operators may add complexity for simple use cases</li> <li>\u274c Execution order control: No built-in priority system for handler ordering</li> <li>\u274c Message validation/interception: No pre-processing pipeline to validate or transform messages before handlers</li> <li>\u274c Unity Inspector debugging: No Inspector integration to visualize message flow</li> <li>\u274c GameObject/Component targeting: Not designed for Unity-specific targeting patterns</li> <li>\u274c Global message observation: Cannot easily listen to all instances of a message type across different sources</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics","title":"Performance Characteristics","text":"<ul> <li>Allocations: MessageBroker is zero-allocation for basic operations. Stream operators (Select, Where, Buffer) may allocate. For simple pub/sub, UniRx matches DxMessaging's allocation profile.</li> <li>Overhead: Higher than simple events due to observable infrastructure</li> <li>Use case: Best for complex event transformations; overhead justified by functionality</li> </ul>"},{"location":"architecture/comparisons/#learning-curve","title":"Learning Curve","text":"<ul> <li>Simple MessageBroker (basic pub/sub): Very easy - just <code>Publish()</code> and <code>Receive()</code>, similar to events</li> <li>Advanced stream operators: Steep - requires understanding reactive programming paradigm</li> <li>Mental model shift: For complex features, must think in streams, not events</li> <li>Documentation: Extensive examples, but reactive concepts take time to master</li> <li>Estimated learning time: 15 minutes for MessageBroker; 1-2 weeks for reactive stream mastery</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50\u2b50\u2b50 (Very easy) - MessageBroker pub/sub is intuitive and straightforward</li> <li>\u2b50\u2b50\u2b50 (Moderate to difficult) - Advanced reactive operators require learning</li> <li>Stream operator code is concise but requires understanding of reactive patterns</li> <li>Hard to debug complex observable chains without Rx knowledge</li> <li>For advanced features: Team buy-in essential; not intuitive for traditional event-driven developers</li> </ul>"},{"location":"architecture/comparisons/#when-unirx-wins","title":"When UniRx Wins","text":"<ul> <li>\u2705 Simple pub/sub with minimal setup (MessageBroker is straightforward)</li> <li>\u2705 Complex event transformations (e.g., double-click, gesture detection)</li> <li>\u2705 Combining multiple input sources</li> <li>\u2705 Time-based logic (debounce, throttle, sample)</li> <li>\u2705 UI data binding with reactive updates</li> <li>\u2705 Teams familiar with reactive programming</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Need Unity-specific features (GameObject targeting, lifecycle management)</li> <li>\u2705 Execution order matters (priority-based ordering)</li> <li>\u2705 Message validation/interception needed (interceptor pipeline)</li> <li>\u2705 Inspector debugging required (message history, registration view)</li> <li>\u2705 Direct GameObject/Component targeting</li> <li>\u2705 Global message observation (listen to all instances of a message type)</li> <li>\u2705 Late-stage processing (post-processors after all handlers)</li> <li>\u2705 Automatic lifecycle management (prevents common memory leaks)</li> <li>\u2705 Teams unfamiliar with reactive programming (and don't need reactive features)</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison","title":"Direct Comparison","text":"Aspect UniRx DxMessaging Primary Use Case Stream transformations Pub/sub messaging Unity Compatibility \u2705 Built for Unity \u2705 Built for Unity Dependencies \u2705 Standalone \u2705 Standalone Performance 18M ops/sec 14M ops/sec Allocations \u26a0\ufe0f Can allocate \u2705 Zero (structs) Learning Curve \u2b50 Steep (Rx paradigm) \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Low \u2b50\u2b50\u2b50\u2b50\u2b50 Plug-and-play DI Integration \u26a0\ufe0f Optional \u26a0\ufe0f Optional Async/Await \u2705 Observables \u26a0\ufe0f Manual Type Safety \u2705 Strong \u2705 Strong Lifecycle Management \u26a0\ufe0f Manual dispose \u2705 Automatic Execution Order \u274c Not built-in \u2705 Priority-based GameObject Targeting \u274c Not designed for \u2705 Built-in Unity Integration \u2b50\u2b50\u2b50\u2b50 Good (UI) \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u274c Not built-in \u2705 Full pipeline Global Observers \u274c Not built-in \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50\u2b50\u2b50\u2b50 Good \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Decoupling \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Temporal Operators \u2705 Extensive (Rx) \u274c Not built-in Complex Stream Logic \u2705 LINQ-style \u274c Not designed for <p>Bottom Line: UniRx is well-suited for complex event stream transformations and reactive programming patterns, with MessageBroker providing straightforward pub/sub setup. DxMessaging focuses on straightforward pub/sub communication with control, validation, debugging, and Unity-specific features. Both are viable options depending on your needs: choose UniRx when you need stream operators, reactive patterns, or simple zero-setup pub/sub; choose DxMessaging when you need Unity-native lifecycle integration, execution control, and debugging tools.</p>"},{"location":"architecture/comparisons/#messagepipe-high-performance-messaging","title":"MessagePipe (High-Performance Messaging)","text":"<p>What It Is: A high-performance, DI-first messaging library by Cysharp (creators of UniTask). Designed for in-memory and distributed messaging with zero-allocation focus.</p> <p>Core Philosophy: Maximum performance with dependency injection integration. Support all messaging patterns with a unified, generic interface.</p>"},{"location":"architecture/comparisons/#key-features_1","title":"Key Features","text":"<ul> <li>Multiple patterns: Pub/Sub, Request/Response, Mediator patterns</li> <li>Sync and async: Full async/await support with configurable strategies (sequential/parallel)</li> <li>Keyed messaging: Type-based or key-based message routing</li> <li>DI-first design: Deep integration with dependency injection containers</li> <li>Filters: Pre/post execution customization (similar to interceptors)</li> <li>Zero allocation: Struct messages with zero GC per publish</li> <li>Roslyn analyzer: Detects subscription leaks at compile-time</li> <li>Global and scoped: Support for global message bus or scoped instances</li> </ul>"},{"location":"architecture/comparisons/#code-example_1","title":"Code Example","text":"C#<pre><code>// Using MessagePipe with DI\npublic class GameManager : MonoBehaviour\n{\n private IPublisher<EnemySpawned> _publisher;\n private IDisposable _subscription;\n\n void Start()\n {\n // Injected via DI container\n _publisher = GlobalMessagePipe.GetPublisher<EnemySpawned>();\n var subscriber = GlobalMessagePipe.GetSubscriber<EnemySpawned>();\n\n _subscription = subscriber.Subscribe(msg =>\n {\n Debug.Log($\"Enemy spawned: {msg.EnemyId}\");\n });\n }\n\n void SpawnEnemy(int id)\n {\n _publisher.Publish(new EnemySpawned { EnemyId = id });\n }\n\n void OnDestroy() => _subscription?.Dispose();\n}\n\n// Async handler with filters\npublic class AchievementSystem\n{\n public AchievementSystem(IAsyncSubscriber<EnemyKilled> subscriber)\n {\n subscriber.Subscribe(async (msg, cancellationToken) =>\n {\n await SaveAchievementAsync(msg.EnemyType);\n });\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_1","title":"What Problems It Solves","text":"<ul> <li>\u2705 Performance: Zero allocations with struct-based messages (see benchmarks for comparison data)</li> <li>\u2705 DI integration: First-class support for dependency injection</li> <li>\u2705 Async messaging: Native async/await without blocking</li> <li>\u2705 Leak detection: Analyzer catches forgotten subscriptions at compile-time</li> <li>\u2705 Flexibility: Keyed, keyless, buffered, request/response patterns</li> <li>\u2705 Cross-platform: Works in Unity, .NET, Blazor, etc.</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_1","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c Unity-specific integration: No built-in Unity MonoBehaviour lifecycle management or GameObject targeting</li> <li>\u274c Inspector debugging: No visual debugging or message history in Unity Inspector</li> <li>\u274c Execution order control: No priority system (handlers execute in subscription order)</li> <li>\u274c Setup complexity: Requires DI container configuration (VContainer/Zenject setup needed)</li> <li>\u274c Global message observation: No built-in way to listen to all instances of a message across different keys/sources</li> <li>\u274c Standalone use: Designed for DI-first architecture (less suitable for non-DI projects)</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_1","title":"Performance Characteristics","text":"<ul> <li>High throughput: MessagePipe is optimized for high-frequency messaging scenarios</li> <li>Zero allocation: Struct-based messages with no GC per publish</li> <li>Benchmark data: See performance section above for actual numbers</li> <li>Use case: Optimized for high-frequency messaging (thousands/frame)</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_1","title":"Learning Curve","text":"<ul> <li>Moderate: Requires understanding of dependency injection</li> <li>DI knowledge: Must be comfortable with service provider pattern</li> <li>Generic interfaces: Multiple generic types can be confusing initially</li> <li>Estimated learning time: 2-3 days with DI experience; 1 week without</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_1","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50\u2b50 (Moderate)</li> <li>Clean, generic interfaces once you understand DI</li> <li>Code is straightforward for developers familiar with DI patterns</li> <li>Harder for teams without DI experience</li> </ul>"},{"location":"architecture/comparisons/#when-messagepipe-wins","title":"When MessagePipe Wins","text":"<ul> <li>\u2705 Performance-critical applications (high message throughput)</li> <li>\u2705 Projects already using DI (VContainer, Zenject, etc.)</li> <li>\u2705 Cross-platform .NET projects (not Unity-only)</li> <li>\u2705 Need async messaging with cancellation</li> <li>\u2705 Large-scale projects with DI architecture</li> <li>\u2705 Teams experienced with DI patterns</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_1","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Unity-first projects (not cross-platform .NET)</li> <li>\u2705 Unity lifecycle management needed (automatic MonoBehaviour cleanup)</li> <li>\u2705 Inspector debugging essential (message history visualization)</li> <li>\u2705 Execution order control needed (priority-based handlers)</li> <li>\u2705 Message validation/interception required (interceptor pipeline)</li> <li>\u2705 Global message observation needed (listen to all message instances)</li> <li>\u2705 Post-processing stage needed (analytics, logging after handlers)</li> <li>\u2705 Teams without DI experience or projects not using DI</li> <li>\u2705 Plug-and-play simplicity preferred over DI configuration</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_1","title":"Direct Comparison","text":"Aspect MessagePipe DxMessaging Primary Use Case High-perf DI messaging Pub/sub messaging Unity Compatibility \u2705 Built for Unity \u2705 Built for Unity Dependencies \u26a0\ufe0f DI container required \u2705 Standalone Performance 97M ops/sec 14M ops/sec Allocations \u2705 Zero (structs) \u2705 Zero (structs) Learning Curve \u2b50\u2b50\u2b50\u2b50 Moderate (DI) \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50 DI setup required \u2b50\u2b50\u2b50\u2b50\u2b50 Plug-and-play DI Integration \u2705 First-class \u26a0\ufe0f Optional Async/Await \u2705 Native \u26a0\ufe0f Manual Type Safety \u2705 Strong \u2705 Strong Lifecycle Management \u26a0\ufe0f Manual dispose \u2705 Automatic Execution Order \u274c Subscription order \u2705 Priority-based GameObject Targeting \u274c Not built-in \u2705 Built-in Unity Integration \u2b50\u2b50\u2b50 Basic (no lifecycle) \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u26a0\ufe0f Filters \u2705 Full pipeline Global Observers \u274c Not built-in \u2705 Listen to all Post-Processing \u26a0\ufe0f Via filters \u2705 Dedicated stage Testability \u2b50\u2b50\u2b50\u2b50\u2b50 DI mocking \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Leak Detection \u2705 Roslyn analyzer \u2705 Automatic lifecycle <p>Bottom Line: MessagePipe is the performance king with DI-first design. DxMessaging is Unity-first with lifecycle awareness and debugging. Use MessagePipe if you have DI infrastructure and need maximum performance. Use DxMessaging if you want Unity-native messaging with automatic lifecycle management.</p> <p>Note on Performance: MessagePipe's ~95M ops/sec vs DxMessaging's ~20M ops/sec shows a significant throughput advantage. This matters primarily for high-frequency messaging scenarios (thousands of messages per frame). For typical gameplay events, both are fast enough that performance is not a distinguishing factor.</p> <p>\ud83d\udca1 Want both? DxMessaging integrates with DI frameworks! See DI Integration Guides for Zenject, VContainer, and Reflex. Use DI for service construction, DxMessaging for event communication.</p>"},{"location":"architecture/comparisons/#zenject-signals-di-based-messaging","title":"Zenject Signals (DI-Based Messaging)","text":"<p>What It Is: The built-in messaging system for Zenject (Extenject), a dependency injection framework for Unity. Signals are an optional extension that provides decoupled communication.</p> <p>Core Philosophy: Loosely coupled messaging integrated with dependency injection. Reduce direct dependencies between classes while maintaining testability.</p>"},{"location":"architecture/comparisons/#key-features_2","title":"Key Features","text":"<ul> <li>DI-integrated: Signals declared and resolved via Zenject container</li> <li>Typed signals: Strongly-typed signal classes with parameters</li> <li>Synchronous and async: Sync (RunSync) and async (RunAsync) execution modes</li> <li>Subscription modes: Require, optional, or optional-with-warning subscribers</li> <li>Installer-based setup: Declare signals in installers for container binding</li> <li>Multiple subscription methods: Direct binding, SignalBus subscription, stream-based (with UniRx)</li> <li>Testable: Easy to mock and test with dependency injection</li> </ul>"},{"location":"architecture/comparisons/#code-example_2","title":"Code Example","text":"C#<pre><code>// 1. Define signal\npublic class EnemyKilledSignal\n{\n public string EnemyType;\n public int Score;\n}\n\n// 2. Install and declare in installer\npublic class GameInstaller : MonoInstaller\n{\n public override void InstallBindings()\n {\n SignalBusInstaller.Install(Container);\n Container.DeclareSignal<EnemyKilledSignal>();\n Container.BindSignal<EnemyKilledSignal>()\n .ToMethod<AchievementSystem>(x => x.OnEnemyKilled)\n .FromResolve();\n }\n}\n\n// 3. Fire signal\npublic class Enemy : MonoBehaviour\n{\n [Inject] private SignalBus _signalBus;\n\n void Die()\n {\n _signalBus.Fire(new EnemyKilledSignal\n {\n EnemyType = \"Orc\",\n Score = 100\n });\n }\n}\n\n// 4. Subscribe to signal\npublic class AchievementSystem\n{\n [Inject] private SignalBus _signalBus;\n\n public void Initialize()\n {\n _signalBus.Subscribe<EnemyKilledSignal>(OnEnemyKilled);\n }\n\n void OnEnemyKilled(EnemyKilledSignal signal)\n {\n Debug.Log($\"Killed {signal.EnemyType} for {signal.Score} points!\");\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_2","title":"What Problems It Solves","text":"<ul> <li>\u2705 Decoupling: Classes communicate without direct references</li> <li>\u2705 DI integration: Seamless with Zenject dependency injection</li> <li>\u2705 Testability: Easy to mock SignalBus in tests</li> <li>\u2705 Type safety: Strongly-typed signal classes</li> <li>\u2705 Subscriber validation: Can enforce required subscribers</li> <li>\u2705 Async support: Fire signals synchronously or asynchronously</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_2","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c Zenject dependency: Must use Zenject/Extenject framework; not standalone</li> <li>\u274c Performance overhead: Higher than lightweight messaging (DI resolution cost)</li> <li>\u274c Execution order control: No priority system for handler ordering</li> <li>\u274c Inspector debugging: No visual message history or flow visualization</li> <li>\u274c Allocations: Signal parameters can cause allocations depending on usage</li> <li>\u274c Validation pipeline: No built-in interceptor or pre-processing stage</li> <li>\u274c Global observation: Cannot easily listen to all signal fires across the system</li> <li>\u274c Post-processing: No dedicated after-handler stage for analytics/logging</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_2","title":"Performance Characteristics","text":"<ul> <li>Overhead: Higher than lightweight messaging (DI resolution + boxing)</li> <li>Allocations: Signal parameters can cause allocations (depends on implementation)</li> <li>Benchmark data: See performance section above for actual numbers</li> <li>Use case: Performance trade-off for testability and DI benefits</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_2","title":"Learning Curve","text":"<ul> <li>Moderate to steep: Requires understanding Zenject dependency injection</li> <li>Zenject knowledge: Must learn Zenject before signals</li> <li>Setup overhead: Installers, bindings, container configuration</li> <li>Estimated learning time: 1 week for Zenject + signals together</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_2","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50 (Moderate)</li> <li>Clear once you understand Zenject</li> <li>Signal concept is straightforward</li> <li>Setup (installers, bindings) adds complexity</li> </ul>"},{"location":"architecture/comparisons/#when-zenject-signals-win","title":"When Zenject Signals Win","text":"<ul> <li>\u2705 Already using Zenject for dependency injection</li> <li>\u2705 Testability is critical (DI makes mocking easy)</li> <li>\u2705 Need subscriber validation (ensure handlers exist)</li> <li>\u2705 Team experienced with Zenject</li> <li>\u2705 Want DI-managed lifecycle</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_2","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Not using Zenject/Extenject (or prefer standalone solution)</li> <li>\u2705 Performance critical (lower overhead than DI-based signals)</li> <li>\u2705 Execution order control needed (priority-based handlers)</li> <li>\u2705 Inspector debugging required (message history visualization)</li> <li>\u2705 Message validation/interception needed (interceptor pipeline)</li> <li>\u2705 Global message observation needed (listen to all signal fires)</li> <li>\u2705 Post-processing stage needed (analytics after handlers)</li> <li>\u2705 Zero-allocation messaging essential (struct-based)</li> <li>\u2705 GameObject/Component targeting needed (Unity-specific patterns)</li> <li>\u2705 Plug-and-play simplicity preferred over DI setup</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_2","title":"Direct Comparison","text":"Aspect Zenject Signals DxMessaging Primary Use Case DI-integrated messaging Pub/sub messaging Unity Compatibility \u2705 Built for Unity \u2705 Built for Unity Dependencies \u274c Zenject required \u2705 Standalone Performance 2.5M ops/sec 14M ops/sec Allocations \u26a0\ufe0f Can allocate \u2705 Zero (structs) Learning Curve \u2b50\u2b50 Steep (Zenject+Signals) \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50 Installers required \u2b50\u2b50\u2b50\u2b50\u2b50 Plug-and-play DI Integration \u2705 Required (Zenject) \u26a0\ufe0f Optional Async/Await \u2705 RunAsync support \u26a0\ufe0f Manual Type Safety \u2705 Strong \u2705 Strong Lifecycle Management \u26a0\ufe0f DI-managed \u2705 Automatic Execution Order \u274c Not built-in \u2705 Priority-based GameObject Targeting \u274c Not built-in \u2705 Built-in Unity Integration \u2b50\u2b50\u2b50\u2b50 DI-managed \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u26a0\ufe0f Subscriber validation \u2705 Full pipeline Global Observers \u274c Not built-in \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50\u2b50\u2b50\u2b50\u2b50 DI mocking \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent <p>Bottom Line: Zenject Signals are well-suited if you're already invested in Zenject and value testability through DI. DxMessaging offers standalone messaging without requiring DI setup, and includes Unity-specific features.</p> <p>\ud83d\udca1 Using Zenject? DxMessaging integrates with Zenject! See DxMessaging + Zenject Integration Guide for step-by-step setup. Get DxMessaging's features (priorities, interceptors, Inspector debugging) with Zenject's DI.</p>"},{"location":"architecture/comparisons/#scriptable-object-architecture-soa","title":"Scriptable Object Architecture (SOA)","text":"<p>What It Is: A Unity-specific pattern popularized by Ryan Hipple's Unite 2017 talk that uses ScriptableObject assets for runtime communication (GameEvent, FloatVariable, etc.).</p> <p>Core Philosophy: Designer-driven, asset-based communication where systems communicate through serialized SO assets instead of direct references.</p> <p>\u26a0\ufe0f Contested Pattern: SOA has both proponents and critics. Supporters value its designer-friendly workflow and Inspector-based event wiring. Critics raise concerns about scalability and maintainability at scale. See Anti-ScriptableObject Architecture for one perspective on the criticisms. Unity recommends ScriptableObjects for immutable design data, not mutable runtime state.</p>"},{"location":"architecture/comparisons/#quick-comparison","title":"Quick Comparison","text":"Aspect SOA (GameEvent/Variables) DxMessaging Designer Control \u2705 High (create events in Inspector) \u274c Low (code-driven) Type Safety \u26a0\ufe0f Mixed (SO refs typed, but UnityEvent wiring loses compile-time safety) \u2705 Strong (compile-time validation) Lifecycle \u26a0\ufe0f Manual (assets persist) \u2705 Automatic (tokens clean up) Performance \u26a0\ufe0f List iteration, UnityAction overhead \u2705 Zero-allocation structs Testability \u26a0\ufe0f Requires SO asset cleanup \u2705 Isolated buses per test"},{"location":"architecture/comparisons/#when-to-use-each","title":"When to Use Each","text":""},{"location":"architecture/comparisons/#choose-soa-when","title":"Choose SOA when","text":"<ul> <li>Designers need to create and wire events in the Inspector without code</li> <li>Your team is already deeply invested in SOA with existing assets</li> <li>Designer empowerment is more important than code maintainability</li> </ul>"},{"location":"architecture/comparisons/#choose-dxmessaging-when","title":"Choose DxMessaging when","text":"<ul> <li>You need type-safe, code-driven messaging</li> <li>Performance and zero-allocation are priorities</li> <li>You want automatic lifecycle management</li> <li>You need interceptors, priorities, or global observers</li> </ul>"},{"location":"architecture/comparisons/#use-both-when","title":"Use Both when","text":"<ul> <li>ScriptableObjects for immutable config data (weapon stats, level configs)</li> <li>DxMessaging for runtime events and communication</li> <li>This is the recommended approach - use each tool correctly</li> </ul>"},{"location":"architecture/comparisons/#full-comparison-guide","title":"Full Comparison Guide","text":"<p>For detailed migration patterns, interoperability strategies, and code examples, see:</p>"},{"location":"architecture/comparisons/#-soa-compatibility-guide","title":"\u2192 SOA Compatibility Guide","text":"<p>Includes:</p> <ul> <li>Pattern A: Bridging SOA GameEvents to DxMessaging</li> <li>Pattern B: Proper ScriptableObject usage (configs + messaging)</li> <li>Migration path from SOA to DxMessaging</li> <li>When to keep using ScriptableObjects</li> </ul>"},{"location":"architecture/comparisons/#resources","title":"Resources","text":"<ul> <li>Unite 2017 Talk - Original SOA presentation</li> <li>Anti-SOA Critique - Detailed criticisms</li> <li>Unity Official Guide - Unity's perspective</li> </ul>"},{"location":"architecture/comparisons/#traditional-approaches","title":"Traditional Approaches","text":""},{"location":"architecture/comparisons/#standard-c-eventsactions","title":"Standard C# Events/Actions","text":"<p>What It Is: C#'s built-in event and delegate system. The default way to handle callbacks and notifications in .NET and Unity.</p> <p>Core Philosophy: Direct, type-safe callbacks between objects. Simple, familiar, and built into the language.</p>"},{"location":"architecture/comparisons/#key-features_3","title":"Key Features","text":"<ul> <li>Language-native: Built into C#, no dependencies</li> <li>Type-safe: Compile-time checking of event signatures</li> <li>Return values: Events can return values and use <code>out</code> parameters</li> <li>Inline lambdas: Subscribe with anonymous functions</li> <li>Multicast: Multiple subscribers per event</li> <li>Fast: Direct method invocation with minimal overhead</li> </ul>"},{"location":"architecture/comparisons/#code-example_3","title":"Code Example","text":"C#<pre><code>// Define and use C# events\npublic class GameManager : MonoBehaviour\n{\n public event Action<int> OnScoreChanged;\n\n public void AddScore(int points)\n {\n OnScoreChanged?.Invoke(points);\n }\n}\n\npublic class UI : MonoBehaviour\n{\n [SerializeField] private GameManager gameManager;\n\n void OnEnable()\n {\n gameManager.OnScoreChanged += UpdateScore;\n }\n\n void OnDisable()\n {\n gameManager.OnScoreChanged -= UpdateScore;\n }\n\n void UpdateScore(int points)\n {\n Debug.Log($\"Score: {points}\");\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_3","title":"What Problems It Solves","text":"<ul> <li>\u2705 Simple callbacks: Straightforward notification pattern</li> <li>\u2705 Type safety: Compile-time checking prevents errors</li> <li>\u2705 Return values: Can get feedback from event handlers</li> <li>\u2705 Performance: Minimal overhead, direct invocation</li> <li>\u2705 Familiarity: Every C# developer knows events</li> <li>\u2705 No dependencies: Built into the language</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_3","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c Memory leaks: Forgetting to unsubscribe causes leaks</li> <li>\u274c Tight coupling: Subscribers need direct references to event sources</li> <li>\u274c Execution order: Undefined handler invocation order</li> <li>\u274c Lifecycle management: Manual subscribe/unsubscribe in OnEnable/OnDisable</li> <li>\u274c Debugging: No visibility into who's subscribed or when events fire</li> <li>\u274c Validation/interception: No pipeline to modify or validate before handlers</li> <li>\u274c Global observation: Cannot listen to all events across the system</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_3","title":"Performance Characteristics","text":"<ul> <li>Fastest option: Direct method invocation (~50ns per call)</li> <li>Zero allocation: No GC pressure for basic events</li> <li>Inline-able: JIT can optimize simple event calls</li> <li>Use case: Best raw performance for simple notifications</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_3","title":"Learning Curve","text":"<ul> <li>Zero for C# developers: Standard language feature</li> <li>Immediate productivity: No new concepts to learn</li> <li>Estimated learning time: Already know it</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_3","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50\u2b50\u2b50 (Very easy)</li> <li>Familiar to all C# developers</li> <li>Straightforward mental model</li> <li>Easy to debug with breakpoints</li> </ul>"},{"location":"architecture/comparisons/#when-c-events-win","title":"When C# Events Win","text":"<ul> <li>\u2705 Small, stable scope (5-10 events max)</li> <li>\u2705 Need return values or <code>out</code> parameters</li> <li>\u2705 Writing a library (DxMessaging is Unity-specific)</li> <li>\u2705 Simple, local communication within a class or module</li> <li>\u2705 Team is C# experts, Unity beginners</li> <li>\u2705 Performance is absolutely critical (lowest overhead)</li> <li>\u2705 Quick prototypes or game jams</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_3","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Memory leaks are a problem (automatic lifecycle management)</li> <li>\u2705 Need decoupling (systems don't reference each other)</li> <li>\u2705 Execution order matters (priority-based handlers)</li> <li>\u2705 Debugging \"what fired when\" (Inspector message history)</li> <li>\u2705 Message validation/interception needed (interceptor pipeline)</li> <li>\u2705 Global observation needed (listen to all message instances)</li> <li>\u2705 Cross-system communication (10+ systems)</li> <li>\u2705 Long-term maintenance (months/years)</li> <li>\u2705 GameObject/Component targeting needed</li> <li>\u2705 Post-processing stage needed (analytics after handlers)</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_3","title":"Direct Comparison","text":"Aspect C# Events DxMessaging Primary Use Case Simple callbacks Pub/sub messaging Unity Compatibility \u2705 Built into C# \u2705 Built for Unity Dependencies \u2705 None (language) \u2705 Standalone Performance ~50ns/call (fastest) ~60ns/call Allocations \u2705 Zero (basic) \u2705 Zero (structs) Learning Curve \u2b50\u2b50\u2b50\u2b50\u2b50 None \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate DI Integration \u26a0\ufe0f Manual \u26a0\ufe0f Optional Async/Await \u26a0\ufe0f Manual \u26a0\ufe0f Manual Type Safety \u2705 Strong \u2705 Strong Lifecycle Management \u274c Manual unsubscribe \u2705 Automatic Execution Order \u274c Undefined \u2705 Priority-based GameObject Targeting \u274c Not built-in \u2705 Built-in Unity Integration \u2b50 None \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u274c Not built-in \u2705 Full pipeline Global Observers \u274c Not built-in \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50\u2b50 Hard to isolate \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50 Tight coupling \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Return Values \u2705 Yes \u274c Fire-and-forget <p>Bottom Line: C# events are the fastest and simplest for basic callbacks. DxMessaging is better for complex, decoupled systems where lifecycle management, debugging, and execution control matter.</p>"},{"location":"architecture/comparisons/#unityevents-inspector-wiring","title":"UnityEvents (Inspector Wiring)","text":"<p>What It Is: Unity's serializable event system that allows wiring callbacks in the Inspector. Designed for designer-friendly event hookups without code.</p> <p>Core Philosophy: Visual, Inspector-based event connections. Enable non-programmers to wire game logic through the editor.</p>"},{"location":"architecture/comparisons/#key-features_4","title":"Key Features","text":"<ul> <li>Inspector wiring: Drag-and-drop connections in Unity Inspector</li> <li>Serializable: Events saved with scenes and prefabs</li> <li>Designer-friendly: Non-programmers can wire logic</li> <li>Persistent references: Connections survive across sessions</li> <li>Dynamic parameters: Pass values from Inspector to callbacks</li> <li>No code required: Can wire entire behaviors without scripting</li> </ul>"},{"location":"architecture/comparisons/#code-example_4","title":"Code Example","text":"C#<pre><code>using UnityEngine;\nusing UnityEngine.Events;\n\npublic class Button : MonoBehaviour\n{\n public UnityEvent onClick;\n\n void OnMouseDown()\n {\n onClick?.Invoke();\n }\n}\n\npublic class UI : MonoBehaviour\n{\n public void ShowMenu()\n {\n Debug.Log(\"Menu shown\");\n }\n\n public void HideMenu()\n {\n Debug.Log(\"Menu hidden\");\n }\n}\n\n// In Inspector: Drag UI component to Button's onClick event\n// Select ShowMenu from dropdown\n// No additional code needed\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_4","title":"What Problems It Solves","text":"<ul> <li>\u2705 Visual wiring: See connections in Inspector</li> <li>\u2705 No code required: Designers can hook up events</li> <li>\u2705 Persistence: Connections saved with scenes/prefabs</li> <li>\u2705 Rapid prototyping: Quick iteration without scripting</li> <li>\u2705 Prefab workflows: Events work across prefab instances</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_4","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c Hidden dependencies: Connections invisible in code, hard to find during refactoring</li> <li>\u274c Brittle at scale: Renaming methods breaks wiring, no compile-time safety</li> <li>\u274c Execution order: Undefined call order for multiple subscribers</li> <li>\u274c No validation: No way to validate or intercept before invocation</li> <li>\u274c Performance: Slower than C# events due to reflection and boxing</li> <li>\u274c Debugging: Hard to trace \"who called what\" at runtime</li> <li>\u274c Merge conflicts: Inspector changes cause git conflicts</li> <li>\u274c Refactoring challenges: Renaming/moving methods silently breaks connections</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_4","title":"Performance Characteristics","text":"<ul> <li>Slow compared to alternatives: Reflection overhead, boxing for value types</li> <li>Allocations: Parameters boxed as objects, causes GC pressure</li> <li>Use case: Acceptable for UI and low-frequency events, avoid for high-frequency gameplay</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_4","title":"Learning Curve","text":"<ul> <li>Very easy: Point-and-click in Inspector</li> <li>No coding knowledge needed: Accessible to designers</li> <li>Estimated learning time: 5-10 minutes</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_4","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50\u2b50 (Easy for wiring, hard for debugging)</li> <li>Simple to connect in Inspector</li> <li>Difficult to understand flow when reading code</li> <li>Hard to track down at scale (where is this method called from?)</li> </ul>"},{"location":"architecture/comparisons/#when-unityevents-win","title":"When UnityEvents Win","text":"<ul> <li>\u2705 Designers need to wire logic without code</li> <li>\u2705 Rapid prototyping with prefabs</li> <li>\u2705 Very simple games (mobile casual, hyper-casual)</li> <li>\u2705 UI interactions with minimal logic</li> <li>\u2705 Small projects (<5 scripts)</li> <li>\u2705 One-off connections that rarely change</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_4","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Code-first development (programmers prefer code visibility)</li> <li>\u2705 Refactoring frequently (compile-time safety)</li> <li>\u2705 Execution order matters (priority-based handlers)</li> <li>\u2705 Need validation/interception (interceptor pipeline)</li> <li>\u2705 Performance-sensitive (zero allocation required)</li> <li>\u2705 Debugging observability (message history)</li> <li>\u2705 Cross-system communication (10+ components)</li> <li>\u2705 Team collaboration (merge-friendly code over Inspector)</li> <li>\u2705 Long-term maintenance (find usages, refactor safely)</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_4","title":"Direct Comparison","text":"Aspect UnityEvents DxMessaging Primary Use Case Inspector wiring Pub/sub messaging Unity Compatibility \u2705 Built into Unity \u2705 Built for Unity Dependencies \u2705 None (Unity) \u2705 Standalone Performance Slow (serialization) ~60ns/call Allocations \u274c Boxing \u2705 Zero (structs) Learning Curve \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Inspector \u2b50\u2b50\u2b50 Code-based DI Integration \u274c No \u26a0\ufe0f Optional Async/Await \u274c No \u26a0\ufe0f Manual Type Safety \u2b50\u2b50 Weak (serialized) \u2705 Strong Lifecycle Management \u26a0\ufe0f Unity-managed \u2705 Automatic Execution Order \u274c Undefined \u2705 Priority-based GameObject Targeting \u26a0\ufe0f Manual references \u2705 Built-in Unity Integration \u2b50\u2b50\u2b50 Inspector-based \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u2b50\u2b50 Connections only \u2705 History + stats Interceptors \u274c Not built-in \u2705 Full pipeline Global Observers \u274c Not possible \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50\u2b50 Scene setup \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50\u2b50 Hidden refs \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Refactoring Safety \u274c Silent breakage \u2705 Compile-time errors Code Visibility \u274c Hidden in Inspector \u2705 Explicit in code <p>Bottom Line: UnityEvents are well-suited for simple Inspector-based wiring and designer workflows. DxMessaging is better for code-first development, refactoring safety, and complex messaging needs.</p>"},{"location":"architecture/comparisons/#unity-sendmessage","title":"Unity SendMessage","text":"<p>What It Is: Unity's legacy reflection-based message system. Calls methods by name on GameObjects and their components.</p> <p>Core Philosophy: String-based, reflection-driven communication. Designed for simplicity and GameObject hierarchy traversal.</p>"},{"location":"architecture/comparisons/#key-features_5","title":"Key Features","text":"<ul> <li>String-based: Call methods by name without references</li> <li>Hierarchy traversal: SendMessageUpwards, BroadcastMessage for parent/child searching</li> <li>No dependencies: Built into Unity GameObject</li> <li>Simple API: One-line method calls</li> <li>GameObject-centric: Works with Unity's component model</li> <li>Optional receivers: Methods don't need to exist (SendMessageOptions.DontRequireReceiver)</li> </ul>"},{"location":"architecture/comparisons/#code-example_5","title":"Code Example","text":"C#<pre><code>using UnityEngine;\n\npublic class Enemy : MonoBehaviour\n{\n void TakeDamage(int amount)\n {\n Debug.Log($\"Took {amount} damage\");\n }\n}\n\npublic class Weapon : MonoBehaviour\n{\n void Attack(GameObject target)\n {\n // Call TakeDamage on target GameObject\n target.SendMessage(\"TakeDamage\", 10);\n }\n\n void AttackUpwards()\n {\n // Call on this GameObject and all parents\n SendMessageUpwards(\"TakeDamage\", 5, SendMessageOptions.DontRequireReceiver);\n }\n\n void AttackChildren()\n {\n // Call on this GameObject and all children\n BroadcastMessage(\"TakeDamage\", 3);\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_5","title":"What Problems It Solves","text":"<ul> <li>\u2705 No references needed: Call methods without GetComponent</li> <li>\u2705 Hierarchy traversal: Easy parent/child communication</li> <li>\u2705 Simple API: One-line method invocation</li> <li>\u2705 Optional receivers: Can call non-existent methods safely</li> <li>\u2705 Built-in: No setup or dependencies</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_5","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c No type safety: String-based, typos cause silent failures</li> <li>\u274c Slow performance: Reflection overhead on every call</li> <li>\u274c Limited parameters: Only 0 or 1 parameter supported</li> <li>\u274c Boxing allocations: Value types boxed to object, causes GC</li> <li>\u274c Hard to debug: No compile-time checking, no IDE \"Find Usages\"</li> <li>\u274c Refactoring difficulty: Renaming methods breaks string references</li> <li>\u274c No validation: No way to validate or intercept messages</li> <li>\u274c Execution order: Undefined call order for multiple receivers</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_5","title":"Performance Characteristics","text":"<ul> <li>Very slow: Reflection overhead much worse than events or messaging systems</li> <li>Allocations: Boxing value type parameters causes GC pressure</li> <li>Use case: Legacy code only; avoid for new development</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_5","title":"Learning Curve","text":"<ul> <li>Very easy: Simple one-line API</li> <li>Immediate productivity: No setup required</li> <li>Estimated learning time: 5 minutes</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_5","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50 (Simple to use, hard to maintain)</li> <li>Easy to write initially</li> <li>Difficult to track method calls (no Find Usages)</li> <li>Refactoring breaks string references silently</li> </ul>"},{"location":"architecture/comparisons/#when-unity-sendmessage-wins","title":"When Unity SendMessage Wins","text":"<ul> <li>\u2705 Legacy code that already uses it</li> <li>\u2705 Quick prototypes (throwaway code)</li> <li>\u2705 Simple tutorials or learning examples</li> <li>\u2705 Calling optional methods that may not exist</li> <li>\u2705 GameObject hierarchies with optional components (DontRequireReceiver pattern)</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_5","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 Type safety required (compile-time checking)</li> <li>\u2705 Performance matters (zero allocation, no reflection)</li> <li>\u2705 Multiple parameters needed (struct fields)</li> <li>\u2705 Refactoring frequently (find usages, rename safely)</li> <li>\u2705 Debugging observability (message history)</li> <li>\u2705 Execution order control (priority-based handlers)</li> <li>\u2705 Message validation/interception (interceptor pipeline)</li> <li>\u2705 Production code (maintainability over simplicity)</li> <li>\u2705 Modern projects (avoid legacy patterns)</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_5","title":"Direct Comparison","text":"Aspect Unity SendMessage DxMessaging Primary Use Case Legacy GameObject calls Pub/sub messaging Unity Compatibility \u2705 Built into Unity \u2705 Built for Unity Dependencies \u2705 None (Unity) \u2705 Standalone Performance Very slow (reflection) ~60ns/call Allocations \u274c Heavy boxing \u2705 Zero (structs) Learning Curve \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 None \u2b50\u2b50\u2b50 Moderate DI Integration \u274c No \u26a0\ufe0f Optional Async/Await \u274c No \u26a0\ufe0f Manual Type Safety \u274c String-based \u2705 Strong Lifecycle Management \u274c None \u2705 Automatic Execution Order \u274c Undefined \u2705 Priority-based GameObject Targeting \u2705 Hierarchy traversal \u2705 Built-in (ID-based) Unity Integration \u2b50\u2b50 Legacy API \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u274c Not built-in \u2705 Full pipeline Global Observers \u274c Not possible \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50\u2b50 Requires GameObjects \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50\u2b50 String-based \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Refactoring Safety \u274c Silent breakage \u2705 Compile-time errors Parameters \u26a0\ufe0f 0 or 1 only \u2705 Unlimited (struct fields) <p>Bottom Line: SendMessage is legacy Unity API. Use only for maintaining old code. DxMessaging provides all the same capabilities with type safety, performance, and modern tooling.</p> <p>Migration Path: DxMessaging provides <code>ReflexiveMessage</code> to bridge legacy SendMessage behavior:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\n// Legacy SendMessage equivalent\nInstanceId target = gameObject;\nvar msg = new ReflexiveMessage(\"OnHit\", ReflexiveSendMode.Upwards, 10);\nMessageHandler.MessageBus.TargetedBroadcast(ref target, ref msg);\n\n// Prefer typed messages for new code:\n// - Multiple parameters via struct fields\n// - By-ref handlers avoid boxing\n// - Compile-time safety\n</code></pre>"},{"location":"architecture/comparisons/#global-event-bus-singletons","title":"Global Event Bus Singletons","text":"<p>What It Is: A static/singleton class that centralizes all events in one global location. Common pattern for decoupling without dependency injection.</p> <p>Core Philosophy: Central event hub accessible from anywhere. Simplify communication through a single global entry point.</p>"},{"location":"architecture/comparisons/#key-features_6","title":"Key Features","text":"<ul> <li>Global access: Static class available everywhere</li> <li>No references needed: No GetComponent or serialized fields</li> <li>Simple pattern: Easy to understand and implement</li> <li>Decoupling: Publishers and subscribers don't know about each other</li> <li>Flexibility: Can add events without changing existing code</li> </ul>"},{"location":"architecture/comparisons/#code-example_6","title":"Code Example","text":"C#<pre><code>using System;\nusing UnityEngine;\n\npublic static class EventHub\n{\n public static event Action<int> OnDamage;\n public static event Action<string> OnEnemyKilled;\n public static event Action OnGameOver;\n\n public static void RaiseDamage(int amount) => OnDamage?.Invoke(amount);\n public static void RaiseEnemyKilled(string enemyType) => OnEnemyKilled?.Invoke(enemyType);\n public static void RaiseGameOver() => OnGameOver?.Invoke();\n}\n\n// Producer\npublic class Enemy : MonoBehaviour\n{\n void Die()\n {\n EventHub.RaiseEnemyKilled(\"Orc\");\n }\n}\n\n// Consumer\npublic class UI : MonoBehaviour\n{\n void OnEnable()\n {\n EventHub.OnEnemyKilled += HandleEnemyKilled;\n }\n\n void OnDisable()\n {\n EventHub.OnEnemyKilled -= HandleEnemyKilled;\n }\n\n void HandleEnemyKilled(string enemyType)\n {\n Debug.Log($\"Enemy killed: {enemyType}\");\n }\n}\n</code></pre>"},{"location":"architecture/comparisons/#what-problems-it-solves_6","title":"What Problems It Solves","text":"<ul> <li>\u2705 Global decoupling: No direct references between systems</li> <li>\u2705 Easy to add events: Just add to static class</li> <li>\u2705 Simple pattern: Straightforward to implement and understand</li> <li>\u2705 No setup: No DI container or framework needed</li> </ul>"},{"location":"architecture/comparisons/#what-problems-it-doesnt-solve-well_6","title":"What Problems It Doesn't Solve Well","text":"<ul> <li>\u274c Memory leaks: Still manual subscribe/unsubscribe (same as C# events)</li> <li>\u274c Global state: Everything in one bag, hard to organize at scale</li> <li>\u274c Execution order: Undefined handler invocation order</li> <li>\u274c Testing difficulty: Global state makes unit testing hard</li> <li>\u274c Naming conflicts: All events in same namespace, naming gets messy</li> <li>\u274c No validation: No way to intercept or validate messages</li> <li>\u274c No observability: Can't see who's subscribed or message history</li> <li>\u274c Ownership unclear: Who manages what events?</li> <li>\u274c Lifecycle management: Manual subscribe/unsubscribe required</li> </ul>"},{"location":"architecture/comparisons/#performance-characteristics_6","title":"Performance Characteristics","text":"<ul> <li>Good performance: Similar to C# events (static overhead is minimal)</li> <li>Zero allocation: No GC pressure for basic events</li> <li>Use case: Acceptable for most scenarios</li> </ul>"},{"location":"architecture/comparisons/#learning-curve_6","title":"Learning Curve","text":"<ul> <li>Very easy: Just a static class with events</li> <li>Immediate productivity: No new concepts</li> <li>Estimated learning time: 10 minutes</li> </ul>"},{"location":"architecture/comparisons/#ease-of-understanding_6","title":"Ease of Understanding","text":"<ul> <li>\u2b50\u2b50\u2b50\u2b50 (Easy initially, hard at scale)</li> <li>Simple pattern to grasp</li> <li>Becomes messy with 20+ events</li> <li>Hard to track ownership and responsibilities</li> </ul>"},{"location":"architecture/comparisons/#when-static-event-bus-wins","title":"When Static Event Bus Wins","text":"<ul> <li>\u2705 You've already built one and it works</li> <li>\u2705 Very simple use cases (just need globals)</li> <li>\u2705 Small projects (<10 events)</li> <li>\u2705 No framework dependencies desired</li> <li>\u2705 Quick prototypes</li> </ul>"},{"location":"architecture/comparisons/#when-dxmessaging-wins_6","title":"When DxMessaging Wins","text":"<ul> <li>\u2705 More than 10-15 events (organization becomes important)</li> <li>\u2705 Memory leaks are a concern (automatic lifecycle management)</li> <li>\u2705 Execution order matters (priority-based handlers)</li> <li>\u2705 Need message validation/interception (interceptor pipeline)</li> <li>\u2705 Testing is important (local buses for isolation)</li> <li>\u2705 Observability needed (Inspector debugging, message history)</li> <li>\u2705 Multiple subsystems (namespacing and organization)</li> <li>\u2705 GameObject/Component targeting needed</li> <li>\u2705 Global observation needed (listen to all message instances)</li> <li>\u2705 Post-processing needed (analytics after handlers)</li> <li>\u2705 Long-term maintenance (structure prevents chaos)</li> </ul>"},{"location":"architecture/comparisons/#direct-comparison_6","title":"Direct Comparison","text":"Aspect Static Event Bus DxMessaging Primary Use Case Global event hub Pub/sub messaging Unity Compatibility \u2705 Works in Unity \u2705 Built for Unity Dependencies \u2705 None (custom) \u2705 Standalone Performance ~50ns/call (fast) ~60ns/call Allocations \u2705 Zero (basic) \u2705 Zero (structs) Learning Curve \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate DI Integration \u26a0\ufe0f Manual \u26a0\ufe0f Optional Async/Await \u26a0\ufe0f Manual \u26a0\ufe0f Manual Type Safety \u2705 Strong \u2705 Strong Lifecycle Management \u274c Manual unsubscribe \u2705 Automatic Execution Order \u274c Undefined \u2705 Priority-based GameObject Targeting \u274c Not built-in \u2705 Built-in Unity Integration \u2b50 None \u2b50\u2b50\u2b50\u2b50\u2b50 Deep Inspector Debugging \u274c No \u2705 History + stats Interceptors \u274c Not built-in \u2705 Full pipeline Global Observers \u274c Not built-in \u2705 Listen to all Post-Processing \u274c Not built-in \u2705 Dedicated stage Testability \u2b50 Hard (global) \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses Decoupling \u2b50\u2b50\u2b50\u2b50 Good \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Organization \u2b50\u2b50 One big class \u2b50\u2b50\u2b50\u2b50\u2b50 Structured <p>Bottom Line: Static event buses solve global access but inherit all the problems of C# events (leaks, undefined order, no observability). DxMessaging provides the same global access with lifecycle safety, structure, and debugging tools.</p> <p>Migration Path: DxMessaging can replace static event buses gradually:</p> C#<pre><code>// Old static event bus\nEventHub.RaiseDamage(5);\n\n// DxMessaging equivalent (global bus)\nvar damage = new TookDamage(5);\ndamage.Emit();\n\n// Or use local buses for subsystems\nvar combatBus = new MessageBus();\nvar combatDamage = new TookDamage(5);\ncombatDamage.Emit(combatBus);\n</code></pre>"},{"location":"architecture/comparisons/#honest-trade-offs-what-you-give-up-what-you-gain","title":"Honest Trade-offs: What You Give Up, What You Gain","text":"<p>DxMessaging involves trade-offs like any architectural choice. This section describes what you gain and what you sacrifice when adopting it.</p> <p>Bottom line first: For game jam prototypes, C# events are faster to write. For projects you'll maintain for months, DxMessaging becomes more valuable as project complexity increases.</p>"},{"location":"architecture/comparisons/#learning-curve_7","title":"Learning Curve","text":""},{"location":"architecture/comparisons/#what-you-give-up","title":"What You Give Up","text":"<ul> <li>\u274c Immediate productivity - ~1-2 days to feel comfortable (reading docs, trying examples)</li> <li>\u274c Familiarity - Your team knows C# events already; DxMessaging is new</li> <li>\u274c \"Just works\" intuition - You need to think: \"Which message type? What priority?\"</li> </ul> <p>Your first message will take 15 minutes. By the 10<sup>th</sup> message, you'll be faster than with events.</p>"},{"location":"architecture/comparisons/#what-you-gain","title":"What You Gain","text":"<ul> <li>\u2705 Long-term velocity - Adding new features doesn't require touching 5 existing systems</li> <li>\u2705 Debugging is faster - Inspector shows \"what fired when\" instantly</li> <li>\u2705 Onboarding is easier - New devs see explicit message contracts, not hidden event chains</li> </ul> <p>Example: Junior dev asks \"How does damage work?\"</p> <ul> <li>C# events: \"Uh, Player has an OnDamaged event, and HealthBar subscribes in line 47, and...\"</li> <li>DxMessaging: \"Search for <code>TookDamage</code> message, see who emits it and who listens.\"</li> </ul>"},{"location":"architecture/comparisons/#verdict","title":"Verdict","text":"<ul> <li>Game jam (1 week project): Learning curve not worth it \u2192 Stick with C# events</li> <li>Mid-size game (1+ month): Pays off by week 2</li> <li>Large game (6+ months): Highly beneficial</li> </ul>"},{"location":"architecture/comparisons/#boilerplate","title":"Boilerplate","text":""},{"location":"architecture/comparisons/#what-you-give-up_1","title":"What You Give Up","text":"<ul> <li>\u274c \"One-liners\" - C# events can be <code>public event Action OnClick;</code> done</li> <li>\u274c Quick and dirty - Need to define message struct, attributes, handler registration</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_1","title":"What You Gain","text":"<ul> <li>\u2705 Explicit contracts - Messages are discoverable types, not hidden delegates</li> <li>\u2705 Auto-generated code - <code>[DxAutoConstructor]</code> reduces boilerplate</li> <li>\u2705 Compile-time safety - Refactors update all usages</li> </ul>"},{"location":"architecture/comparisons/#example-comparison","title":"Example Comparison","text":"C#<pre><code>// C# Event (minimal boilerplate)\npublic event Action<int> OnDamage;\nOnDamage?.Invoke(5);\n\n// DxMessaging (more upfront definition)\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\nvar msg = new TookDamage(5);\nmsg.EmitGameObjectBroadcast(gameObject);\n</code></pre> <p>Verdict: For 1-3 simple events, C# events win on brevity. For 10+ events with complex flows, DxMessaging's structure pays dividends.</p>"},{"location":"architecture/comparisons/#performance","title":"Performance","text":""},{"location":"architecture/comparisons/#what-you-give-up_2","title":"What You Give Up","text":"<ul> <li>\u274c Absolute minimal overhead - Raw C# events/delegates are faster (~10ns per call)</li> <li>\u274c Zero abstraction cost - Direct calls can be inlined by the compiler</li> <li>\u274c Simplicity in profiler - One extra layer in call stack</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_2","title":"What You Gain","text":"<ul> <li>\u2705 Zero-allocation struct messages - No GC pressure from boxing</li> <li>\u2705 Predictable performance - No hidden allocations from lambdas</li> <li>\u2705 Scalable diagnostics - Built-in profiling/logging without custom instrumentation</li> </ul>"},{"location":"architecture/comparisons/#hard-numbers","title":"Hard Numbers","text":"<ul> <li>C# event invoke: ~50ns baseline</li> <li>DxMessaging handler: ~60ns (~10ns overhead)</li> <li>Memory: Zero allocations for struct messages</li> </ul> <p>Verdict: For UI, gameplay events, scene management \u2192 DxMessaging overhead is negligible. For ECS architectures processing millions of events per frame \u2192 consider raw delegates or native code, which are better suited for that specific use case.</p>"},{"location":"architecture/comparisons/#flexibility","title":"Flexibility","text":""},{"location":"architecture/comparisons/#what-you-give-up_3","title":"What You Give Up","text":"<ul> <li>\u274c Return values - DxMessaging is fire-and-forget (no synchronous responses)</li> <li>\u274c Out parameters - Can't use <code>out</code> or <code>ref</code> for bidirectional communication</li> <li>\u274c Dynamic subscriptions - Can't easily pass lambdas inline</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_3","title":"What You Gain","text":"<ul> <li>\u2705 Interception - Validate/transform messages before handlers</li> <li>\u2705 Post-processing - Analytics/logging without polluting handlers</li> <li>\u2705 Priority control - Explicit execution order</li> <li>\u2705 Context - Always know who sent/received</li> </ul>"},{"location":"architecture/comparisons/#when-limitations-hurt","title":"When Limitations Hurt","text":"C#<pre><code>// C# events can return values (DxMessaging can't)\npublic delegate bool DamageValidator(int amount);\npublic event DamageValidator OnValidateDamage;\n\nif (OnValidateDamage?.Invoke(damage) == true) {\n // Allowed\n}\n\n// DxMessaging workaround: Use interceptors or separate query pattern\n</code></pre> <p>Verdict: If you need synchronous request/response, C# delegates/events or direct method calls are better. DxMessaging is designed for notifications and commands.</p>"},{"location":"architecture/comparisons/#debuggability","title":"Debuggability","text":""},{"location":"architecture/comparisons/#what-you-give-up_4","title":"What You Give Up","text":"<ul> <li>\u274c Simplicity - Stack traces show message bus internals</li> <li>\u274c Step-through - Can't F11 directly from emit to handler (need breakpoints)</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_4","title":"What You Gain","text":"<ul> <li>\u2705 Message history - See last N messages in Inspector</li> <li>\u2705 Registration view - Know exactly who's listening</li> <li>\u2705 Global observability - Track all messages without instrumenting code</li> <li>\u2705 Filtering - Intercept messages for debugging without changing code</li> </ul>"},{"location":"architecture/comparisons/#example-finding-who-fired-a-message","title":"Example: Finding Who Fired a Message","text":"C#<pre><code>// C# events: Set breakpoint on every possible Invoke(), or add logging everywhere\nOnDamage?.Invoke(5); // Where did this come from??\n\n// DxMessaging: Check Inspector message history or add global logger\n_ = debugToken.RegisterBroadcastWithoutSource<TookDamage>(\n (src, msg) => Debug.Log($\"Damage from {src}: {msg.amount}\")\n);\n</code></pre> <p>Verdict: Initial debugging is slightly harder (extra layer), but systemic debugging is easier (observability tools).</p>"},{"location":"architecture/comparisons/#coupling-and-architecture","title":"Coupling and Architecture","text":""},{"location":"architecture/comparisons/#what-you-give-up_5","title":"What You Give Up","text":"<ul> <li>\u274c Quick hacks - Can't just <code>GetComponent<T>().DoThing()</code> anymore</li> <li>\u274c Direct inspector wiring - Can't drag-and-drop references to emit messages</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_5","title":"What You Gain","text":"<ul> <li>\u2705 True decoupling - Systems don't know about each other</li> <li>\u2705 Testability - Easy to isolate with local buses</li> <li>\u2705 Refactorability - Move/rename components without breaking wiring</li> </ul>"},{"location":"architecture/comparisons/#impact-on-architecture","title":"Impact on Architecture","text":"<p>Before (tight coupling):</p> Text Only<pre><code>UI \u2192 References 15 systems\nSystem A \u2192 References System B, C, D\nEvery change ripples through dependencies\n</code></pre> <p>After (loose coupling):</p> Text Only<pre><code>All systems \u2192 Emit messages\nAll systems \u2192 Listen to messages\nAdd/remove systems without affecting others\n</code></pre> <p>Verdict: If your project is <5k lines, tight coupling is manageable. For larger projects, DxMessaging's decoupling significantly improves maintainability.</p>"},{"location":"architecture/comparisons/#testing","title":"Testing","text":""},{"location":"architecture/comparisons/#what-you-give-up_6","title":"What You Give Up","text":"<ul> <li>\u274c Simplicity - Can't just mock an event subscription</li> </ul>"},{"location":"architecture/comparisons/#what-you-gain_6","title":"What You Gain","text":"<ul> <li>\u2705 Isolation - Local buses per test, zero global state</li> <li>\u2705 Observability - Count messages, inspect payloads easily</li> <li>\u2705 Determinism - Priority-based ordering eliminates flakiness</li> </ul>"},{"location":"architecture/comparisons/#example","title":"Example","text":"C#<pre><code>// Test with isolated bus\n[Test]\npublic void TestAchievementSystem() {\n var testBus = new MessageBus();\n var token = MessageRegistrationToken.Create(achievementHandler, testBus);\n\n var msg = new EnemyKilled(\"Boss\", 10);\n msg.EmitGameObjectBroadcast(enemy, testBus);\n\n Assert.IsTrue(achievementSystem.Unlocked(\"BossSlayer\"));\n}\n</code></pre> <p>Verdict: DxMessaging makes integration testing easier, unit testing slightly more verbose.</p>"},{"location":"architecture/comparisons/#feature-by-feature-comparison-matrix","title":"Feature-by-Feature Comparison Matrix","text":""},{"location":"architecture/comparisons/#unity-messaging-frameworks-comparison","title":"Unity Messaging Frameworks Comparison","text":"Aspect DxMessaging UniRx MessagePipe Zenject Signals Primary Use Case Pub/sub messaging Stream transformations High-perf DI messaging DI-integrated messaging Unity Compatibility \u2705 Built for Unity \u2705 Built for Unity \u2705 Built for Unity \u2705 Built for Unity Performance \u2b50\u2b50\u2b50\u2b50 Good (14M) \u2b50\u2b50\u2b50\u2b50 Good (18M) \u2b50\u2b50\u2b50\u2b50\u2b50 Best (97M) \u2b50\u2b50 Moderate (2.5M) Zero Allocations \u2705 Yes (structs) \u26a0\ufe0f Can allocate \u2705 Yes (structs) \u26a0\ufe0f Can allocate Unity Integration \u2b50\u2b50\u2b50\u2b50\u2b50 Deep (lifecycle) \u2b50\u2b50\u2b50\u2b50 Good (UI/async) \u2b50\u2b50\u2b50 Basic (no lifecycle) \u2b50\u2b50\u2b50\u2b50 Good (DI-managed) Inspector Debugging \u2705 Yes (history + stats) \u274c No \u274c No \u274c No Execution Order \u2705 Priority-based \u274c Not built-in \u274c Subscription order \u274c Not built-in Lifecycle Management \u2705 Automatic (MonoBehaviour) \u26a0\ufe0f Manual dispose \u26a0\ufe0f Manual dispose \u26a0\ufe0f DI-managed Learning Curve \u2b50\u2b50\u2b50 Moderate \u2b50\u2b50 Steep (Rx paradigm) \u2b50\u2b50\u2b50\u2b50 Moderate (DI) \u2b50\u2b50 Steep (DI+Signals) Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Plug-and-play \u2b50\u2b50\u2b50\u2b50\u2b50 Low \u2b50\u2b50\u2b50 DI setup required \u2b50\u2b50 Installers required DI Integration \u26a0\ufe0f Optional \u26a0\ufe0f Optional \u2705 First-class \u2705 Required (Zenject) Async/Await \u26a0\ufe0f Manual \u2705 Native (observables) \u2705 Native \u2705 Yes Message Validation \u2705 Interceptor pipeline \u274c Not built-in \u26a0\ufe0f Filters (middleware) \u274c Not built-in GameObject Targeting \u2705 Built-in \u274c Not designed for \u274c Not built-in \u274c Not built-in Global Observers \u2705 Listen to all sources \u274c Not built-in \u274c Not built-in \u274c Not built-in Post-Processing \u2705 Dedicated stage \u274c Not built-in \u26a0\ufe0f Via filters \u274c Not built-in Stream Operators \u274c Not built-in \u2705 Extensive (LINQ) \u274c Not built-in \u26a0\ufe0f With UniRx Testability \u2b50\u2b50\u2b50\u2b50\u2b50 Local buses \u2b50\u2b50\u2b50\u2b50 Good \u2b50\u2b50\u2b50\u2b50\u2b50 DI mocking \u2b50\u2b50\u2b50\u2b50\u2b50 DI mocking Decoupling \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Type Safety \u2b50\u2b50\u2b50\u2b50\u2b50 Strong \u2b50\u2b50\u2b50\u2b50\u2b50 Strong \u2b50\u2b50\u2b50\u2b50\u2b50 Strong \u2b50\u2b50\u2b50\u2b50\u2b50 Strong Dependencies \u2705 None \u2705 None \u26a0\ufe0f MessagePipe package \u274c Zenject required"},{"location":"architecture/comparisons/#traditional-approaches-comparison","title":"Traditional Approaches Comparison","text":"Aspect C# Events UnityEvents SOA (GameEvent) Static Bus DxMessaging Setup Complexity \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50\u2b50 Simple \u2b50\u2b50 Asset creation \u2b50\u2b50\u2b50 Moderate \u2b50\u2b50\u2b50 Moderate Boilerplate \u2b50\u2b50\u2b50\u2b50\u2b50 Low \u2b50\u2b50\u2b50\u2b50\u2b50 Low \u2b50\u2b50 High \u2b50\u2b50\u2b50 Medium \u2b50\u2b50\u2b50 Medium Performance \u2b50\u2b50\u2b50\u2b50\u2b50 Fastest \u2b50\u2b50 Slow (boxing) \u2b50\u2b50\u2b50 Moderate \u2b50\u2b50\u2b50\u2b50 Fast \u2b50\u2b50\u2b50\u2b50 Fast Decoupling \u2b50 Tight \u2b50\u2b50 Hidden \u2b50\u2b50\u2b50\u2b50 Good \u2b50\u2b50\u2b50\u2b50 Good \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent Designer Control \u2b50 None \u2b50\u2b50\u2b50\u2b50\u2b50 High \u2b50\u2b50\u2b50\u2b50\u2b50 High \u2b50 None \u2b50 None Lifecycle Safety \u2b50 Manual \u2b50\u2b50\u2b50 Unity-managed \u2b50\u2b50 Manual persist \u2b50 Manual \u2b50\u2b50\u2b50\u2b50\u2b50 Automatic Observability \u2b50 None \u2b50 None \u2b50 Inspector only \u2b50 None \u2b50\u2b50\u2b50\u2b50\u2b50 Built-in Execution Order \u2b50 Undefined \u2b50 Undefined \u2b50 Undefined \u2b50 Undefined \u2b50\u2b50\u2b50\u2b50\u2b50 Priority-based Type Safety \u2b50\u2b50\u2b50\u2b50\u2b50 Strong \u2b50\u2b50 Weak \u2b50\u2b50\u2b50 Mixed \u2b50\u2b50\u2b50 Varies \u2b50\u2b50\u2b50\u2b50\u2b50 Strong Testability \u2b50\u2b50 Hard \u2b50\u2b50 Hard \u2b50 Very Hard \u2b50 Very Hard \u2b50\u2b50\u2b50\u2b50\u2b50 Easy Learning Curve \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50\u2b50\u2b50 Minimal \u2b50\u2b50\u2b50 Moderate \u2b50\u2b50\u2b50\u2b50 Low \u2b50\u2b50\u2b50 Moderate Memory Safety \u2b50 Leak-prone \u2b50\u2b50\u2b50 Unity-managed \u2b50\u2b50 Asset persist \u2b50 Leak-prone \u2b50\u2b50\u2b50\u2b50\u2b50 Leak-free Debugging \u2b50\u2b50 Hard at scale \u2b50\u2b50 Hard at scale \u2b50\u2b50 Inspector-only \u2b50 Very Hard \u2b50\u2b50\u2b50\u2b50\u2b50 Excellent"},{"location":"architecture/comparisons/#overall-verdict-by-use-case","title":"Overall Verdict by Use Case","text":"<ul> <li>Small prototype/jam: C# Events or UnityEvents win (simplicity > all)</li> <li>Mid-size game (5-20k lines): DxMessaging starts paying off (decoupling, debugging)</li> <li>Large game (20k+ lines): DxMessaging essential for maintainability</li> <li>Designer-driven workflow: SOA has value (Inspector wiring) but consider maintenance costs</li> <li>Legacy SOA project: Use Pattern B (keep SOs for configs, migrate events to DxMessaging)</li> <li>Performance-critical (millions of messages/frame): MessagePipe offers the highest throughput</li> <li>Performance-critical (Unity-specific): DxMessaging (strong performance + Unity integration)</li> <li>UI-heavy: DxMessaging provides decoupled updates and global observers for UI state</li> <li>Complex event transformations: UniRx provides reactive stream operators</li> <li>DI-first architecture: MessagePipe or Zenject Signals win (DI integration)</li> <li>Analytics/diagnostics heavy: DxMessaging provides built-in support (global observers, post-processors, Inspector)</li> <li>Need execution control: DxMessaging offers priorities, interceptors, and ordered stages</li> </ul>"},{"location":"architecture/comparisons/#when-each-approach-actually-wins","title":"When Each Approach ACTUALLY Wins","text":""},{"location":"architecture/comparisons/#dxmessaging-wins-when","title":"DxMessaging Wins When","text":"<ul> <li>\u2705 Unity-first projects (MonoBehaviour lifecycle integration)</li> <li>\u2705 10+ systems that communicate (pub/sub decoupling)</li> <li>\u2705 Observability essential (Inspector debugging, message history)</li> <li>\u2705 Memory leaks are a concern (automatic lifecycle management)</li> <li>\u2705 Cross-team development (clear message contracts)</li> <li>\u2705 Long-term maintenance (years, not weeks)</li> <li>\u2705 GameObject/Component targeting needed (Unity-specific patterns)</li> <li>\u2705 Execution order control essential (priority-based handlers)</li> <li>\u2705 Message validation/transformation needed (interceptor pipeline)</li> <li>\u2705 Global observation needed (listen to all message instances)</li> <li>\u2705 Post-processing needed (analytics, logging after handlers)</li> <li>\u2705 Late update semantics needed (timing-specific processing)</li> <li>\u2705 Teams without DI experience (no framework dependencies)</li> <li>\u2705 Want plug-and-play solution (zero dependencies, immediate use)</li> </ul>"},{"location":"architecture/comparisons/#unirx-wins-when","title":"UniRx Wins When","text":"<ul> <li>\u2705 Simple pub/sub with minimal setup (MessageBroker is straightforward)</li> <li>\u2705 Complex event stream transformations needed</li> <li>\u2705 Time-based operations (throttle, debounce, buffer)</li> <li>\u2705 Combining multiple input sources</li> <li>\u2705 Reactive UI data binding</li> <li>\u2705 Team familiar with reactive programming</li> <li>\u2705 Need LINQ-style query operators on events</li> <li>\u2705 Async operations with cancellation and composition</li> </ul>"},{"location":"architecture/comparisons/#messagepipe-wins-when","title":"MessagePipe Wins When","text":"<ul> <li>\u2705 Performance is THE priority (highest throughput)</li> <li>\u2705 Already using DI (VContainer, Zenject, etc.)</li> <li>\u2705 Cross-platform .NET projects (not Unity-only)</li> <li>\u2705 Need native async/await support</li> <li>\u2705 Large-scale projects with DI architecture</li> <li>\u2705 Want compile-time leak detection (Roslyn analyzer)</li> <li>\u2705 High message frequency (thousands/frame)</li> </ul>"},{"location":"architecture/comparisons/#zenject-signals-win-when","title":"Zenject Signals Win When","text":"<ul> <li>\u2705 Already using Zenject for dependency injection</li> <li>\u2705 Testability through DI is critical</li> <li>\u2705 Need subscriber validation (ensure handlers exist)</li> <li>\u2705 Team experienced with Zenject</li> <li>\u2705 Want DI-managed lifecycle</li> <li>\u2705 Integration with existing Zenject architecture</li> </ul>"},{"location":"architecture/comparisons/#c-events-win-when","title":"C# Events Win When","text":"<ul> <li>\u2705 You need return values or out parameters</li> <li>\u2705 Writing a library (DxMessaging is Unity-specific)</li> <li>\u2705 Small, stable scope (5-10 events max)</li> <li>\u2705 Team is C# experts, Unity beginners</li> </ul>"},{"location":"architecture/comparisons/#unityevents-win-when","title":"UnityEvents Win When","text":"<ul> <li>\u2705 Designers need to wire logic without code</li> <li>\u2705 Rapid prototyping with prefabs</li> <li>\u2705 Very simple games (mobile casual, hyper-casual)</li> </ul>"},{"location":"architecture/comparisons/#soa-gameeventvariables-wins-when","title":"SOA (GameEvent/Variables) Wins When","text":"<ul> <li>\u2705 Designers must create and wire events without touching code</li> <li>\u2705 Team is already heavily invested in SOA with many existing assets</li> <li>\u2705 Designer empowerment is the absolute top priority</li> <li>\u26a0\ufe0f BUT: Consider migration costs and maintainability issues (see Anti-SOA critique)</li> <li>\u26a0\ufe0f Alternative: Use ScriptableObjects for configs only + DxMessaging for events (Pattern B in SOA Guide)</li> </ul>"},{"location":"architecture/comparisons/#static-event-bus-wins-when","title":"Static Event Bus Wins When","text":"<ul> <li>\u2705 You've already built one and it works</li> <li>\u2705 Very simple use cases (just need globals)</li> </ul>"},{"location":"architecture/comparisons/#cost-benefit-summary","title":"Cost-Benefit Summary","text":""},{"location":"architecture/comparisons/#costs","title":"Costs","text":"<ol> <li>Learning curve (~1-2 days to feel comfortable)</li> <li>More upfront code (message definitions)</li> <li>Slightly slower than raw C# events (~10ns/call)</li> <li>Can't return values (fire-and-forget only)</li> </ol>"},{"location":"architecture/comparisons/#benefits","title":"Benefits","text":"<ol> <li>Automatic lifecycle prevents common memory leaks</li> <li>Full decoupling (systems don't reference each other)</li> <li>Observability (Inspector diagnostics, message history)</li> <li>Predictable ordering (priority-based execution)</li> <li>Interception/validation (before handlers run)</li> <li>Testability (isolated buses)</li> </ol> <p>Break-even point: Usually around 10-20 hours into a project, when event management becomes painful.</p>"},{"location":"architecture/comparisons/#making-the-decision-be-honest-with-yourself","title":"Making the Decision (Be Honest With Yourself)","text":""},{"location":"architecture/comparisons/#answer-these-questions-honestly","title":"Answer these questions honestly","text":""},{"location":"architecture/comparisons/#1-project-lifespan","title":"1. Project Lifespan?","text":"<ul> <li><1 week (game jam): Skip DxMessaging \u2192 Use C# events or direct calls</li> <li>1-4 weeks (prototype): Maybe \u2192 If you plan to continue, use DxMessaging</li> <li>1+ months (real project): Yes \u2192 DxMessaging will save you time</li> <li>6+ months (production): Absolutely \u2192 You'll thank yourself later</li> </ul>"},{"location":"architecture/comparisons/#2-team-size","title":"2. Team Size?","text":"<ul> <li>Solo dev: Optional \u2192 Depends on project complexity</li> <li>2-3 devs: Valuable \u2192 Reduces communication overhead</li> <li>4+ devs: Highly recommended \u2192 Clear contracts between systems</li> <li>Remote/distributed team: Essential \u2192 Explicit message contracts prevent miscommunication</li> </ul>"},{"location":"architecture/comparisons/#3-codebase-size","title":"3. Codebase Size?","text":"<ul> <li><1k lines: Skip it \u2192 Direct method calls are fine</li> <li>1k-5k lines: Consider it \u2192 If you're growing fast</li> <li>5k-20k lines: Recommended \u2192 Coupling becomes painful</li> <li>20k+ lines: Absolutely \u2192 Refactoring becomes significantly more challenging without it</li> </ul>"},{"location":"architecture/comparisons/#4-how-many-systems-need-to-communicate","title":"4. How Many Systems Need to Communicate?","text":"<ul> <li>1-2 systems: Skip \u2192 Just call methods directly</li> <li>3-5 systems: Consider \u2192 If they don't share references</li> <li>6-10 systems: Recommended \u2192 Coupling becomes unmanageable</li> <li>10+ systems: Essential \u2192 Managing many SerializeField references becomes difficult</li> </ul>"},{"location":"architecture/comparisons/#5-have-you-had-memory-leaks-from-forgotten-unsubscribes","title":"5. Have You Had Memory Leaks From Forgotten Unsubscribes?","text":"<ul> <li>Never: Optional \u2192 You may not need automatic lifecycle management</li> <li>Once or twice: Consider it \u2192 Prevention is cheaper than debugging</li> <li>Multiple times: Recommended \u2192 Automatic cleanup would help</li> <li>Currently debugging one: Strongly recommended \u2192 Consider adopting DxMessaging</li> </ul>"},{"location":"architecture/comparisons/#6-how-often-do-you-debug-what-fired-when","title":"6. How Often Do You Debug \"What Fired When?\"","text":"<ul> <li>Never: Likely not needed \u2192 Small projects may not require message debugging tools</li> <li>Rarely: Optional, but would help</li> <li>Monthly: Recommended \u2192 Inspector diagnostics will save hours</li> <li>Weekly: Strongly recommended \u2192 Debugging tools would provide significant time savings</li> </ul>"},{"location":"architecture/comparisons/#quick-decision-matrix","title":"Quick Decision Matrix","text":"Text Only<pre><code>Game Jam \u2192 C# Events (speed over safety)\nPrototype \u2192 DxMessaging IF continuing, else C# Events\nProduction \u2192 DxMessaging (unless <1k lines)\nLegacy codebase \u2192 Migrate gradually (see Migration Guide)\n</code></pre>"},{"location":"architecture/comparisons/#the-real-question","title":"The Real Question","text":""},{"location":"architecture/comparisons/#will-this-project-still-exist-in-3-months","title":"\"Will this project still exist in 3 months?\"","text":"<ul> <li>No: C# events are fine</li> <li>Yes: Use DxMessaging</li> </ul>"},{"location":"architecture/comparisons/#will-anyone-else-work-on-this-code","title":"\"Will anyone else work on this code?\"","text":"<ul> <li>No: C# events might be okay</li> <li>Yes: Use DxMessaging (future you counts as \"someone else\")</li> </ul>"},{"location":"architecture/comparisons/#rule-of-thumb","title":"Rule of Thumb","text":"<p>If you're reading this and thinking:</p> <ul> <li>\"I've experienced these pain points\" \u2192 DxMessaging will help</li> <li>\"This seems like overkill\" \u2192 You probably don't need it yet</li> <li>\"I need this yesterday\" \u2192 DxMessaging may be a good fit</li> </ul> <p>See also</p> <ul> <li>Message Types</li> <li>Diagnostics (Editor inspector)</li> <li>Migration Guide - How to adopt gradually</li> </ul>"},{"location":"architecture/design-and-architecture/","title":"Design & Architecture: Under the Hood","text":"<p>This document explains DxMessaging\u2019s internal design, performance optimizations, and architectural decisions. Read this to understand how and why DxMessaging works the way it does.</p>"},{"location":"architecture/design-and-architecture/#table-of-contents","title":"Table of Contents","text":"<ul> <li>Core Design Principles</li> <li>Architecture Overview</li> <li>Performance Optimizations</li> <li>Message Type System</li> <li>Registration and Lifecycle</li> <li>The Message Bus</li> <li>Why DxMessaging is Fast</li> <li>Design Decisions and Tradeoffs</li> </ul>"},{"location":"architecture/design-and-architecture/#core-design-principles","title":"Core Design Principles","text":"<p>DxMessaging was built with these principles:</p> <ol> <li>Zero\u2011Allocation Communication</li> <li>Messages are <code>readonly struct</code> types passed by <code>ref</code>.</li> <li>No boxing, no temporary objects, minimal GC pressure.</li> <li> <p>Handlers receive <code>ref</code> parameters for struct messages.</p> </li> <li> <p>Type\u2011Safe by Default</p> </li> <li>Compile\u2011time guarantees via generic constraints.</li> <li>No string\u2011based dispatch (unlike Unity\u2019s <code>SendMessage</code>).</li> <li> <p>Source generators provide boilerplate\u2011free message definitions.</p> </li> <li> <p>Predictable Execution</p> </li> <li>Priority\u2011based handler ordering (lower priority runs first).</li> <li>Three\u2011stage pipeline: Interceptors \u2192 Handlers \u2192 Post\u2011Processors.</li> <li> <p>Deterministic behavior within each priority level.</p> </li> <li> <p>Observable & Debuggable</p> </li> <li>Built\u2011in diagnostics via <code>CyclicBuffer</code>.</li> <li>Registration logging with <code>RegistrationLog</code>.</li> <li> <p>Inspector integration for runtime visibility.</p> </li> <li> <p>Lifecycle Safety</p> </li> <li><code>MessageRegistrationToken</code> manages enable/disable.</li> <li>Automatic cleanup prevents memory leaks.</li> <li> <p>Unity lifecycle integration via <code>MessageAwareComponent</code>.</p> </li> <li> <p>Decoupled by Nature</p> </li> <li>Three semantic categories: Untargeted, Targeted, Broadcast.</li> <li>No direct references between producers and consumers.</li> <li>Context\u2011aware (who sent, who received) without tight coupling.</li> </ol>"},{"location":"architecture/design-and-architecture/#architecture-overview","title":"Architecture Overview","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TB\n subgraph App[\"Application Layer\"]\n CompA[Component A<br/>Token.Reg]\n CompB[Component B<br/>Token.Reg]\n CompC[Component C<br/>Token.Reg]\n end\n\n subgraph Token[\"Registration Layer\"]\n MRT[MessageRegistrationToken<br/>\u2022 Stages registrations<br/>\u2022 Enable/Disable<br/>\u2022 Lifecycle management]\n end\n\n subgraph Handler[\"Handler Layer\"]\n MH[MessageHandler<br/>\u2022 Per-component handler<br/>\u2022 Active/Inactive state]\n end\n\n subgraph Bus[\"Message Bus Layer\"]\n MB[MessageBus<br/>\u2022 Interceptors<br/>\u2022 Handlers<br/>\u2022 Post-Processors]\n end\n\n CompA ==> MRT\n CompB ==> MRT\n CompC ==> MRT\n MRT ==> MH\n MH ==> MB\n\n classDef primary stroke-width:3px\n class App,CompA,CompB,CompC primary\n classDef warning stroke-width:3px\n class Token,MRT warning\n classDef accent stroke-width:3px\n class Handler,MH accent\n classDef success stroke-width:3px\n class Bus,MB success</code></pre>"},{"location":"architecture/design-and-architecture/#layer-responsibilities","title":"Layer Responsibilities","text":"<ol> <li>Application Layer - Your Unity components register message handlers</li> <li>Registration Layer - Token manages handler lifecycle (enable/disable/cleanup)</li> <li>Handler Layer - Per-component state management (active/inactive)</li> <li>Message Bus Layer - Routes messages through interceptors \u2192 handlers \u2192 post-processors</li> </ol>"},{"location":"architecture/design-and-architecture/#performance-optimizations","title":"Performance Optimizations","text":"<ul> <li>Struct messages passed by <code>ref</code> to avoid copying and GC.</li> <li>Minimal allocations in hot paths; logging and diagnostics use ring buffers.</li> <li>Pre\u2011allocated internal collections for common operations.</li> <li>Handlers are sorted by priority once during registration. Emitting a message iterates through all active handlers in that order.</li> </ul>"},{"location":"architecture/design-and-architecture/#message-type-system","title":"Message Type System","text":"<ul> <li>Untargeted: broadcast\u2011like notifications without an explicit receiver.</li> <li>Targeted: deliver to a specific target (e.g., GameObject, <code>InstanceId</code>).</li> <li>Broadcast: deliver to all listeners (optionally capturing the source).</li> </ul> <p>Attributes like <code>[DxTargetedMessage]</code> and <code>[DxBroadcastMessage]</code> (with source generators) provide strong typing with minimal boilerplate.</p>"},{"location":"architecture/design-and-architecture/#registration-and-lifecycle","title":"Registration and Lifecycle","text":"<ul> <li><code>MessageRegistrationToken</code> groups per\u2011component registrations.</li> <li>Enable/disable toggles all component handlers together.</li> <li>Disposal cleans up handlers automatically, preventing leaks.</li> <li><code>MessageAwareComponent</code> wires Unity lifecycles to tokens for safety.</li> </ul>"},{"location":"architecture/design-and-architecture/#the-message-bus","title":"The Message Bus","text":"<p>Message flow: Interceptors \u2192 Handlers \u2192 Post\u2011Processors.</p> <ul> <li>Interceptors may transform or cancel messages before delivery.</li> <li>Handlers execute in priority order; lower number executes first.</li> <li>Post\u2011processors observe outcomes and can emit follow\u2011up messages.</li> </ul>"},{"location":"architecture/design-and-architecture/#why-dxmessaging-is-fast","title":"Why DxMessaging is Fast","text":"<ul> <li>No reflection for dispatch; compile\u2011time generics and static typing.</li> <li>No string dispatch or dynamic lookup.</li> <li>Ref\u2011based delivery avoids copies and allocations.</li> <li>Tight internal data structures tuned for Unity hot loops.</li> </ul>"},{"location":"architecture/design-and-architecture/#design-decisions-and-tradeoffs","title":"Design Decisions and Tradeoffs","text":"<ul> <li>Priorities are numeric for clarity and control; predictable ordering beats implicit timing.</li> <li>Strong typing over dynamic flexibility; safer refactoring and IDE support.</li> <li>Diagnostics are opt\u2011in and lightweight to keep runtime overhead minimal.</li> </ul> <p>See also:</p> <ul> <li>Interceptors and Ordering</li> <li>Diagnostics</li> <li>Message Types</li> </ul>"},{"location":"architecture/performance/","title":"Performance Benchmarks","text":"<p>This page is auto-updated by the Unity PlayMode benchmark tests in the Performance PlayMode benchmark suite.</p> <p>How it works:</p> <ul> <li>Run PlayMode tests locally in your Unity project that references this package.</li> <li>The benchmark test writes an OS-specific section below with a markdown table.</li> <li>CI runs skip writing to avoid noisy diffs.</li> </ul> <p>See also: Performance optimizations for design details.</p>"},{"location":"architecture/performance/#benchmark-methodology-and-caveats","title":"Benchmark Methodology and Caveats","text":"<p>These benchmarks measure raw message dispatch throughput using a simple counter-increment handler. Each test runs for 5 seconds, dispatching messages in batches of 10,000 operations per iteration with a pre-warm phase to avoid cold-start effects.</p>"},{"location":"architecture/performance/#important-considerations","title":"Important considerations","text":"<ul> <li>Results will vary based on your hardware, Unity version, and runtime environment.</li> <li>The benchmarks test isolated message dispatch with minimal handler logic. Real-world performance depends heavily on what your handlers actually do.</li> <li>The \"Unity\" baseline uses <code>GameObject.SendMessage()</code>, which performs string-based method lookup and allocates memory. Direct method calls would be faster than any messaging system.</li> <li>\"Allocations?\" indicates whether the test detected GC allocations during message dispatch under test conditions.</li> </ul>"},{"location":"architecture/performance/#performance-tradeoffs-to-be-aware-of","title":"Performance tradeoffs to be aware of","text":"<ul> <li>Interceptors and post-processors add overhead. With 8 interceptors registered, throughput drops to roughly 45% of the no-interceptor baseline. With 8 post-processors, throughput drops to roughly 38%. This is an expected tradeoff for the additional flexibility these features provide.</li> <li>Reflexive messaging (dynamic method invocation) is slower than direct handler registration due to the reflection overhead.</li> </ul> <p>You can run these benchmarks yourself to get results specific to your environment. The source code is available in the test suite linked above.</p>"},{"location":"architecture/performance/#windows","title":"Windows","text":"Message Tech Operations / Second Allocations? Unity 2,576,000 Yes DxMessaging (GameObject) - Normal 10,264,000 No DxMessaging (Component) - Normal 10,086,000 No DxMessaging (GameObject) - No-Copy 11,552,000 No DxMessaging (Component) - No-Copy 11,266,000 No DxMessaging (Untargeted) - No-Copy 16,892,000 No DxMessaging (Untargeted) - Interceptors 7,628,000 No DxMessaging (Untargeted) - Post-Processors 6,562,000 No Reflexive (One Argument) 2,868,000 No Reflexive (Two Arguments) 2,386,000 No Reflexive (Three Arguments) 2,372,000 No"},{"location":"architecture/performance/#macos","title":"macOS","text":"<p>Run the PlayMode benchmarks on macOS to populate this section.</p>"},{"location":"architecture/performance/#linux","title":"Linux","text":"<p>Run the PlayMode benchmarks on Linux to populate this section.</p>"},{"location":"concepts/","title":"Concepts","text":"<p>\u2190 Documentation Home | Getting Started</p> <p>This section explains the core concepts behind DxMessaging. Concepts are the foundational ideas and mental models that inform how you design and structure your messaging\u2014understanding these will help you make better architectural decisions and avoid common pitfalls.</p>"},{"location":"concepts/#start-here","title":"Start Here","text":"<ul> <li>Mental Model \u2014 How to think about DxMessaging. Covers the philosophy, the three message types with analogies, tokens and lifecycle, and when to use what. Read this first.</li> </ul>"},{"location":"concepts/#core-concepts","title":"Core Concepts","text":"<ul> <li> <p>Message Types \u2014 Deep dive into Untargeted, Targeted, and Broadcast messages with code examples and decision guides.</p> </li> <li> <p>Targeting and Context \u2014 How DxMessaging uses GameObjects and Components as message context, and the role of <code>InstanceId</code>.</p> </li> <li> <p>Listening Patterns \u2014 All the ways to receive messages: targeted, untargeted, broadcast, and \"without targeting/source\" patterns.</p> </li> <li> <p>Interceptors and Ordering \u2014 Control message flow with priorities, post-processors, and interceptors.</p> </li> </ul>"},{"location":"concepts/#quick-reference","title":"Quick Reference","text":"Concept One-Line Summary Mental Model Philosophy and first principles Message Types The three message categories Targeting GameObjects and Components as context Listening Ways to receive messages Ordering Priority and interception"},{"location":"concepts/#related-sections","title":"Related Sections","text":"<ul> <li>Getting Started \u2014 Hands-on walkthrough</li> <li>Guides \u2014 Practical patterns and recipes</li> <li>Reference \u2014 API documentation</li> </ul>"},{"location":"concepts/interceptors-and-ordering/","title":"Interceptors, Ordering, and Post\u2011Processing","text":""},{"location":"concepts/interceptors-and-ordering/#snapshot-semantics-frozen-listener-lists","title":"Snapshot Semantics: Frozen Listener Lists","text":"<p>IMPORTANT: DxMessaging uses snapshot semantics for message emissions. When a message is emitted, the system creates a snapshot of all current listeners (interceptors, handlers, and post-processors). This snapshot is \"frozen\" for the duration of that emission.</p>"},{"location":"concepts/interceptors-and-ordering/#what-this-means","title":"What this means","text":"<ul> <li>Listeners added during emission will not be invoked for the current message</li> <li>Newly registered listeners will only become active starting with the next emission</li> <li>Listeners removed during emission will still complete their execution for the current message</li> <li>This behavior applies to all registration types: handlers, interceptors, and post-processors</li> <li>This behavior applies to all message categories: Untargeted, Targeted, and Broadcast</li> </ul>"},{"location":"concepts/interceptors-and-ordering/#example","title":"Example","text":"C#<pre><code>// Handler adds a new listener during emission\n_ = token.RegisterUntargeted<GameEvent>(msg => {\n DoWork();\n // This new listener will NOT run for this emission\n _ = token.RegisterUntargeted<GameEvent>(newMsg => ProcessLater());\n});\n\n// First emission: only the original handler runs\nvar firstEvent = new GameEvent();\nfirstEvent.Emit(); // DoWork() executes, ProcessLater() does NOT\n\n// Second emission: both handlers run\nvar secondEvent = new GameEvent();\nsecondEvent.Emit(); // Both DoWork() and ProcessLater() execute\n</code></pre>"},{"location":"concepts/interceptors-and-ordering/#why-this-matters","title":"Why this matters","text":"<ul> <li>Prevents infinite loops (a handler that registers itself won't recurse)</li> <li>Guarantees predictable execution order</li> <li>Ensures all listeners see a consistent view of the registration state</li> <li>Makes debugging and reasoning about message flow easier</li> </ul> <p>This snapshot behavior is extensively tested in the Mutation During Emission tests.</p> <p>Execution order (precise)</p> <p>DxMessaging runs emissions through a fixed pipeline. This section documents the exact order used at runtime for every category. Unless otherwise noted:</p> <ul> <li>Priority: lower numbers run earlier.</li> <li>Same priority: registration order is preserved.</li> <li>Within a priority group, fast handlers (by\u2011ref) run before action handlers.</li> <li>Each category (Untargeted, Targeted, Broadcast) has its own pipeline.</li> </ul> <p>Untargeted pipeline</p> <ol> <li>Interceptors for <code>T</code> (ascending priority; within priority by registration order)</li> <li>Global Accept\u2011All Untargeted handlers (in the MessageHandler that registered them)</li> <li>Untargeted handlers for <code>T</code> (ascending priority; within priority by registration order)</li> <li>Untargeted Post\u2011Processors for <code>T</code> (ascending priority; within priority by registration order)</li> </ol> <p>Targeted pipeline</p> <ol> <li>Interceptors for <code>T</code> (ascending priority)</li> <li>Global Accept\u2011All Targeted handlers (receive <code>(target, ITargetedMessage)</code>)</li> <li>Targeted handlers for <code>T</code> registered for the specific <code>target</code></li> <li>Targeted\u2011Without\u2011Targeting handlers for <code>T</code> (listen for all targets)</li> <li>Targeted Post\u2011Processors for <code>T</code> registered for the specific <code>target</code></li> <li>Targeted\u2011Without\u2011Targeting Post\u2011Processors for <code>T</code> (listen for all targets)</li> </ol> <p>Broadcast pipeline</p> <ol> <li>Interceptors for <code>T</code> (ascending priority)</li> <li>Global Accept\u2011All Broadcast handlers (receive <code>(source, IBroadcastMessage)</code>)</li> <li>Broadcast handlers for <code>T</code> registered for the specific <code>source</code></li> <li>Broadcast\u2011Without\u2011Source handlers for <code>T</code> (listen for all sources)</li> <li>Broadcast Post\u2011Processors for <code>T</code> registered for the specific <code>source</code></li> <li>Broadcast\u2011Without\u2011Source Post\u2011Processors for <code>T</code> (listen for all sources)</li> </ol> <p>Notes on handler groups</p> <ul> <li>Fast vs Action: At a given priority, fast handlers (by\u2011ref delegates) are invoked before action handlers, and within each group the registration order is preserved.</li> <li>\u201cWithout Targeting/Source\u201d registrations run in their own groups and do not replace the specific target/source groups.</li> </ul> <p>Visual overview</p> <pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n subgraph Untargeted[\"Untargeted Messages\"]\n direction TB\n U1[\"Interceptors(T)\"] --> U2[Global Accept\u2011All Untargeted]\n U2 --> U3[\"Handlers(T)\"]\n U3 --> U4[\"Post\u2011Processors(T)\"]\n end\n\n subgraph Targeted[\"Targeted Messages\"]\n direction TB\n T1[\"Interceptors(T)\"] --> T2[Global Accept\u2011All Targeted]\n T2 --> T3[\"Handlers(T) @ target\"]\n T3 --> T4[\"Handlers(T) (All Targets)\"]\n T4 --> T5[\"Post\u2011Processors(T) @ target\"]\n T5 --> T6[\"Post\u2011Processors(T) (All Targets)\"]\n end\n\n subgraph Broadcast[\"Broadcast Messages\"]\n direction TB\n B1[\"Interceptors(T)\"] --> B2[Global Accept\u2011All Broadcast]\n B2 --> B3[\"Handlers(T) @ source\"]\n B3 --> B4[\"Handlers(T) (All Sources)\"]\n B4 --> B5[\"Post\u2011Processors(T) @ source\"]\n B5 --> B6[\"Post\u2011Processors(T) (All Sources)\"]\n end\n\n classDef neutral stroke-width:2px\n class Untargeted,Targeted,Broadcast neutral\n classDef warning stroke-width:2px\n class U1,T1,B1 warning\n classDef accent stroke-width:2px\n class U2,T2,B2 accent\n classDef primary stroke-width:2px\n class U3,T3,T4,B3,B4 primary\n classDef success stroke-width:2px\n class U4,T5,T6,B5,B6 success</code></pre> <p>Example sequence</p> <pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant P as Producer\n participant I as Interceptor(s)\n participant G as Global Accept\u2011All\n participant H as Handler(s)\n participant PP as Post\u2011Processor(s)\n P->>I: emit(ref message)\n I-->>P: false? cancel : continue\n I->>G: message (category\u2011specific)\n G->>H: message\n H->>PP: after all handlers complete</code></pre> <p>Interceptors</p> <ul> <li>Mutate or cancel messages before any handler runs. Return <code>false</code> to cancel.</li> <li>Define per category: <code>RegisterUntargetedInterceptor<T></code>, <code>RegisterTargetedInterceptor<T></code>, <code>RegisterBroadcastInterceptor<T></code>.</li> <li>Useful for validation, normalization, enrichment, and short\u2011circuiting.</li> </ul> C#<pre><code>using DxMessaging.Core; // MessageHandler, InstanceId\nusing DxMessaging.Core.MessageBus; // IMessageBus\n\n// Cancel <=0 damage and clamp high values\nvar bus = MessageHandler.MessageBus;\n_ = bus.RegisterTargetedInterceptor<TookDamage>(\n (ref InstanceId target, ref TookDamage m) =>\n {\n if (m.amount <= 0) return false;\n m = new TookDamage(Math.Min(m.amount, 999));\n return true;\n },\n priority: 0\n);\n</code></pre> <p>Real\u2011World Use Cases</p>"},{"location":"concepts/interceptors-and-ordering/#statebased-message-cancellation","title":"State\u2011Based Message Cancellation","text":"<p>Prevent UI messages from being processed based on current UI state:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct OpenMenu\n{\n public readonly string menuName;\n}\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct ShowDialog\n{\n public readonly string dialogText;\n}\n\npublic class UIStateManager\n{\n private bool _isInCutscene;\n private bool _isPaused;\n private bool _isLoading;\n\n public void RegisterInterceptors()\n {\n var bus = MessageHandler.MessageBus;\n\n // Block all UI interactions during cutscenes, loading, or when paused\n _ = bus.RegisterUntargetedInterceptor<OpenMenu>(\n (ref OpenMenu m) => !_isInCutscene && !_isLoading,\n priority: -100 // Run early\n );\n\n _ = bus.RegisterUntargetedInterceptor<ShowDialog>(\n (ref ShowDialog m) => !_isInCutscene && !_isPaused,\n priority: -100\n );\n }\n\n public void EnterCutscene() => _isInCutscene = true;\n public void ExitCutscene() => _isInCutscene = false;\n}\n\n// Usage: UI messages automatically blocked during cutscenes\nvar uiManager = new UIStateManager();\nuiManager.RegisterInterceptors();\nuiManager.EnterCutscene();\n\nvar openMenu = new OpenMenu(\"inventory\");\nopenMenu.Emit(); // Cancelled by interceptor\nvar showDialog = new ShowDialog(\"Hello!\");\nshowDialog.Emit(); // Cancelled by interceptor\n</code></pre>"},{"location":"concepts/interceptors-and-ordering/#value-clamping-and-normalization","title":"Value Clamping and Normalization","text":"<p>Ensure message data stays within valid ranges:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct MovementInput\n{\n public readonly Vector2 direction;\n public readonly float speed;\n}\n\npublic class InputNormalizer\n{\n public void RegisterInterceptors()\n {\n var bus = MessageHandler.MessageBus;\n\n // Normalize and clamp movement input\n _ = bus.RegisterUntargetedInterceptor<MovementInput>(\n (ref MovementInput m) =>\n {\n // Normalize direction vector\n var normalized = m.direction.normalized;\n\n // Clamp speed to valid range\n var clampedSpeed = Mathf.Clamp(m.speed, 0f, 10f);\n\n // Mutate the message with cleaned values\n m = new MovementInput(normalized, clampedSpeed);\n return true;\n },\n priority: 0\n );\n }\n}\n\n// Usage: all movement input is automatically normalized\nvar normalizer = new InputNormalizer();\nnormalizer.RegisterInterceptors();\n\n// Even invalid input gets cleaned\nvar input = new MovementInput(new Vector2(100, 200), 9999f);\ninput.Emit();\n// Handlers receive: direction=(0.45, 0.89), speed=10.0\n</code></pre>"},{"location":"concepts/interceptors-and-ordering/#permission-and-authorization-checks","title":"Permission and Authorization Checks","text":"<p>Block messages that violate game rules or permissions:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SpendCurrency\n{\n public readonly int amount;\n}\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct UnlockAchievement\n{\n public readonly string achievementId;\n}\n\npublic class PermissionSystem\n{\n private readonly IPlayerDataService _playerData;\n\n public PermissionSystem(IPlayerDataService playerData)\n {\n _playerData = playerData;\n }\n\n public void RegisterInterceptors()\n {\n var bus = MessageHandler.MessageBus;\n\n // Block currency spending if player doesn't have enough\n _ = bus.RegisterTargetedInterceptor<SpendCurrency>(\n (ref InstanceId playerId, ref SpendCurrency m) =>\n {\n var balance = _playerData.GetCurrencyBalance(playerId);\n if (balance < m.amount)\n {\n Debug.LogWarning($\"Player {playerId} attempted to spend {m.amount} but only has {balance}\");\n return false; // Cancel the message\n }\n return true;\n },\n priority: -50\n );\n\n // Block achievement unlocks if already unlocked (idempotency)\n _ = bus.RegisterTargetedInterceptor<UnlockAchievement>(\n (ref InstanceId playerId, ref UnlockAchievement m) =>\n {\n if (_playerData.HasAchievement(playerId, m.achievementId))\n {\n return false; // Already unlocked, skip\n }\n return true;\n },\n priority: -50\n );\n }\n}\n\n// Usage: invalid operations automatically blocked\nvar permissions = new PermissionSystem(playerDataService);\npermissions.RegisterInterceptors();\n\n// This will be blocked if player doesn't have 100 currency\nvar spendCurrency = new SpendCurrency(100);\nspendCurrency.EmitTargeted(playerId);\n\n// This will be blocked if achievement is already unlocked\nvar unlockAchievement = new UnlockAchievement(\"first_boss\");\nunlockAchievement.EmitTargeted(playerId);\n</code></pre>"},{"location":"concepts/interceptors-and-ordering/#message-enrichment-and-context-addition","title":"Message Enrichment and Context Addition","text":"<p>Add contextual data to messages as they flow through the system:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing System;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerAction\n{\n public readonly string actionType;\n [DxOptionalParameter]\n public readonly long timestamp; // Added by interceptor\n [DxOptionalParameter]\n public readonly string sessionId; // Added by interceptor\n}\n\npublic class TelemetryEnricher\n{\n private readonly string _currentSessionId;\n\n public TelemetryEnricher(string sessionId)\n {\n _currentSessionId = sessionId;\n }\n\n public void RegisterInterceptors()\n {\n var bus = MessageHandler.MessageBus;\n\n // Enrich player actions with timestamp and session context\n _ = bus.RegisterTargetedInterceptor<PlayerAction>(\n (ref InstanceId playerId, ref PlayerAction m) =>\n {\n // Add timestamp and session ID to every action\n m = new PlayerAction(\n m.actionType,\n DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),\n _currentSessionId\n );\n return true;\n },\n priority: -100 // Run very early to enrich before other interceptors\n );\n }\n}\n\n// Usage: messages automatically enriched with context\nvar enricher = new TelemetryEnricher(Guid.NewGuid().ToString());\nenricher.RegisterInterceptors();\n\n// Emit without timestamp/session - interceptor adds them\nvar action = new PlayerAction(\"jump\");\naction.EmitTargeted(playerId);\n// Handlers receive fully enriched message with timestamp and sessionId\n</code></pre>"},{"location":"concepts/interceptors-and-ordering/#cooldown-and-rate-limiting","title":"Cooldown and Rate Limiting","text":"<p>Prevent message spam by enforcing cooldowns:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing System;\nusing System.Collections.Generic;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct CastSpell\n{\n public readonly string spellName;\n}\n\npublic class CooldownManager\n{\n private readonly Dictionary<(InstanceId, string), DateTime> _lastCastTimes = new();\n private readonly TimeSpan _globalCooldown = TimeSpan.FromSeconds(1.5);\n\n public void RegisterInterceptors()\n {\n var bus = MessageHandler.MessageBus;\n\n _ = bus.RegisterTargetedInterceptor<CastSpell>(\n (ref InstanceId casterId, ref CastSpell m) =>\n {\n var key = (casterId, m.spellName);\n var now = DateTime.UtcNow;\n\n if (_lastCastTimes.TryGetValue(key, out var lastCast))\n {\n if (now - lastCast < _globalCooldown)\n {\n // Still on cooldown\n return false;\n }\n }\n\n _lastCastTimes[key] = now;\n return true;\n },\n priority: -10\n );\n }\n}\n\n// Usage: rapid spell casts automatically throttled\nvar cooldowns = new CooldownManager();\ncooldowns.RegisterInterceptors();\n\nvar spell1 = new CastSpell(\"fireball\");\nspell1.EmitTargeted(playerId); // \u2713 Allowed\nvar spell2 = new CastSpell(\"fireball\");\nspell2.EmitTargeted(playerId); // \u2717 Blocked (too soon)\n// ... wait 1.5s ...\nvar spell3 = new CastSpell(\"fireball\");\nspell3.EmitTargeted(playerId); // \u2713 Allowed\n</code></pre> <p>When to Use Interceptors</p> <p>\u2705 Good use cases:</p> <ul> <li>Input validation and sanitization</li> <li>Value clamping and normalization</li> <li>Permission and authorization checks</li> <li>State\u2011based message filtering</li> <li>Rate limiting and cooldown enforcement</li> <li>Message enrichment (adding timestamps, session IDs, etc.)</li> <li>Early exit for duplicate or redundant messages</li> <li>Logging suspicious or invalid message attempts</li> </ul> <p>\u26a0\ufe0f Key principles:</p> <ul> <li>Run before handlers: Interceptors execute before any type\u2011specific handlers, making them perfect for preprocessing</li> <li>Can mutate: Unlike post\u2011processors, interceptors can modify message data</li> <li>Can cancel: Return <code>false</code> to prevent the message from reaching handlers</li> <li>Priority matters: Lower priority values run first (use negative priorities for early interceptors)</li> </ul> <p>\u274c Avoid for:</p> <ul> <li>Read\u2011only observation (use handlers or post\u2011processors instead)</li> <li>Actions that should run after message processing (use post\u2011processors)</li> <li>Heavy computation that doesn't need to block the message</li> </ul> <p>Post\u2011processors</p> <ul> <li>Observe after handlers. Great for logging, analytics, or follow\u2011up emission.</li> <li>Per category and scope (per target/source or all):</li> <li>Untargeted: <code>RegisterUntargetedPostProcessor<T></code></li> <li>Targeted (specific): <code>RegisterTargetedPostProcessor<T>(target, ...)</code></li> <li>Targeted (all): <code>RegisterTargetedWithoutTargetingPostProcessor<T>(...)</code></li> <li>Broadcast (specific): <code>RegisterBroadcastPostProcessor<T>(source, ...)</code></li> <li>Broadcast (all): <code>RegisterBroadcastWithoutSourcePostProcessor<T>(...)</code></li> </ul> <p>Global Accept\u2011All</p> <ul> <li>Register once and observe all messages on a handler.</li> <li>Overloads exist for action and fast handlers.</li> <li>Runs between interceptors and type\u2011specific handlers.</li> </ul> <p>Related</p> <ul> <li>Message Types</li> <li>Listening Patterns</li> </ul>"},{"location":"concepts/listening-patterns/","title":"Listening Patterns","text":""},{"location":"concepts/listening-patterns/#targeted-across-all-targets","title":"Targeted across all targets","text":"<ul> <li>Accept every targeted message of a given type regardless of who it\u2019s for.</li> </ul> C#<pre><code>using DxMessaging.Core; // InstanceId\nusing DxMessaging.Core.Messages;\n\n// Observe all Heal messages and their intended targets\n_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\nvoid OnAnyHeal(InstanceId target, Heal m) => Audit(target, m);\n\n// Post\u2011process all targeted of type\n_ = token.RegisterTargetedWithoutTargetingPostProcessor<Heal>(OnAnyHealPost);\nvoid OnAnyHealPost(InstanceId target, Heal m) => Log(target, m);\n</code></pre>"},{"location":"concepts/listening-patterns/#broadcast-across-all-sources","title":"Broadcast across all sources","text":"<ul> <li>Accept every broadcast message of a given type regardless of who emitted it.</li> </ul> C#<pre><code>using DxMessaging.Core; // InstanceId\nusing DxMessaging.Core.Messages;\n\n// Observe all TookDamage messages and their sources\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyTookDamage);\nvoid OnAnyTookDamage(InstanceId source, TookDamage m) => Track(source, m);\n\n// Post\u2011process all broadcast of type\n_ = token.RegisterBroadcastWithoutSourcePostProcessor<TookDamage>(OnAnyTookDamagePost);\nvoid OnAnyTookDamagePost(InstanceId source, TookDamage m) => Log(source, m);\n</code></pre>"},{"location":"concepts/listening-patterns/#global-acceptall-debuginspection","title":"Global accept\u2011all (debug/inspection)","text":"<ul> <li>Receive every message of every type on a handler; useful for tooling.</li> </ul> C#<pre><code>using DxMessaging.Core;\n\nvar bus = MessageHandler.MessageBus;\nvar handler = new MessageHandler(new InstanceId(1)) { active = true };\nvar dereg = bus.RegisterGlobalAcceptAll(handler);\n// implement handler callbacks for generic categories on your MessageHandler\n</code></pre>"},{"location":"concepts/listening-patterns/#realworld-use-cases","title":"Real\u2011World Use Cases","text":""},{"location":"concepts/listening-patterns/#development-debug-dump","title":"Development Debug Dump","text":"<p>Capture all messages during development for debugging and diagnostics:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Messages;\nusing UnityEngine;\n\npublic class DebugMessageLogger : MessageHandler\n{\n public DebugMessageLogger() : base(new InstanceId(999)) { }\n\n public override void Handle(ref IUntargetedMessage message)\n {\n Debug.Log($\"[Untargeted] {message.GetType().Name}: {message}\");\n }\n\n public override void Handle(ref InstanceId target, ref ITargetedMessage message)\n {\n Debug.Log($\"[Targeted \u2192 {target}] {message.GetType().Name}: {message}\");\n }\n\n public override void Handle(ref InstanceId source, ref IBroadcastMessage message)\n {\n Debug.Log($\"[Broadcast \u2190 {source}] {message.GetType().Name}: {message}\");\n }\n}\n\n// Register in development builds only\n#if DEVELOPMENT_BUILD || UNITY_EDITOR\nvar logger = new DebugMessageLogger { active = true };\n_ = MessageHandler.MessageBus.RegisterGlobalAcceptAll(logger);\n#endif\n</code></pre>"},{"location":"concepts/listening-patterns/#attributebased-network-replication","title":"Attribute\u2011Based Network Replication","text":"<p>Automatically replicate messages marked with custom attributes across the network:</p> C#<pre><code>using System;\nusing System.Reflection;\nusing System.Collections.Generic;\nusing DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\n// Mark messages that should be replicated\n[AttributeUsage(AttributeTargets.Struct)]\npublic class NetworkedAttribute : Attribute { }\n\n[Networked]\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerMoved\n{\n public readonly Vector3 position;\n}\n\n[Networked]\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct DealDamage\n{\n public readonly float amount;\n}\n\n// Network replication handler\npublic class NetworkReplicator : MessageHandler\n{\n private readonly INetworkManager _network;\n private readonly HashSet<Type> _networkedTypes = new();\n\n public NetworkReplicator(INetworkManager network) : base(new InstanceId(1000))\n {\n _network = network;\n CacheNetworkedTypes();\n }\n\n private void CacheNetworkedTypes()\n {\n // Find all message types with [Networked] attribute\n foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())\n {\n foreach (var type in assembly.GetTypes())\n {\n if (type.GetCustomAttribute<NetworkedAttribute>() != null)\n {\n _networkedTypes.Add(type);\n }\n }\n }\n }\n\n public override void Handle(ref IUntargetedMessage message)\n {\n if (_networkedTypes.Contains(message.GetType()))\n {\n _network.Send(message); // Serialize and send\n }\n }\n\n public override void Handle(ref InstanceId target, ref ITargetedMessage message)\n {\n if (_networkedTypes.Contains(message.GetType()))\n {\n _network.Send(target, message);\n }\n }\n\n public override void Handle(ref InstanceId source, ref IBroadcastMessage message)\n {\n if (_networkedTypes.Contains(message.GetType()))\n {\n _network.Send(source, message);\n }\n }\n}\n\n// Usage: any message with [Networked] automatically replicates\nvar replicator = new NetworkReplicator(networkManager) { active = true };\n_ = MessageHandler.MessageBus.RegisterGlobalAcceptAll(replicator);\n\n// These messages are now replicated across the network\nvar playerMoved = new PlayerMoved(playerPos);\nplayerMoved.Emit();\nvar dealDamage = new DealDamage(50f);\ndealDamage.EmitTargeted(enemyId);\n</code></pre>"},{"location":"concepts/listening-patterns/#message-analytics-and-metrics","title":"Message Analytics and Metrics","text":"<p>Track message frequency and performance across your entire game:</p> C#<pre><code>using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\npublic class MessageAnalytics : MessageHandler\n{\n private readonly Dictionary<Type, (int count, long totalMs)> _stats = new();\n private readonly Stopwatch _stopwatch = new();\n\n public MessageAnalytics() : base(new InstanceId(1001)) { }\n\n public override void Handle(ref IUntargetedMessage message)\n {\n TrackMessage(message.GetType());\n }\n\n public override void Handle(ref InstanceId target, ref ITargetedMessage message)\n {\n TrackMessage(message.GetType());\n }\n\n public override void Handle(ref InstanceId source, ref IBroadcastMessage message)\n {\n TrackMessage(message.GetType());\n }\n\n private void TrackMessage(Type messageType)\n {\n _stopwatch.Restart();\n // Message processing happens here\n _stopwatch.Stop();\n\n if (!_stats.TryGetValue(messageType, out var stat))\n {\n stat = (0, 0);\n }\n _stats[messageType] = (stat.count + 1, stat.totalMs + _stopwatch.ElapsedMilliseconds);\n }\n\n public void PrintStats()\n {\n foreach (var kvp in _stats)\n {\n var avg = kvp.Value.totalMs / (double)kvp.Value.count;\n UnityEngine.Debug.Log($\"{kvp.Key.Name}: {kvp.Value.count} messages, avg {avg:F2}ms\");\n }\n }\n}\n</code></pre> <p>When to Use Global Accept\u2011All</p> <p>\u2705 Good use cases:</p> <ul> <li>Development\u2011time debugging and logging</li> <li>Cross\u2011cutting concerns (analytics, telemetry, metrics)</li> <li>Attribute\u2011based systems (networking, serialization, persistence)</li> <li>Testing and diagnostics tools</li> <li>Message replay/recording systems</li> </ul> <p>\u26a0\ufe0f Performance consideration: Global Accept-All handlers are invoked for every message of every type. For performance-sensitive gameplay logic, prefer type-specific registrations which use O(1) lookup instead of O(N) iteration.</p> <p>\u274c Avoid for:</p> <ul> <li>Core gameplay logic that only needs specific message types</li> <li>Hot paths with thousands of messages per frame</li> <li>Production code that can use specific type registrations instead</li> </ul> <p>Tips</p> <ul> <li>Use across\u2011all listeners for diagnostics, analytics, or cross\u2011cutting observers.</li> <li>Prefer specific (target/source) registrations for gameplay logic.</li> </ul> <p>Related</p> <ul> <li>Interceptors & Ordering</li> <li>Diagnostics</li> </ul>"},{"location":"concepts/mental-model/","title":"Mental Model: How to Think About DxMessaging","text":"<p>\u2190 Concepts Index | Message Types | Getting Started</p> <p>This guide explains how to think about DxMessaging from first principles. Understanding the mental model makes everything else easier.</p>"},{"location":"concepts/mental-model/#the-core-idea","title":"The Core Idea","text":"<p>DxMessaging is built around one principle: it gets out of your way.</p> <p>You have data. You need to pass it around. That's the problem.</p> <p>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. The library handles the plumbing.</p> <p>You don't build your game INTO the messaging system. The messaging system is opt-in and optional - a tool you reach for when it helps.</p>"},{"location":"concepts/mental-model/#what-problem-does-this-solve","title":"What Problem Does This Solve?","text":"<p>In a typical Unity project, you face a common challenge:</p> Text Only<pre><code>Player takes damage\n \u251c\u2500\u2500 Health bar needs updating\n \u251c\u2500\u2500 Sound system plays damage audio\n \u251c\u2500\u2500 Camera shakes\n \u251c\u2500\u2500 Achievement system checks milestones\n \u251c\u2500\u2500 Analytics logs the event\n \u2514\u2500\u2500 Tutorial system checks for tips\n</code></pre> <p>Traditional approaches require tight coupling: either the Player knows about all these systems, or they all hold references to the Player. Both paths lead to tangled dependencies.</p> <p>DxMessaging inverts this relationship. The Player broadcasts a fact - \"I took 25 damage\" - and interested systems subscribe to receive it. Systems come and go without the Player knowing or caring.</p>"},{"location":"concepts/mental-model/#the-three-message-categories","title":"The Three Message Categories","text":"<p>DxMessaging models three fundamental patterns of communication. Each maps to a real-world analogy.</p>"},{"location":"concepts/mental-model/#untargeted-the-pa-system","title":"Untargeted: The PA System","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n S[Someone] -->|announces| PA[\ud83d\udce2 PA System]\n PA --> L1[Listener A]\n PA --> L2[Listener B]\n PA --> L3[Listener C]</code></pre> <p>Untargeted messages are announcements. No specific recipient. No specific sender that matters. Everyone who cares can hear it.</p> <p>\ud83d\udcdd Note: No sender identity</p> <p>Unlike a real PA system, untargeted messages carry no concept of \"who announced it.\" There is no sender identity attached - only the message content itself.</p>"},{"location":"concepts/mental-model/#real-examples","title":"Real examples","text":"<ul> <li>\"The game is paused\"</li> <li>\"Settings changed\"</li> <li>\"Scene finished loading\"</li> <li>\"Day/night cycle switched to night\"</li> </ul> C#<pre><code>// Define an untargeted message with attributes\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct GamePaused\n{\n public readonly bool isPaused;\n}\n\n// Multiple fields are supported\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SettingsChanged\n{\n public readonly float volume;\n public readonly int quality;\n}\n</code></pre> <p>Use when: You need to announce something to whoever might care. The sender doesn't know or care who's listening.</p>"},{"location":"concepts/mental-model/#targeted-the-addressed-letter","title":"Targeted: The Addressed Letter","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n S[Sender] -->|\"To: Player\"| Letter[\ud83d\udcec Message Bus]\n Letter --> Player[Player receives]\n Other1[Enemy A] -.->|ignores| Letter\n Other2[Enemy B] -.->|ignores| Letter</code></pre> <p>Targeted messages are commands or directed events. They have a specific recipient. Only that recipient (and any interested observers) receives them.</p>"},{"location":"concepts/mental-model/#real-examples_1","title":"Real examples","text":"<ul> <li>\"Player, heal for 10 HP\"</li> <li>\"Door #7, open\"</li> <li>\"This specific enemy, take 25 damage\"</li> <li>\"Inventory slot 3, equip this weapon\"</li> </ul> C#<pre><code>// Define a targeted message with attributes\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n\n// Emit to a specific target\nHeal heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGameObject);\n</code></pre> <p>Use when: You're commanding a specific entity to do something, or notifying a specific entity about something that happened to it.</p>"},{"location":"concepts/mental-model/#broadcast-the-radio-station","title":"Broadcast: The Radio Station","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n Source[Enemy] -->|\"I took damage!\"| Radio[\ud83d\udcfb Message Bus]\n Radio --> L1[Damage Numbers UI]\n Radio --> L2[Achievement Tracker]\n Radio --> L3[Analytics]\n Radio --> L4[Combat Log]</code></pre> <p>Broadcast messages are facts emitted by a specific source. Unlike targeted messages, there's no intended recipient - just an origin. Anyone who wants to observe can tune in.</p>"},{"location":"concepts/mental-model/#real-examples_2","title":"Real examples","text":"<ul> <li>\"This enemy took 25 damage\" (from the enemy)</li> <li>\"The player picked up item X\" (from the player)</li> <li>\"This chest opened\" (from the chest)</li> <li>\"Projectile hit something\" (from the projectile)</li> </ul> C#<pre><code>// Define a broadcast message with attributes\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage\n{\n public readonly int amount;\n}\n\n// Emit from a specific source\nTookDamage damage = new TookDamage(25);\ndamage.EmitGameObjectBroadcast(thisEnemy);\n</code></pre> <p>Use when: Something happened to an entity, and other systems might want to know about it. The entity doesn't care who's listening.</p>"},{"location":"concepts/mental-model/#deciding-which-type-to-use","title":"Deciding Which Type to Use","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TD\n Start([I need to send a message])\n Start --> Q1{Does it matter<br/>WHO sent it?}\n\n Q1 -->|No| Q2{Does it matter<br/>WHO receives it?}\n Q2 -->|No| Untargeted[Use UNTARGETED<br/>Global announcement]\n Q2 -->|Yes| Targeted[Use TARGETED<br/>Directed command]\n\n Q1 -->|Yes| Q3{Am I commanding<br/>someone to act?}\n Q3 -->|Yes| Targeted\n Q3 -->|No| Broadcast[Use BROADCAST<br/>Observable fact]</code></pre> Question Untargeted Targeted Broadcast Has a specific sender that matters? \u274c \u274c \u2705 Has a specific recipient? \u274c \u2705 \u274c Is it a command? \u274c \u2705 \u274c Is it an observable fact? Maybe \u274c \u2705 Is it a global announcement? \u2705 \u274c \u274c"},{"location":"concepts/mental-model/#tokens-and-lifecycle","title":"Tokens and Lifecycle","text":"<p>Registration in DxMessaging happens through tokens. A <code>MessageRegistrationToken</code> collects your registrations and ties them to a lifecycle.</p>"},{"location":"concepts/mental-model/#the-pattern","title":"The Pattern","text":"C#<pre><code>public class HealthDisplay : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n\n // Register handlers through the Token\n _ = Token.RegisterGameObjectBroadcast<TookDamage>(player, OnPlayerDamaged);\n _ = Token.RegisterUntargeted<GamePaused>(OnGamePaused);\n }\n\n private void OnPlayerDamaged(ref TookDamage msg) { /* update display */ }\n private void OnGamePaused(ref GamePaused msg) { /* pause animations */ }\n}\n</code></pre> <p>\u26a0\ufe0f Warning: Always call <code>base.RegisterMessageHandlers()</code></p> <p>When overriding <code>RegisterMessageHandlers()</code>, always call <code>base.RegisterMessageHandlers()</code> first. This ensures that any registrations from parent classes are preserved. Forgetting this call can silently break inherited behavior.</p>"},{"location":"concepts/mental-model/#what-the-token-does","title":"What the Token Does","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant C as Component\n participant T as Token\n participant B as MessageBus\n\n Note over C,B: Awake\n C->>T: Create Token\n C->>T: Register handlers\n T-->>T: Stage (not active yet)\n\n Note over C,B: OnEnable\n C->>T: Enable()\n T->>B: Activate registrations\n\n Note over C,B: Messages flow\n B->>C: Handler called\n\n Note over C,B: OnDisable\n C->>T: Disable()\n T->>B: Deactivate registrations\n\n Note over C,B: OnDestroy\n C->>T: Dispose/Release\n T->>B: Remove registrations</code></pre> <p>The token:</p> <ol> <li>Stages registrations when you call <code>Register*</code> methods</li> <li>Activates them when you call <code>Enable()</code></li> <li>Deactivates them when you call <code>Disable()</code></li> <li>Cleans up when the token is disposed or the component is destroyed</li> </ol> <p>This maps directly to Unity's lifecycle: registrations activate in <code>OnEnable</code>, deactivate in <code>OnDisable</code>, and clean up in <code>OnDestroy</code>.</p>"},{"location":"concepts/mental-model/#why-tokens-matter","title":"Why Tokens Matter","text":"<p>Tokens prevent common event system bugs:</p> <ul> <li>No forgotten unsubscribes: The token tracks everything</li> <li>No null reference handlers: Disabled tokens don't receive messages</li> <li>No memory leaks: Token cleanup is automatic with <code>MessageAwareComponent</code></li> <li>Predictable timing: Enable/disable follows Unity's enable state</li> </ul>"},{"location":"concepts/mental-model/#context-through-game-primitives","title":"Context Through Game Primitives","text":"<p>DxMessaging uses Unity's primitives (GameObjects, Components) as natural context for messages.</p>"},{"location":"concepts/mental-model/#instanceid","title":"InstanceId","text":"<p>Every registration and emission uses an <code>InstanceId</code> - a lightweight identifier that wraps a Unity Object's instance ID. You rarely need to create these manually because extension methods handle the conversion:</p> C#<pre><code>// These are equivalent:\nheal.EmitGameObjectTargeted(playerGameObject);\nheal.EmitTargeted((InstanceId)playerGameObject);\n\n// Registering for a specific component\nToken.RegisterComponentTargeted<Damage>(this, OnDamage);\n\n// Registering for a whole GameObject\nToken.RegisterGameObjectTargeted<Damage>(gameObject, OnDamage);\n</code></pre>"},{"location":"concepts/mental-model/#gameobject-vs-component-targeting","title":"GameObject vs Component Targeting","text":"<p>You can target at two levels:</p> <p>GameObject level: All components on that GameObject can respond</p> C#<pre><code>// Registration\nToken.RegisterGameObjectTargeted<Command>(gameObject, HandleCommand);\n\n// Emission (any component on playerGO can receive this)\ncommand.EmitGameObjectTargeted(playerGO);\n</code></pre> <p>Component level: Only that specific component responds</p> C#<pre><code>// Registration\nToken.RegisterComponentTargeted<Command>(this, HandleCommand);\n\n// Emission (only this specific component instance receives this)\ncommand.EmitComponentTargeted(specificComponent);\n</code></pre>"},{"location":"concepts/mental-model/#observing-without-targeting","title":"Observing Without Targeting","text":"<p>Sometimes you want to see all messages of a type, regardless of their target or source.</p>"},{"location":"concepts/mental-model/#observing-all-targeted-messages","title":"Observing All Targeted Messages","text":"C#<pre><code>// See every Heal message, no matter who it's for\nToken.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\n\nvoid OnAnyHeal(ref InstanceId target, ref Heal msg)\n{\n // 'target' tells you who was healed\n Debug.Log($\"Someone healed {target} for {msg.amount}\");\n}\n</code></pre>"},{"location":"concepts/mental-model/#observing-all-broadcast-messages","title":"Observing All Broadcast Messages","text":"C#<pre><code>// See every TookDamage message, no matter who broadcast it\nToken.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n\nvoid OnAnyDamage(ref InstanceId source, ref TookDamage msg)\n{\n // 'source' tells you who took damage\n Debug.Log($\"{source} took {msg.amount} damage\");\n}\n</code></pre> <p>These patterns are useful for:</p> <ul> <li>Analytics systems</li> <li>Combat logs</li> <li>Debug overlays</li> <li>Achievement tracking</li> <li>Network replication</li> </ul>"},{"location":"concepts/mental-model/#the-gets-out-of-the-way-philosophy","title":"The \"Gets Out of the Way\" Philosophy","text":"<p>DxMessaging is designed to be invisible when you don't need it:</p> <ol> <li>No global state pollution: Messages are just data. Define them anywhere.</li> <li>No inheritance requirements: Implement an interface or add an attribute. That's it.</li> <li>No framework lock-in: Your message structs are plain C#. They work outside DxMessaging.</li> <li>No ceremony: Define a struct, emit it, handle it. Three steps.</li> <li>Optional everywhere: Don't want to use it for something? Don't. Mix and match.</li> </ol> <p>The library provides primitives. You compose them into patterns that fit your game.</p>"},{"location":"concepts/mental-model/#common-patterns-at-a-glance","title":"Common Patterns at a Glance","text":"Pattern Message Type Registration Emission Global setting change Untargeted <code>RegisterUntargeted<T></code> <code>msg.EmitUntargeted()</code> Command to entity Targeted <code>RegisterGameObjectTargeted<T></code> <code>msg.EmitGameObjectTargeted(go)</code> Something happened Broadcast <code>RegisterGameObjectBroadcast<T></code> <code>msg.EmitGameObjectBroadcast(go)</code> Observe all of type Targeted <code>RegisterTargetedWithoutTargeting<T></code> (normal emission) Observe all sources Broadcast <code>RegisterBroadcastWithoutSource<T></code> (normal emission)"},{"location":"concepts/mental-model/#common-mistakes","title":"Common Mistakes","text":"<p>When getting started with DxMessaging, watch out for these common pitfalls:</p>"},{"location":"concepts/mental-model/#forgetting-to-enable-the-token","title":"Forgetting to Enable the Token","text":"<p>Registrations are staged when you call <code>Register*</code> methods, but they don't become active until the token is enabled. If you're using <code>MessageAwareComponent</code>, this is handled automatically. If managing tokens manually, remember to call <code>Token.Enable()</code> after registering.</p>"},{"location":"concepts/mental-model/#targeting-component-when-you-meant-gameobject-or-vice-versa","title":"Targeting Component When You Meant GameObject (or Vice Versa)","text":"<p>Component-level targeting (<code>EmitComponentTargeted</code>) and GameObject-level targeting (<code>EmitGameObjectTargeted</code>) are distinct. A message emitted to a specific component won't be received by handlers registered at the GameObject level for the same object, and vice versa. Be deliberate about which level you're targeting.</p>"},{"location":"concepts/mental-model/#forgetting-to-call-baseregistermessagehandlers","title":"Forgetting to Call <code>base.RegisterMessageHandlers()</code>","text":"<p>See the warning in The Pattern above. Always call the base method first when overriding <code>RegisterMessageHandlers()</code>.</p>"},{"location":"concepts/mental-model/#not-understanding-synchronous-handler-execution","title":"Not Understanding Synchronous Handler Execution","text":"<p>Message handlers are called synchronously and immediately when a message is emitted. This means:</p> <ul> <li>The emitting code blocks until all handlers complete</li> <li>Long-running handlers will cause frame hitches</li> <li>Handlers execute in registration order (modified by priority)</li> <li>Exceptions in handlers can affect other handlers if not caught</li> </ul> <p>Design your handlers to be fast and non-blocking.</p>"},{"location":"concepts/mental-model/#next-steps","title":"Next Steps","text":"<ul> <li>Message Types: Detailed reference for all three types</li> <li>Listening Patterns: All the ways to receive messages</li> <li>Getting Started: Hands-on walkthrough</li> <li>Patterns Guide: Real-world usage patterns</li> </ul>"},{"location":"concepts/message-types/","title":"Message Types: When and How to Use","text":"<p>\u2190 Back to Index | Getting Started | Patterns | Visual Guide</p> <p>This guide introduces the three message categories in DxMessaging with concepts, when to use them, and practical code.</p>"},{"location":"concepts/message-types/#overview","title":"Overview","text":"<ul> <li>Untargeted: global notifications anyone can listen to (e.g., world regenerated).</li> <li>Targeted: directed at one recipient (e.g., heal Player by 10).</li> <li>Broadcast: emitted from a source for anyone to observe (e.g., Enemy took 5 damage).</li> </ul>"},{"location":"concepts/message-types/#quick-decision-guide","title":"Quick Decision Guide","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TD\n Start([Choose Message Type])\n\n Start --> Q1{Is it a global<br/>announcement?}\n\n Q1 -->|Yes<br/>e.g., game paused,<br/>settings changed| Untargeted[\u2705 Use UNTARGETED<br/>Everyone listens]\n\n Q1 -->|No| Q2{Are you commanding<br/>a specific entity?}\n\n Q2 -->|Yes<br/>e.g., heal Player,<br/>open Chest #3| Targeted[\u2705 Use TARGETED<br/>One recipient]\n\n Q2 -->|No| Q3{Is an entity announcing<br/>something happened?}\n\n Q3 -->|Yes<br/>e.g., Enemy died,<br/>Chest opened| Broadcast[\u2705 Use BROADCAST<br/>Anyone can observe]\n\n Q3 -->|No| Rethink[\ud83e\udd14 Rethink your<br/>message design]\n\n classDef primary stroke-width:3px\n class Untargeted primary\n classDef warning stroke-width:3px\n class Targeted warning\n classDef success stroke-width:3px\n class Broadcast success\n classDef danger stroke-width:2px\n class Rethink danger\n classDef neutral stroke-width:2px\n class Start,Q1,Q2,Q3 neutral</code></pre>"},{"location":"concepts/message-types/#untargeted-messages","title":"Untargeted Messages","text":"<ul> <li>Use for cross\u2011cutting notifications: settings changed, scene loaded, world regenerated.</li> <li>Any listener can subscribe; no specific sender/recipient required.</li> <li>Define as immutable structs; prefer generic interface for zero\u2011boxing.</li> </ul> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneLoaded\n{\n public readonly int buildIndex;\n}\n\n// Emit (bind struct to a variable)\nvar sceneLoaded = new SceneLoaded(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex);\nsceneLoaded.Emit();\n</code></pre>"},{"location":"concepts/message-types/#targeted-messages","title":"Targeted Messages","text":"<ul> <li>Use for commands/events directed at one entity: Heal, EquipWeapon, OpenDoor.</li> <li>You address a specific <code>InstanceId</code> (e.g., a player GameObject/component).</li> <li>Ideal when only one recipient should act.</li> </ul> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing UnityEngine;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n\n// Emit to one target (GameObject)\nvar heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGameObject);\n</code></pre>"},{"location":"concepts/message-types/#broadcast-messages","title":"Broadcast Messages","text":"<ul> <li>Use for reactionary \u201cfacts\u201d about a specific source: TookDamage, PickedUpItem.</li> <li>Many systems can observe and react independently.</li> <li>Distinct from targeted: the source is the sender; listeners decide if they care.</li> </ul> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing UnityEngine;\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage\n{\n public readonly int amount;\n}\n\n// Emit from a source (GameObject)\nvar hit = new TookDamage(5);\nhit.EmitGameObjectBroadcast(enemyGameObject);\n</code></pre>"},{"location":"concepts/message-types/#organizing-messages-with-nested-types","title":"Organizing Messages with Nested Types","text":"<p>Group related messages inside a container class for better organization:</p> C#<pre><code>using DxMessaging.Core.Attributes;\n\npublic partial class CombatEvents\n{\n [DxTargetedMessage]\n [DxAutoConstructor]\n public readonly partial struct Heal\n {\n public readonly int amount;\n [DxOptionalParameter(false)] // Custom default value\n public readonly bool showEffect;\n }\n\n [DxBroadcastMessage]\n [DxAutoConstructor]\n public readonly partial struct TookDamage\n {\n public readonly int amount;\n [DxOptionalParameter(Expression = \"DamageType.Physical\")] // Enum default\n public readonly DamageType type;\n }\n}\n\n// Usage:\nvar heal = new CombatEvents.Heal(10, showEffect: true);\nheal.EmitComponentTargeted(player);\n\nvar damage = new CombatEvents.TookDamage(5); // Uses DamageType.Physical\ndamage.EmitGameObjectBroadcast(enemy);\n</code></pre>"},{"location":"concepts/message-types/#benefits","title":"Benefits","text":"<ul> <li>Reduces namespace pollution</li> <li>Makes message relationships clear</li> <li>Works with all message types (Untargeted, Targeted, Broadcast)</li> <li>Full source generator support</li> </ul>"},{"location":"concepts/message-types/#listening-to-everything-in-a-category","title":"Listening to everything in a category","text":"<ul> <li>All targeted of a type (any target): <code>RegisterTargetedWithoutTargeting<T></code> or post\u2011process with <code>RegisterTargetedWithoutTargetingPostProcessor<T></code>.</li> <li>All broadcast of a type (any source): <code>RegisterBroadcastWithoutSource<T></code> or post\u2011process with <code>RegisterBroadcastWithoutSourcePostProcessor<T></code>.</li> </ul> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\n// Observe every Heal regardless of target\n_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\nvoid OnAnyHeal(ref InstanceId target, ref Heal m) => Audit(target, m);\n\n// Observe every TookDamage regardless of source\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyTookDamage);\nvoid OnAnyTookDamage(ref InstanceId source, ref TookDamage m) => Track(source, m);\n</code></pre>"},{"location":"concepts/message-types/#choosing-the-right-type","title":"Choosing the right type","text":"<ul> <li>Start with Broadcast for \u201cX happened at Y\u201d facts others may observe.</li> <li>Use Targeted when one specific recipient must act.</li> <li>Use Untargeted for global state changes anyone might care about.</li> </ul>"},{"location":"concepts/message-types/#dos","title":"Do's","text":"<ul> <li>Keep messages small, immutable, and specific.</li> <li>Use attributes + <code>DxAutoConstructor</code> for clarity and onboarding.</li> <li>Use GameObject/Component helpers (<code>EmitGameObject*</code>/<code>EmitComponent*</code>) instead of manual <code>InstanceId</code> casts.</li> <li>Organize related messages using nested types for better structure.</li> <li>Use internal visibility for implementation-only messages.</li> </ul>"},{"location":"concepts/message-types/#donts","title":"Don\u2019ts","text":"<ul> <li>Don\u2019t use Untargeted for per\u2011entity commands; prefer Targeted.</li> <li>Don\u2019t overload Broadcast for commands; commands need a recipient (Targeted).</li> <li>Avoid deep inheritance trees; messages should be small, flat data.</li> <li>Don\u2019t emit from temporaries; bind structs to a variable before <code>Emit*</code>.</li> </ul>"},{"location":"concepts/message-types/#related-documentation","title":"Related Documentation","text":""},{"location":"concepts/message-types/#prerequisites","title":"Prerequisites","text":"<ul> <li>\u2192 Getting Started \u2014 Understand the basics first</li> <li>\u2192 Visual Guide \u2014 See the 3 types visualized</li> </ul>"},{"location":"concepts/message-types/#next-steps","title":"Next Steps","text":"<ul> <li>\u2192 Patterns \u2014 Real-world examples of each type</li> <li>\u2192 Listening Patterns \u2014 All the ways to receive messages</li> <li>\u2192 Interceptors & Ordering \u2014 Control message flow</li> </ul>"},{"location":"concepts/message-types/#try-it","title":"Try It","text":"<ul> <li>\u2192 Quick Start \u2014 Working example</li> <li>\u2192 Mini Combat sample \u2014 See all 3 types in action</li> </ul>"},{"location":"concepts/targeting-and-context/","title":"Targeting and Context (Component vs GameObject)","text":"<p>Critical Unity concept: Understanding GameObject vs Component targeting is essential for using DxMessaging effectively.</p>"},{"location":"concepts/targeting-and-context/#overview","title":"Overview","text":"<p>Targeted and broadcast messages carry context: an <code>InstanceId</code> for the target (targeted) or source (broadcast). In Unity, <code>InstanceId</code> can represent either a <code>GameObject</code> or a specific <code>Component</code>. These are completely separate channels \u2014 mixing them is the #1 cause of \"why isn't my handler firing?\" bugs.</p>"},{"location":"concepts/targeting-and-context/#key-concepts","title":"Key Concepts","text":""},{"location":"concepts/targeting-and-context/#instanceid","title":"InstanceId","text":"<ul> <li>Implicit conversion: Automatically converts from both <code>GameObject</code> and <code>Component</code></li> <li>Equality: Based on Unity's instance ID (internally stored as an <code>int</code>)</li> <li>Matching: The bus compares <code>InstanceId</code> values for exact matches</li> <li>Separate channels: GameObject and Component are distinct addresses</li> </ul>"},{"location":"concepts/targeting-and-context/#matching-rules","title":"Matching Rules","text":"<ul> <li>A handler registered for a <code>GameObject</code> only receives messages emitted to that <code>GameObject</code></li> <li>A handler registered for a <code>Component</code> only receives messages emitted to that <code>Component</code></li> <li>Emitting to a <code>GameObject</code> does not reach Component-registered handlers (and vice versa)</li> <li>This applies to both targeted and broadcast messages</li> </ul>"},{"location":"concepts/targeting-and-context/#understanding-gameobject-vs-component-targeting","title":"Understanding GameObject vs Component Targeting","text":""},{"location":"concepts/targeting-and-context/#what-gameobject-targeting-means","title":"What GameObject Targeting Means","text":"<p>When you emit to a GameObject, you're saying: \"This message is for whatever is attached to this GameObject.\"</p> C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n// Emit to GameObject\nvar heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGameObject);\n</code></pre>"},{"location":"concepts/targeting-and-context/#who-receives-it","title":"Who receives it?","text":"<ul> <li>Any Component on that GameObject that registered with <code>RegisterGameObjectTargeted</code></li> <li>Multiple components on the same GameObject can all receive it</li> <li>Components on child/parent GameObjects will NOT receive it</li> </ul>"},{"location":"concepts/targeting-and-context/#use-when","title":"Use when","text":"<ul> <li>You don't care which specific component handles it</li> <li>You want flexibility (multiple components can respond)</li> <li>You're commanding \"the player\" or \"the enemy\" as a conceptual unit</li> </ul>"},{"location":"concepts/targeting-and-context/#what-component-targeting-means","title":"What Component Targeting Means","text":"<p>When you emit to a Component, you're saying: \"This message is specifically for this one Component instance.\"</p> C#<pre><code>// Emit to specific Component\nvar heal = new Heal(10);\nheal.EmitComponentTargeted(playerHealthComponent);\n</code></pre>"},{"location":"concepts/targeting-and-context/#who-receives-it_1","title":"Who receives it?","text":"<ul> <li>ONLY that specific Component instance</li> <li>Other components on the same GameObject will NOT receive it</li> <li>This is a direct, pinpoint message</li> </ul>"},{"location":"concepts/targeting-and-context/#use-when_1","title":"Use when","text":"<ul> <li>You have a reference to the exact component you want to message</li> <li>You need precise control (only this component, not its siblings)</li> <li>You're working with component-specific logic</li> </ul>"},{"location":"concepts/targeting-and-context/#visual-comparison-targeted-messages","title":"Visual Comparison: Targeted Messages","text":"C#<pre><code>// Setup: Player GameObject with multiple components\nGameObject player = /* ... */;\nHealthComponent health = player.GetComponent<HealthComponent>();\nUIComponent ui = player.GetComponent<UIComponent>();\n\n// Both components register for Heal on the GameObject\n_ = healthToken.RegisterGameObjectTargeted<Heal>(player, health.OnHeal);\n_ = uiToken.RegisterGameObjectTargeted<Heal>(player, ui.OnHeal);\n\n// Scenario 1: Target the GameObject\nheal.EmitGameObjectTargeted(player);\n// \u2705 health.OnHeal() fires\n// \u2705 ui.OnHeal() fires\n// Both components receive it!\n\n// Scenario 2: Target a Component (but registered for GameObject)\nheal.EmitComponentTargeted(health);\n// \u274c health.OnHeal() does NOT fire (registered for GameObject, not Component)\n// \u274c ui.OnHeal() does NOT fire\n// Nothing happens! Wrong channel!\n\n// Scenario 3: Register for Component, emit to Component\n_ = healthToken.RegisterComponentTargeted<Heal>(health, health.OnHeal);\nheal.EmitComponentTargeted(health);\n// \u2705 health.OnHeal() fires\n// \u274c ui.OnHeal() does NOT fire (different component)\n</code></pre>"},{"location":"concepts/targeting-and-context/#broadcast-messages-same-rules-apply","title":"Broadcast Messages: Same Rules Apply","text":"<p>Broadcast messages follow the exact same GameObject vs Component rules:</p> C#<pre><code>[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\n// Broadcast from GameObject\nvar damage = new TookDamage(5);\ndamage.EmitGameObjectBroadcast(enemyGameObject);\n\n// Register for broadcasts from this GameObject\n_ = token.RegisterGameObjectBroadcast<TookDamage>(enemyGameObject, OnEnemyDamage);\n// \u2705 OnEnemyDamage fires when damage.EmitGameObjectBroadcast(enemyGameObject) is called\n\n// Register for broadcasts from this Component\n_ = token.RegisterComponentBroadcast<TookDamage>(enemyComponent, OnComponentDamage);\n// \u274c OnComponentDamage does NOT fire (registered for Component, but emitted from GameObject)\n</code></pre>"},{"location":"concepts/targeting-and-context/#the-this-trap","title":"The <code>this</code> Trap","text":"<p>Most common mistake: Using <code>this</code> in a MonoBehaviour and forgetting it's a Component, not a GameObject.</p> C#<pre><code>public class Enemy : MonoBehaviour\n{\n void Start()\n {\n // \u274c WRONG: Registered for GameObject\n _ = token.RegisterGameObjectTargeted<TakeDamage>(gameObject, OnDamage);\n }\n\n void TakeDamageFrom(GameObject attacker)\n {\n var damage = new TakeDamage(10);\n // \u274c WRONG: Emitting to Component (this)\n damage.EmitAt(this); // WON'T BE RECEIVED!\n }\n\n void OnDamage(ref TakeDamage msg) { }\n}\n\n// FIX 1: Both use GameObject\n_ = token.RegisterGameObjectTargeted<TakeDamage>(gameObject, OnDamage);\ndamage.EmitAt(gameObject); // \u2705 Works!\n\n// FIX 2: Both use Component\n_ = token.RegisterComponentTargeted<TakeDamage>(this, OnDamage);\ndamage.EmitAt(this); // \u2705 Works!\n</code></pre> <p>Remember: In Unity, <code>this</code> inside a <code>MonoBehaviour</code> is always a Component, never a GameObject!</p>"},{"location":"concepts/targeting-and-context/#listening-to-all-events-global-observers","title":"Listening to ALL Events (Global Observers)","text":"<p>Feature: DxMessaging allows listening to all targeted or broadcast messages without knowing the specific target or source.</p> <p>This is useful for:</p> <ul> <li>Analytics \u2014 Track every action in your game without coupling to individual objects</li> <li>Debugging \u2014 See all events of a type in one place</li> <li>Cross-cutting concerns \u2014 Achievements, logging, VFX that respond to any entity's events</li> </ul>"},{"location":"concepts/targeting-and-context/#why-this-is-different-from-classic-event-buses","title":"Why this is different from classic event buses","text":"Approach Classic Event Bus DxMessaging Global Observers Subscriptions needed One per entity type One for ALL entities Coupling Tight (know all entity types) Zero (no knowledge needed) New entity types Update all subscribers Zero changes needed Context Lost (who was damaged?) Provided (source/target parameter)"},{"location":"concepts/targeting-and-context/#classic-event-bus-anti-pattern","title":"Classic Event Bus Anti-Pattern","text":"C#<pre><code>// \u274c Traditional approach: Tight coupling, multiple subscriptions\nEventBus.PlayerDamaged += OnPlayerDamaged;\nEventBus.EnemyDamaged += OnEnemyDamaged;\nEventBus.NPCDamaged += OnNPCDamaged;\nEventBus.BossDamaged += OnBossDamaged;\n// Add new entity? Update EVERY analytics/logging system!\n\nvoid OnPlayerDamaged(int amount) { RecordDamage(\"Player\", amount); }\nvoid OnEnemyDamaged(int amount) { RecordDamage(\"Enemy\", amount); }\nvoid OnNPCDamaged(int amount) { RecordDamage(\"NPC\", amount); }\nvoid OnBossDamaged(int amount) { RecordDamage(\"Boss\", amount); }\n</code></pre>"},{"location":"concepts/targeting-and-context/#dxmessaging-global-observer-pattern","title":"DxMessaging Global Observer Pattern","text":"C#<pre><code>// \u2705 DxMessaging: One subscription, zero coupling\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n\nvoid OnAnyDamage(ref InstanceId source, ref TookDamage msg)\n{\n // Works for Player, Enemy, NPC, Boss, and any future entity type!\n RecordDamage(source, msg.amount);\n}\n// Add new entity? Zero changes needed.\n</code></pre>"},{"location":"concepts/targeting-and-context/#listening-to-all-targeted-messages","title":"Listening to All Targeted Messages","text":"<p>Use <code>RegisterTargetedWithoutTargeting</code> to receive ALL targeted messages of a type, regardless of target:</p> C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n// Listen to ALL heal messages, no matter who they're targeted at\n_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\n\nvoid OnAnyHeal(ref InstanceId target, ref Heal msg)\n{\n Debug.Log($\"Someone healed {target} for {msg.amount}\");\n // You get the target as a parameter so you know WHO was healed\n}\n\n// Now when ANYONE emits a heal...\nheal.EmitAt(player); // OnAnyHeal fires with target = player\nheal.EmitAt(enemy); // OnAnyHeal fires with target = enemy\nheal.EmitAt(npc); // OnAnyHeal fires with target = npc\n</code></pre>"},{"location":"concepts/targeting-and-context/#use-cases","title":"Use cases","text":"<ul> <li>Analytics (\"track all damage dealt\")</li> <li>Debugging (\"log every heal in the game\")</li> <li>Achievements (\"count total kills\")</li> <li>Global UI updates (\"show floating damage numbers for any entity\")</li> </ul>"},{"location":"concepts/targeting-and-context/#listening-to-all-broadcast-messages","title":"Listening to All Broadcast Messages","text":"<p>Use <code>RegisterBroadcastWithoutSource</code> to receive ALL broadcast messages of a type, regardless of source:</p> C#<pre><code>[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\n// Listen to ALL damage events from any source\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n\nvoid OnAnyDamage(ref InstanceId source, ref TookDamage msg)\n{\n Debug.Log($\"{source} took {msg.amount} damage\");\n // You get the source as a parameter so you know WHO took damage\n}\n\n// Now when ANYONE broadcasts damage...\ndamage.EmitFrom(enemy); // OnAnyDamage fires with source = enemy\ndamage.EmitFrom(player); // OnAnyDamage fires with source = player\ndamage.EmitFrom(boss); // OnAnyDamage fires with source = boss\n</code></pre>"},{"location":"concepts/targeting-and-context/#use-cases_1","title":"Use cases","text":"<ul> <li>Combat logs (\"record all damage in the scene\")</li> <li>Particle effects (\"spawn blood VFX for any damage\")</li> <li>Achievement tracking (\"count enemy deaths\")</li> <li>Analytics dashboards (\"total damage per second\")</li> </ul>"},{"location":"concepts/targeting-and-context/#specific-vs-global-listeners-both-can-coexist","title":"Specific vs Global Listeners: Both Can Coexist","text":"<p>You can have BOTH specific listeners AND global listeners for the same message:</p> C#<pre><code>// Specific listener: only cares about player damage\n_ = playerToken.RegisterGameObjectBroadcast<TookDamage>(playerGameObject, OnPlayerDamage);\n\n// Global listener: cares about ALL damage\n_ = analyticsToken.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n\n// When player takes damage\ndamage.EmitFrom(playerGameObject);\n// \u2705 OnPlayerDamage fires (specific listener)\n// \u2705 OnAnyDamage fires (global listener)\n// Both fire!\n\n// When enemy takes damage\ndamage.EmitFrom(enemyGameObject);\n// \u274c OnPlayerDamage does NOT fire (wrong source)\n// \u2705 OnAnyDamage fires (global listener catches everything)\n</code></pre>"},{"location":"concepts/targeting-and-context/#real-world-example-combat-system","title":"Real-World Example: Combat System","text":"<p>Here's how to combine specific and global listeners in a combat system:</p> C#<pre><code>public class Player : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n // Listen only to damage targeting THIS player\n _ = Token.RegisterGameObjectBroadcast<TookDamage>(gameObject, OnPlayerTookDamage);\n }\n\n void OnPlayerTookDamage(ref TookDamage msg)\n {\n // Player-specific logic: update health bar, play hurt sound\n health -= msg.amount;\n PlayHurtSound();\n }\n}\n\npublic class CombatLog : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n // Listen to ALL damage from ANY source (global observer)\n _ = Token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n }\n\n void OnAnyDamage(ref InstanceId source, ref TookDamage msg)\n {\n // Global combat logging\n LogToFile($\"{Time.time}: {source} took {msg.amount} damage\");\n }\n}\n\npublic class AchievementSystem : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n // Listen to ALL damage targeting ANY entity (global observer)\n _ = Token.RegisterTargetedWithoutTargeting<DealDamage>(OnAnyDamageDealt);\n }\n\n void OnAnyDamageDealt(ref InstanceId target, ref DealDamage msg)\n {\n // Track total damage dealt for achievements\n totalDamageDealt += msg.amount;\n CheckDamageAchievements();\n }\n}\n</code></pre>"},{"location":"concepts/targeting-and-context/#performance-note","title":"Performance Note","text":"<p>Global listeners (<code>RegisterTargetedWithoutTargeting</code> / <code>RegisterBroadcastWithoutSource</code>) are slightly slower than specific listeners because they receive every message of that type. This is usually negligible, but avoid them in hot paths if you're emitting thousands of messages per frame.</p>"},{"location":"concepts/targeting-and-context/#rule-of-thumb","title":"Rule of thumb","text":"<ul> <li>For gameplay (< 100 messages/frame): Use freely</li> <li>For analytics/debugging: Perfect use case</li> <li>For tight loops (> 1000 messages/frame): Prefer specific listeners</li> </ul>"},{"location":"concepts/targeting-and-context/#when-to-use-which","title":"When to Use Which","text":""},{"location":"concepts/targeting-and-context/#gameobject-context","title":"GameObject Context","text":""},{"location":"concepts/targeting-and-context/#use-when_2","title":"Use when","text":"<ul> <li>The whole object is the logical target/source (e.g., \"Player was healed\", \"Enemy took damage\")</li> <li>Multiple components should coordinate under the same address</li> <li>You don't care which specific component handles it</li> <li>You want flexibility for future refactoring (adding/removing components)</li> </ul>"},{"location":"concepts/targeting-and-context/#examples","title":"Examples","text":"<ul> <li>Combat: \"Deal damage to this enemy\"</li> <li>UI: \"Update all panels related to this character\"</li> <li>State: \"Notify all systems that this entity died\"</li> </ul>"},{"location":"concepts/targeting-and-context/#component-context","title":"Component Context","text":""},{"location":"concepts/targeting-and-context/#use-when_3","title":"Use when","text":"<ul> <li>The message is explicitly for one component's responsibility (e.g., \"open JUST this DoorController\")</li> <li>Multiple components on the same GameObject must be independently addressable</li> <li>You need pinpoint precision</li> <li>You have a direct reference to the target component</li> </ul>"},{"location":"concepts/targeting-and-context/#examples_1","title":"Examples","text":"<ul> <li>UI: \"Update this specific health bar, not the mana bar\"</li> <li>Animation: \"Tell this animator to play a clip\"</li> <li>Physics: \"Toggle this specific collider\"</li> </ul>"},{"location":"concepts/targeting-and-context/#best-practices","title":"Best Practices","text":""},{"location":"concepts/targeting-and-context/#dos","title":"Do's","text":"<p>\u2705 Pick a context and be consistent across emitters and listeners for that message type \u2705 Prefer GameObject when in doubt \u2014 easier coordination among components on the same object \u2705 Use explicit helpers (<code>EmitGameObjectTargeted</code>, <code>RegisterGameObjectTargeted</code>) to make intent clear \u2705 Document which context your message types use (add it to your message comments) \u2705 Use global listeners for analytics, debugging, and cross-cutting concerns</p>"},{"location":"concepts/targeting-and-context/#donts","title":"Don'ts","text":"<p>\u274c Don't emit to GameObject and expect Component-registered handlers to receive it (and vice versa) \u274c Don't assume <code>this</code> means GameObject \u2014 it's always a Component in Unity \u274c Don't mix GameObject/Component for the same message type across your codebase \u274c Don't register the same handler under both unless you intend to handle both contexts \u274c Don't use global listeners in performance-critical tight loops (thousands of messages/frame)</p>"},{"location":"concepts/targeting-and-context/#troubleshooting","title":"Troubleshooting","text":""},{"location":"concepts/targeting-and-context/#my-handler-isnt-firing","title":"\"My handler isn't firing!\"","text":"<p>Most likely cause: GameObject/Component mismatch</p> C#<pre><code>// Check 1: Are you emitting and registering on the same type?\n// \u274c WRONG\n_ = token.RegisterGameObjectTargeted<Heal>(gameObject, OnHeal);\nheal.EmitAt(this); // Component, not GameObject!\n\n// \u2705 CORRECT\n_ = token.RegisterGameObjectTargeted<Heal>(gameObject, OnHeal);\nheal.EmitAt(gameObject); // Both GameObject\n</code></pre>"},{"location":"concepts/targeting-and-context/#other-causes","title":"Other causes","text":"<ol> <li>Handler not enabled (call <code>token.Enable()</code> in <code>OnEnable</code>)</li> <li>Registration happened after emission</li> <li>Using wrong bus (local vs global)</li> </ol> <p>See Troubleshooting for more debugging tips.</p>"},{"location":"concepts/targeting-and-context/#quick-reference","title":"Quick Reference","text":"Operation GameObject Variant Component Variant Emit Targeted <code>msg.EmitGameObjectTargeted(go)</code> <code>msg.EmitComponentTargeted(comp)</code> Emit Broadcast <code>msg.EmitGameObjectBroadcast(go)</code> <code>msg.EmitComponentBroadcast(comp)</code> Register Targeted <code>RegisterGameObjectTargeted<T>(go, handler)</code> <code>RegisterComponentTargeted<T>(comp, handler)</code> Register Broadcast <code>RegisterGameObjectBroadcast<T>(go, handler)</code> <code>RegisterComponentBroadcast<T>(comp, handler)</code> Global Targeted <code>RegisterTargetedWithoutTargeting<T>(handler)</code> (Same \u2014 no distinction) Global Broadcast <code>RegisterBroadcastWithoutSource<T>(handler)</code> (Same \u2014 no distinction)"},{"location":"concepts/targeting-and-context/#see-also","title":"See Also","text":"<ul> <li>Emit Shorthands \u2014 Concise ways to emit messages</li> <li>Message Types \u2014 Understanding Untargeted, Targeted, and Broadcast</li> <li>Unity Integration \u2014 MessageAwareComponent and lifecycle</li> <li>Quick Reference \u2014 API cheat sheet</li> <li>Troubleshooting \u2014 Solving common issues</li> </ul>"},{"location":"examples/end-to-end-scene-transitions/","title":"End\u2011to\u2011End Example: Scene Transitions + Overlay Pause","text":"<p>Short intro</p> <p>An end\u2011to\u2011end pattern showing scene\u2011scoped buses, a global overlay that persists across scenes, and pausing emissions/listeners safely.</p> <p>Scenario</p> <ul> <li>Each scene has its own local MessageBus for scene\u2011scoped flows.</li> <li>A global overlay persists across scenes and listens to global notifications.</li> <li>During pause, some systems should stop processing (Disable tokens), but others still emit (emit while disabled).</li> </ul> <p>Setup</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing UnityEngine;\n\npublic static class SceneBus\n{\n public static MessageBus Current { get; private set; } = new MessageBus();\n public static void NewScene() => Current = new MessageBus();\n}\n</code></pre> <p>Messages</p> C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxUntargetedMessage][DxAutoConstructor]\npublic readonly partial struct SceneLoaded { public readonly int buildIndex; }\n\n[DxUntargetedMessage][DxAutoConstructor]\npublic readonly partial struct Paused { }\n\n[DxUntargetedMessage][DxAutoConstructor]\npublic readonly partial struct Resumed { }\n</code></pre> <p>Global overlay (persists across scenes)</p> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core.Messages;\nusing UnityEngine;\n\npublic sealed class GlobalOverlay : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n DontDestroyOnLoad(gameObject);\n _ = Token.RegisterUntargeted<SceneLoaded>(OnSceneLoaded);\n _ = Token.RegisterUntargeted<Paused>(m => ShowPause());\n _ = Token.RegisterUntargeted<Resumed>(m => HidePause());\n }\n\n private void OnSceneLoaded(ref SceneLoaded m) => RebuildUIForScene(m.buildIndex);\n}\n</code></pre> <p>Scene systems (use local bus)</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing DxMessaging.Core.Messages;\n\npublic sealed class SceneDriver\n{\n public void OnSceneStart(int buildIndex)\n {\n // Switch to a fresh bus per scene\n SceneBus.NewScene();\n\n // Broadcast globally (untargeted) so overlay updates\n var info = new SceneLoaded(buildIndex);\n info.Emit();\n\n // For scene\u2011local flows, pass the local bus to tokens\n // var token = MessageRegistrationToken.Create(handler, SceneBus.Current);\n }\n}\n</code></pre> <p>Pausing</p> C#<pre><code>using DxMessaging.Unity;\nusing UnityEngine;\n\npublic sealed class PauseController : MonoBehaviour\n{\n public MessagingComponent[] systemsToPause; // listeners to disable during pause\n\n public void Pause()\n {\n foreach (var mc in systemsToPause)\n {\n mc.ToggleMessageHandler(false); // disable listeners\n }\n\n var p = new Paused(); p.Emit(); // overlay still listening globally\n }\n\n public void Resume()\n {\n var r = new Resumed(); r.Emit();\n foreach (var mc in systemsToPause)\n {\n mc.ToggleMessageHandler(true);\n }\n }\n}\n</code></pre> <p>Emitting while disabled</p> C#<pre><code>// For specific emitters that must keep sending even if disabled:\nmessaging.emitMessagesWhenDisabled = true;\n</code></pre> <p>Notes</p> <ul> <li>Use global untargeted messages to communicate scene transitions to global overlays.</li> <li>Keep scene\u2011specific flows isolated on a per\u2011scene bus (pass to tokens for registrations and emit to it).</li> <li>Toggle listeners off to pause, and opt into emission while disabled for emitters that must remain active.</li> </ul>"},{"location":"examples/end-to-end/","title":"End\u2011to\u2011End Example: Combat + UI + Settings","text":"<p>Short intro</p> <p>A small scenario tying together Untargeted, Targeted, and Broadcast messages with Unity integration, ordering, interceptors, and diagnostics.</p> <p>Scenario</p> <ul> <li>Global: settings change (Untargeted)</li> <li>Targeted: heal a specific player (Targeted)</li> <li>Broadcast: enemies report damage taken (Broadcast)</li> <li>UI listens globally to update overlays</li> <li>Analytics listens across all sources/targets</li> </ul> <p>Messages</p> C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxUntargetedMessage][DxAutoConstructor]\npublic readonly partial struct VideoSettingsChanged { public readonly int width; public readonly int height; }\n\n[DxTargetedMessage][DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n[DxBroadcastMessage][DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n</code></pre> <p>Unity: Player component (targeted)</p> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core.Messages;\n\npublic sealed class Player : MessageAwareComponent\n{\n private int _hp;\n\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterComponentTargeted<Heal>(this, OnHeal);\n }\n\n private void OnHeal(ref Heal m) => _hp += m.amount;\n}\n</code></pre> <p>Unity: Enemy component (broadcast)</p> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core.Extensions;\nusing DxMessaging.Core.Messages;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class Enemy : UnityEngine.MonoBehaviour\n{\n public void ApplyDamage(int amount)\n {\n var took = new TookDamage(amount);\n took.EmitGameObjectBroadcast(gameObject);\n }\n}\n</code></pre> <p>UI overlay (global + all\u2011sources)</p> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core;\nusing DxMessaging.Core.Messages;\nusing UnityEngine;\n\npublic sealed class UIOverlay : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<VideoSettingsChanged>(OnSettings);\n _ = Token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n }\n\n private void OnSettings(ref VideoSettingsChanged m) => RebuildUI(m.width, m.height);\n private void OnAnyDamage(ref InstanceId src, ref TookDamage m) => ShowFloatingText(src, $\"-{m.amount}\");\n}\n</code></pre> <p>Interceptors and post\u2011processing (ordering)</p> C#<pre><code>using DxMessaging.Core; // MessageHandler\nusing DxMessaging.Core.MessageBus; // IMessageBus\n\nvar bus = MessageHandler.MessageBus;\n\n// Normalize negatives to zero and clamp max\n_ = bus.RegisterTargetedInterceptor<Heal>((ref InstanceId tgt, ref Heal m) =>\n{\n var amount = UnityEngine.Mathf.Clamp(m.amount, 0, 999);\n if (amount == 0) return false; // cancel zero heals\n m = new Heal(amount);\n return true;\n}, priority: 0);\n\n// Log after handlers\n_ = token.RegisterBroadcastWithoutSourcePostProcessor<TookDamage>((InstanceId src, TookDamage m) =>\n{\n Analytics.Log(\"Damage\", new { src, m.amount });\n});\n</code></pre> <p>Settings menu (untargeted)</p> C#<pre><code>using DxMessaging.Core.Extensions;\n\npublic sealed class SettingsMenu\n{\n public void Apply(int width, int height)\n {\n var changed = new VideoSettingsChanged(width, height);\n changed.Emit();\n }\n}\n</code></pre> <p>Diagnostics (Editor)</p> <ul> <li>On any GameObject with a MessagingComponent:</li> <li>Enable Global Diagnostics to record emissions.</li> <li>Inspect Global Buffer for recent messages (type + context). Matching listeners are highlighted.</li> <li>Inspect Local Buffer per listener and paginated Registrations with priorities and contexts.</li> </ul> <p>Notes</p> <ul> <li>Use component vs gameObject context consistently for targeted/broadcast messages (see Targeting & Context).</li> <li>For tests/subsystems, use a local MessageBus and pass it to the token factory.</li> </ul>"},{"location":"getting-started/","title":"DxMessaging Documentation Hub","text":"<p>This is the documentation for DxMessaging, a type-safe messaging system for Unity.</p>"},{"location":"getting-started/#visual-documentation-map","title":"Visual Documentation Map","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TD\n Start[START HERE<br/>Visual Guide<br/>5 minutes]\n Start --> Quick[Quick Start<br/>5 min]\n Start --> Getting[Getting Started<br/>10 min]\n Start --> Overview[Overview<br/>5 min]\n Quick --> Patterns[Patterns & Samples<br/>Hands-on!]\n Getting --> Patterns\n Overview --> Patterns\n\n classDef primary stroke-width:3px\n class Start primary\n classDef success stroke-width:2px\n class Patterns success\n classDef secondary stroke-width:2px\n class Quick,Getting,Overview secondary</code></pre>"},{"location":"getting-started/#table-of-contents","title":"Table of Contents","text":"<ul> <li>New to DxMessaging</li> <li>Learning Path</li> <li>Core Documentation</li> <li>Reference</li> <li>Examples and Samples</li> <li>By Use Case</li> <li>Quick Start Path</li> </ul>"},{"location":"getting-started/#new-to-dxmessaging","title":"New to DxMessaging","text":"<p>Never used a messaging system before? Start here:</p> <ol> <li>Mental Model - How to think about DxMessaging (10 min)</li> <li>Visual Guide - Beginner-friendly visual introduction (5 min)</li> <li>Getting Started Guide - Comprehensive guide with examples (10 min)</li> <li>Quick Start - Your first working message (5 min)</li> <li>Overview - What DxMessaging is and why it exists (5 min)</li> </ol>"},{"location":"getting-started/#learning-path","title":"Learning Path","text":""},{"location":"getting-started/#for-absolute-beginners-never-used-messaging-before","title":"For Absolute Beginners (Never Used Messaging Before)","text":"<ol> <li>Read Mental Model (10 min) - Philosophy first!</li> <li>Read Visual Guide (5 min) - Pictures and analogies!</li> <li>Read Getting Started (10 min) - Complete introduction</li> <li>Try Quick Start (5 min) - Hands-on tutorial</li> <li>Understand Message Types (10 min) - When to use what</li> <li>Study Common Patterns (15 min) - Real examples</li> </ol>"},{"location":"getting-started/#for-advanced-users","title":"For Advanced Users","text":"<ol> <li>Master Interceptors & Ordering</li> <li>Explore Listening Patterns</li> <li>Deep dive into Design & Architecture</li> <li>Review Advanced Topics</li> </ol>"},{"location":"getting-started/#core-documentation","title":"Core Documentation","text":""},{"location":"getting-started/#essentials","title":"Essentials","text":"<ul> <li>Visual Guide - Beginner-friendly visual introduction with pictures and analogies</li> <li>Getting Started - Complete beginner's guide with mental models</li> <li>Overview - What and why</li> <li>Quick Start - First message in 5 minutes</li> <li>Message Types - When to use Untargeted/Targeted/Broadcast</li> <li>Comparisons - DxMessaging vs C# Events, UnityEvents, SendMessage</li> </ul>"},{"location":"getting-started/#core-concepts","title":"Core Concepts","text":"<ul> <li>Interceptors & Ordering - Validate, transform, control execution</li> <li>Listening Patterns - All the ways to receive messages</li> <li>Targeting & Context - GameObject vs Component</li> <li>Patterns - Real-world usage patterns</li> </ul>"},{"location":"getting-started/#unity-integration","title":"Unity Integration","text":"<ul> <li>Unity Integration - MessagingComponent, MessageAwareComponent</li> <li>Diagnostics - Inspector tools, debugging, observability</li> </ul>"},{"location":"getting-started/#architecture--performance","title":"Architecture & Performance","text":"<ul> <li>Design & Architecture - Deep dive into internals and optimizations</li> <li>Performance Benchmarks - OS-specific tables auto-generated by tests</li> <li>Advanced - Lifecycles, safety, manual control</li> </ul>"},{"location":"getting-started/#reference","title":"Reference","text":""},{"location":"getting-started/#quick-lookups","title":"Quick Lookups","text":"<ul> <li>Glossary - All terms explained in plain English</li> <li>Quick Reference - API cheat sheet</li> <li>API Reference - Complete API documentation</li> <li>FAQ - Common questions</li> <li>Troubleshooting - Solving common issues</li> </ul>"},{"location":"getting-started/#tools--utilities","title":"Tools & Utilities","text":"<ul> <li>Helpers - Source generators, attributes, extensions</li> <li>Message Bus Providers - Provider system and MessageBusProviderHandle for flexible bus configuration</li> <li>Runtime Configuration - Setting message buses at runtime, re-binding registrations</li> <li>Registration Builders - Fluent API for building message registrations</li> <li>Emit Shorthands - <code>Emit</code>/<code>EmitAt</code>/<code>EmitFrom</code> usage and pitfalls</li> <li>String Messages - Prototyping and debugging</li> <li>Compatibility - Unity versions and render pipelines</li> <li>Install - Installation guide</li> </ul>"},{"location":"getting-started/#examples-and-samples","title":"Examples and Samples","text":""},{"location":"getting-started/#unity-samples-importable","title":"Unity Samples (Importable!)","text":"<p>Located in <code>Samples~/</code> directory - Import via Unity Package Manager!</p> <ul> <li>Mini Combat - Interactive combat demo with Heal/Damage messages</li> <li>Perfect first example to understand message flow</li> <li>Shows Targeted and Broadcast messages in action</li> <li> <p>Complete working scene you can play with</p> </li> <li> <p>UI Buttons + Inspector - Interactive diagnostics demo</p> </li> <li>See the Inspector diagnostics in action</li> <li>Explore message history and handler registrations</li> <li>Great for debugging and understanding the system</li> </ul>"},{"location":"getting-started/#code-examples-in-docs","title":"Code Examples (In Docs)","text":"<ul> <li>End-to-End Example - Complete feature walkthrough</li> <li>Scene Transitions Example - Scene management pattern</li> </ul>"},{"location":"getting-started/#real-world-patterns","title":"Real-World Patterns","text":"<p>From Common Patterns:</p> <ul> <li>Scene-wide events (Untargeted)</li> <li>Directed commands (Targeted)</li> <li>Observability (Broadcast)</li> <li>Validation with Interceptors</li> <li>Analytics with Post-Processors</li> <li>Local bus islands for testing</li> </ul>"},{"location":"getting-started/#by-use-case","title":"By Use Case","text":""},{"location":"getting-started/#i-want-to","title":"\"I want to...\"","text":"<ul> <li>Decouple my systems - Start with Getting Started</li> <li>Replace C# events - See Comparisons</li> <li>Send a command to one object - Use Targeted Messages</li> <li>Broadcast an event - Use Broadcast Messages</li> <li>Notify globally - Use Untargeted Messages</li> <li>Validate messages before execution - Learn Interceptors</li> <li>Track all damage/events - See Listening Without Context</li> <li>Debug message flow - Use Diagnostics</li> <li>Optimize performance - Read Performance Tips</li> <li>Isolate tests - Create Local Bus Islands</li> <li>Use dependency injection - DxMessaging + Zenject, DxMessaging + VContainer, DxMessaging + Reflex</li> <li>Configure buses at runtime - See Runtime Configuration</li> <li>Use message bus providers - Learn Message Bus Providers</li> </ul>"},{"location":"getting-started/#visual-message-pipeline","title":"Visual: Message Pipeline","text":"<p>Every message flows through 3 stages:</p> <pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n P[Producer] --> I[Interceptors<br/>validate/mutate/cancel]\n I --> H[Handlers<br/>main logic by priority]\n H --> PP[Post-Processors<br/>analytics/logging]\n\n classDef neutral stroke-width:2px\n class P neutral\n classDef warning stroke-width:2px\n class I warning\n classDef primary stroke-width:2px\n class H primary\n classDef success stroke-width:2px\n class PP success</code></pre>"},{"location":"getting-started/#learning-resources","title":"Learning Resources","text":""},{"location":"getting-started/#must-read-docs-in-order","title":"Must-Read Docs (In Order)","text":"<ol> <li>Getting Started - Start here! (10 min)</li> <li>Message Types - Choose the right type (10 min)</li> <li>Patterns - See real examples (15 min)</li> <li>Diagnostics - Debug like a pro (10 min)</li> <li>Design & Architecture - Understand the internals (30 min)</li> </ol>"},{"location":"getting-started/#feature-specific","title":"Feature-Specific","text":"<ul> <li>Priority & Ordering - Interceptors & Ordering</li> <li>Global Observers - Listening Patterns</li> <li>Unity Lifecycle - Unity Integration</li> <li>Performance - Design & Architecture</li> </ul>"},{"location":"getting-started/#comparison-charts","title":"Comparison Charts","text":""},{"location":"getting-started/#dxmessaging-vs-alternatives","title":"DxMessaging vs Alternatives","text":"<p>From Comparisons:</p> Feature DxMessaging C# Events UnityEvents Static Bus Decoupling Full Tight Hidden Yes Lifecycle Safety Auto Manual Unity Manual Execution Order Priority Random Random Random Observability Built-in No No No Performance Zero-alloc Good Boxing Good"},{"location":"getting-started/#search-by-topic","title":"Search by Topic","text":""},{"location":"getting-started/#concepts","title":"Concepts","text":"<ul> <li>Messages - Message Types, Getting Started</li> <li>Handlers - Listening Patterns, Unity Integration</li> <li>Lifecycle - Advanced, Unity Integration</li> <li>Performance - Design & Architecture</li> <li>Testing - Patterns, Testing Guide</li> </ul>"},{"location":"getting-started/#features","title":"Features","text":"<ul> <li>Interceptors - Interceptors & Ordering</li> <li>Post-Processors - Interceptors & Ordering</li> <li>Priorities - Interceptors & Ordering</li> <li>Global Accept-All - Listening Patterns</li> <li>Diagnostics - Diagnostics</li> <li>Local Buses - Design & Architecture</li> </ul>"},{"location":"getting-started/#components","title":"Components","text":"<ul> <li>MessageBus - Design & Architecture</li> <li>MessageHandler - API Reference</li> <li>MessageRegistrationToken - Unity Integration, Advanced</li> <li>MessageAwareComponent - Unity Integration, Getting Started</li> <li>MessagingComponent - Unity Integration</li> </ul>"},{"location":"getting-started/#complete-document-list","title":"Complete Document List","text":""},{"location":"getting-started/#getting-started","title":"Getting Started","text":"<ul> <li>Visual Guide - Perfect for beginners</li> <li>Getting Started Guide</li> <li>Overview</li> <li>Quick Start</li> <li>Install</li> </ul>"},{"location":"getting-started/#core-concepts_1","title":"Core Concepts","text":"<ul> <li>Message Types</li> <li>Interceptors & Ordering</li> <li>Listening Patterns</li> <li>Targeting & Context</li> </ul>"},{"location":"getting-started/#unity","title":"Unity","text":"<ul> <li>Unity Integration</li> <li>Diagnostics</li> <li>Patterns</li> </ul>"},{"location":"getting-started/#deep-dives","title":"Deep Dives","text":"<ul> <li>Design & Architecture</li> <li>Advanced</li> <li>Comparisons</li> </ul>"},{"location":"getting-started/#reference_1","title":"Reference","text":"<ul> <li>Quick Reference</li> <li>API Reference</li> <li>Helpers</li> <li>FAQ</li> <li>Troubleshooting</li> </ul>"},{"location":"getting-started/#dependency-injection","title":"Dependency Injection","text":"<ul> <li>Runtime Configuration - Setting and overriding message buses, re-binding registrations</li> <li>Message Bus Providers - Provider system and MessageBusProviderHandle</li> <li>DxMessaging + Zenject - Zenject integration guide</li> <li>DxMessaging + VContainer - VContainer integration guide</li> <li>DxMessaging + Reflex - Reflex integration guide</li> </ul>"},{"location":"getting-started/#miscellaneous","title":"Miscellaneous","text":"<ul> <li>String Messages</li> <li>Compatibility</li> <li>End-to-End Example</li> <li>Scene Transitions Example</li> <li>Mini Combat Sample</li> <li>UI Buttons + Inspector Sample</li> </ul>"},{"location":"getting-started/#quick-start-path","title":"Quick Start Path","text":"<p>Absolute Beginner? Follow this 40-minute path:</p> <ol> <li>10 min: Mental Model - Understand the philosophy first!</li> <li>5 min: Visual Guide - Pictures & analogies</li> <li>10 min: Getting Started - Deep dive</li> <li>5 min: Quick Start - Hands-on code</li> <li>10 min: Try a Sample - See it in action</li> </ol> <p>Want to go deep? Continue with:</p> <ol> <li>15 min: Patterns</li> <li>20 min: Interceptors & Ordering</li> <li>30 min: Design & Architecture</li> </ol> <p>Need help? Check FAQ or Troubleshooting.</p>"},{"location":"getting-started/getting-started/","title":"Getting Started with DxMessaging","text":"<p>Back to Index | Visual Guide | Quick Start | FAQ</p> <p>This guide covers the basics of DxMessaging. By the end, you'll understand what DxMessaging is, when it might be useful, and how to use it in your Unity projects.</p>"},{"location":"getting-started/getting-started/#your-goal","title":"Your Goal","text":"<p>By the end of this guide, you will:</p> <ul> <li>Understand the three message types and when to use each</li> <li>Write your first message handler in Unity</li> <li>Know when to use DxMessaging vs. traditional events</li> <li>Avoid common beginner mistakes</li> </ul>"},{"location":"getting-started/getting-started/#table-of-contents","title":"Table of Contents","text":"<ul> <li>What is DxMessaging?</li> <li>Quick Start</li> <li>Message Types</li> <li>Common Patterns</li> <li>Troubleshooting</li> <li>Next Steps</li> <li>Quick Reference</li> </ul>"},{"location":"getting-started/getting-started/#what-is-dxmessaging","title":"What is DxMessaging?","text":"<p>Picture this: You're building a Unity game. Your player takes damage. Now you need to:</p> <ul> <li>Update the health bar UI</li> <li>Play a damage sound</li> <li>Show a damage number popup</li> <li>Track damage for analytics</li> <li>Check if an achievement unlocked</li> <li>Maybe trigger a tutorial tip</li> </ul> <p>One approach: The Player script needs references to all 6 systems. Or they all need references to the Player. This can create tight coupling.</p> <p>With DxMessaging: The Player emits one message: <code>TookDamage(25)</code>. Interested systems receive it through subscriptions. This approach decouples systems and handles cleanup automatically.</p> <p>Technical summary: DxMessaging is a type-safe messaging system that provides an alternative to C# events, UnityEvents, and global event buses.</p> <p>Think of it as: An alternative event system designed for larger projects.</p>"},{"location":"getting-started/getting-started/#what-you-get","title":"What you get","text":"<ul> <li>Decoupled systems - no manual subscribe/unsubscribe, leak prevention built-in</li> <li>Predictable execution - priority-based ordering, see exactly what runs when</li> <li>Actually debuggable - Inspector shows message history with timestamps</li> <li>Designed to scale - works for prototypes and larger codebases</li> </ul>"},{"location":"getting-started/getting-started/#quick-start","title":"Quick Start","text":"<ol> <li>Define messages</li> </ol> C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage\n{\n public readonly int amount;\n}\n</code></pre> <ol> <li>Listen in a component</li> </ol> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Extensions;\nusing DxMessaging.Unity;\n\npublic class GameUI : MessageAwareComponent\n{\n // Assume this references a Player GameObject\n public UnityEngine.GameObject playerGO;\n\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n\n _ = Token.RegisterGameObjectTargeted<Heal>(playerGO, OnPlayerHealed);\n _ = Token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n }\n\n private void OnPlayerHealed(ref Heal m) => UpdateHealthBar(m.amount);\n private void OnAnyDamage(InstanceId source, TookDamage m) => ShowDamageEffect(source);\n}\n</code></pre> <ol> <li>Send a message</li> </ol> C#<pre><code>var heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGameObject);\n</code></pre>"},{"location":"getting-started/getting-started/#message-types","title":"Message Types","text":""},{"location":"getting-started/getting-started/#new-to-messaging-use-this-decision-tree","title":"New to messaging? Use this decision tree","text":"Text Only<pre><code>Is it a global announcement? (pause, settings, scene load)\n -> Use UNTARGETED\n\nAre you commanding a specific entity? (heal Player, open Chest #3)\n -> Use TARGETED\n\nIs an entity announcing something happened? (Enemy died, Chest opened)\n -> Use BROADCAST\n</code></pre>"},{"location":"getting-started/getting-started/#examples","title":"Examples","text":"C#<pre><code>// Untargeted: \"Everyone, the game paused!\"\n[DxUntargetedMessage]\npublic struct GamePaused { }\n\n// Targeted: \"Player, heal yourself by 50!\"\n[DxTargetedMessage]\npublic struct Heal { public int amount; }\n\n// Broadcast: \"I (Enemy #3) just died!\"\n[DxBroadcastMessage]\npublic struct EnemyDied { public string enemyName; }\n</code></pre> <p>Need more detail? See the Visual Guide for additional explanations, or Message Types for technical details.</p>"},{"location":"getting-started/getting-started/#common-patterns","title":"Common Patterns","text":""},{"location":"getting-started/getting-started/#example-use-cases","title":"Example use cases","text":"<ul> <li>UI reacting to gameplay - Health bar updates when player takes damage (without UI knowing about Player)</li> <li>Achievement systems - Track ALL kills across ALL enemies with ONE listener</li> <li>Cross-system coordination - Scene transitions coordinate audio, save system, and UI automatically</li> <li>Input handling - Decouple input system from player controller</li> <li>Analytics - Track all events without polluting gameplay code</li> </ul> <p>Want code examples? See Patterns for example patterns and Listening Patterns for additional techniques.</p>"},{"location":"getting-started/getting-started/#troubleshooting","title":"Troubleshooting","text":""},{"location":"getting-started/getting-started/#my-handler-isnt-being-called","title":"\"My handler isn't being called\"","text":"<p>Checklist:</p> <ol> <li>Did you call <code>base.RegisterMessageHandlers()</code> first?</li> <li>Is your component enabled in the scene?</li> <li>Are you emitting to the right target? (Check GameObject vs Component)</li> <li>Check the Inspector - does the registration show up?</li> </ol>"},{"location":"getting-started/getting-started/#my-message-is-firing-twice","title":"\"My message is firing twice\"","text":"<ul> <li>Check if you accidentally registered the same handler multiple times</li> <li>Make sure you're calling <code>base.RegisterMessageHandlers()</code> only once</li> </ul>"},{"location":"getting-started/getting-started/#i-get-a-compile-error-on-dxautoconstructor","title":"\"I get a compile error on <code>[DxAutoConstructor]</code>\"","text":"<ul> <li>Did you mark the struct as <code>partial</code>? (required for code generation)</li> <li>Example: <code>public readonly partial struct MyMessage</code></li> </ul>"},{"location":"getting-started/getting-started/#which-message-type-should-i-use","title":"\"Which message type should I use\"","text":"<ul> <li>See the decision tree in Message Types above</li> <li>When in doubt, start with <code>Broadcast</code> - it's the most flexible</li> </ul> <p>Still stuck? See Troubleshooting for the complete guide or FAQ for common questions.</p>"},{"location":"getting-started/getting-started/#next-steps","title":"Next Steps","text":"<ul> <li>Mental Model - Understand the philosophy behind DxMessaging</li> <li>Visual Guide</li> <li>Quick Reference</li> <li>Diagnostics</li> <li>Interceptors and Ordering</li> </ul>"},{"location":"getting-started/getting-started/#quick-reference","title":"Quick Reference","text":"C#<pre><code>// Listen\n_ = Token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\n_ = Token.RegisterGameObjectTargeted<Heal>(playerGO, OnPlayerHealed);\n\n// Emit (broadcasts always require a source)\nvar damage = new TookDamage(25);\ndamage.EmitGameObjectBroadcast(enemyGO); // enemyGO is the source\nvar heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGO);\n</code></pre>"},{"location":"getting-started/install/","title":"Install (Unity UPM)","text":"<p>This page helps you install DxMessaging into a Unity 2021.3+ project using the Unity Package Manager (UPM).</p>"},{"location":"getting-started/install/#quick-reference","title":"Quick Reference","text":"Method Command/URL Auto-Updates OpenUPM (Recommended) <code>openupm add com.wallstop-studios.dxmessaging</code> Yes Git URL <code>https://github.com/wallstop/DxMessaging.git</code> No NPM Scoped Registry Add registry + resolve Yes From Releases Download .unitypackage No From Source Clone/download zip No Manual (not recommended) Edit manifest.json No"},{"location":"getting-started/install/#methods","title":"Methods","text":""},{"location":"getting-started/install/#openupm-recommended","title":"OpenUPM (Recommended)","text":"<p>The easiest way to install DxMessaging is via OpenUPM.</p> <p>If you have the openupm-cli installed, run:</p> Bash<pre><code>openupm add com.wallstop-studios.dxmessaging\n</code></pre>"},{"location":"getting-started/install/#benefits-of-openupm","title":"Benefits of OpenUPM","text":"<ul> <li>Automatic update notifications in Unity Package Manager</li> <li>Easy dependency management</li> <li>Simple one-command installation</li> <li>Version history and changelogs available on the OpenUPM page</li> </ul> <p>Don't want to install the CLI? You can also add OpenUPM as a scoped registry manually.</p>"},{"location":"getting-started/install/#git-url","title":"Git URL","text":"<ul> <li>Unity Package Manager > Add package from git URL...</li> <li>Paste:</li> </ul> Text Only<pre><code>https://github.com/wallstop/DxMessaging.git\n</code></pre> <ul> <li>Click Add. Unity imports the package and its analyzers/generators.</li> </ul>"},{"location":"getting-started/install/#npm-scoped-registry","title":"NPM Scoped Registry","text":"<ol> <li>Open Unity Package Manager</li> <li>(Optional) Enable Pre-release packages to get the latest, cutting-edge builds</li> <li>Open the Advanced Package Settings</li> <li>Add an entry for a new \"Scoped Registry\"</li> <li>Name: <code>NPM</code></li> <li>URL: <code>https://registry.npmjs.org</code></li> <li>Scope(s): <code>com.wallstop-studios.dxmessaging</code></li> <li>Resolve the latest <code>DxMessaging</code></li> </ol> <p>Unity will notify you of version updates when using scoped registries.</p>"},{"location":"getting-started/install/#from-releases","title":"From Releases","text":"<p>Check out the latest Releases to grab the Unity Package and import to your project.</p>"},{"location":"getting-started/install/#from-source","title":"From Source","text":"<p>Grab a copy of this repo (either <code>git clone</code> this repo or download a zip of the source) and copy the contents to your project's <code>Assets</code> directory.</p>"},{"location":"getting-started/install/#manual---manifestjson-not-recommended","title":"Manual - Manifest.json (not recommended)","text":"<ul> <li>Open your project's <code>Packages/manifest.json</code> and add:</li> </ul> JSON<pre><code>{\n \"dependencies\": {\n \"com.wallstop-studios.dxmessaging\": \"https://github.com/wallstop/DxMessaging.git\"\n }\n}\n</code></pre>"},{"location":"getting-started/install/#minimum-requirements","title":"Minimum Requirements","text":"<ul> <li>Unity 2021.3+ (LTS recommended). See Compatibility for Unity \u00d7 Render Pipeline support (Built\u2011In, URP, HDRP).</li> </ul>"},{"location":"getting-started/install/#after-installation","title":"After Installation","text":"<ul> <li>In your project, create a GameObject and add <code>MessagingComponent</code> to start sending/receiving.</li> <li>Optional: enable diagnostics in Editor from the MessagingComponent inspector to see live emissions.</li> <li>Jump to Quick Start</li> </ul>"},{"location":"getting-started/overview/","title":"Overview","text":"<p>Back to Index | Getting Started | Quick Start | Visual Guide</p> <p>DxMessaging is a type-safe messaging system for Unity that addresses three common pain points of traditional event systems:</p> <ol> <li>Memory leaks from forgotten unsubscribes -> Automatic lifecycle management</li> <li>Tight coupling creating refactoring nightmares -> Full decoupling with no direct references</li> <li>Debugging black holes (\"what fired when?\") -> Built-in Inspector diagnostics</li> </ol>"},{"location":"getting-started/overview/#what-problems-does-it-solve","title":"What Problems Does It Solve?","text":""},{"location":"getting-started/overview/#for-beginners-im-calling-methods-manually-everywhere","title":"For Beginners: \"I'm calling methods manually everywhere\"","text":""},{"location":"getting-started/overview/#traditional-approaches-often-look-like-this","title":"Traditional approaches often look like this","text":"C#<pre><code>public class Player : MonoBehaviour {\n public HealthBar healthBar;\n public AudioManager audio;\n public AchievementSystem achievements;\n\n void TakeDamage(int amount) {\n health -= amount;\n healthBar.UpdateHealth(health); // Manual call\n audio.PlayDamageSound(); // Manual call\n achievements.CheckDamage(amount); // Manual call\n // Add analytics? More manual calls...\n }\n}\n</code></pre> <p>Every new system = another SerializeField + another manual call. This approach requires frequent updates as systems are added.</p>"},{"location":"getting-started/overview/#dxmessaging-fixes-this","title":"DxMessaging fixes this","text":"C#<pre><code>void TakeDamage(int amount) {\n health -= amount;\n var damage = new TookDamage(amount);\n damage.EmitComponentBroadcast(this);\n // Subscribed systems react to this message.\n}\n</code></pre>"},{"location":"getting-started/overview/#for-intermediate-devs-i-use-c-events-but-they-leak","title":"For Intermediate Devs: \"I use C# events but they leak\"","text":""},{"location":"getting-started/overview/#you-know-this-pain","title":"You know this pain","text":"C#<pre><code>void OnEnable() { GameManager.OnScoreChanged += UpdateUI; }\nvoid OnDisable() { /* Missing this results in a memory leak: */ }\n</code></pre> <p>DxMessaging manages subscription lifecycle automatically, eliminating the need for manual unsubscribe - cleanup occurs when components are destroyed.</p>"},{"location":"getting-started/overview/#for-advanced-devs-i-need-observability-and-control","title":"For Advanced Devs: \"I need observability and control\"","text":"<ul> <li>See message history in Inspector (timestamps, payloads, call counts)</li> <li>Priority-based execution (deterministic ordering)</li> <li>Interceptors (validate/normalize before handlers)</li> <li>Global observers (track ALL instances of a message type)</li> <li>Local bus islands (isolated testing, zero global state)</li> </ul>"},{"location":"getting-started/overview/#what-it-solves-technical","title":"What It Solves (Technical)","text":"<ul> <li>Decoupling without references - producers/consumers never know about each other</li> <li>Predictable lifecycle - explicit tokens tied to Unity component lifecycles</li> <li>Performance - struct messages passed by-ref, designed to minimize allocations and boxing</li> <li>Observability - interceptors, post-processors, diagnostics, registration logs</li> <li>Scalable taxonomy - three message types (Untargeted/Targeted/Broadcast) cover most common messaging patterns</li> </ul>"},{"location":"getting-started/overview/#when-to-consider-dxmessaging","title":"When to Consider DxMessaging","text":""},{"location":"getting-started/overview/#use-it-when","title":"Use it when","text":"<ul> <li>You have 3+ systems that need to communicate</li> <li>You've debugged memory leaks from forgotten unsubscribes</li> <li>You're tired of UI depending on 15 different gameplay systems</li> <li>You want to see \"what fired when\" without setting 50 breakpoints</li> <li>You're building for the long term (months/years, not days)</li> </ul>"},{"location":"getting-started/overview/#skip-it-when","title":"Skip it when","text":"<ul> <li>Game jam prototype (<1 week)</li> <li>Tiny project (<1000 lines)</li> <li>Single-system communication (call the method directly)</li> <li>You need synchronous return values (DxMessaging is fire-and-forget)</li> </ul> <p>Core ideas</p> <ul> <li>Message categories</li> <li>Untargeted: Global notifications (e.g., settings changed).</li> <li>Targeted: Sent to one specific target (e.g., Heal(10) to Player).</li> <li>Broadcast: Emitted from a source; anyone can observe (e.g., TookDamage from Enemy).</li> <li>Ordering: Lower priority runs earlier; same priority uses registration order.</li> <li>Pipeline: Interceptors -> Handlers -> Post-Processors, with diagnostics optionally enabled.</li> <li>Unity integration: <code>MessagingComponent</code> and <code>MessageAwareComponent</code> manage subscription lifecycles automatically.</li> </ul>"},{"location":"getting-started/overview/#key-features","title":"Key Features","text":""},{"location":"getting-started/overview/#global-observers-observing-all-instances","title":"Global Observers: Observing All Instances","text":""},{"location":"getting-started/overview/#traditional-event-systems-force-you-to-do-this","title":"Traditional event systems force you to do this","text":"C#<pre><code>// Subscribe to each entity type separately\nPlayerEvents.OnDamaged += TrackPlayerDamage;\nEnemyEvents.OnDamaged += TrackEnemyDamage;\nNPCEvents.OnDamaged += TrackNPCDamage;\n// Add a new entity type? More subscriptions...\n</code></pre>"},{"location":"getting-started/overview/#dxmessaging-lets-you-do-this","title":"DxMessaging lets you do this","text":"C#<pre><code>// Subscribe ONCE to ALL damage, regardless of source\n_ = Token.RegisterBroadcastWithoutSource<TookDamage>(\n (InstanceId source, TookDamage msg) => {\n Analytics.Log($\"{source} took {msg.amount} damage\");\n }\n);\n</code></pre> <p>Example use cases: Achievement systems, combat logs, and analytics that work with any entity - including ones added later.</p>"},{"location":"getting-started/overview/#other-features","title":"Other Features","text":"<ul> <li>Priority-based ordering - control execution flow explicitly</li> <li>Interceptor pipeline - validate/normalize messages BEFORE handlers run (one validation, all handlers protected)</li> <li>Local bus islands - isolated testing with zero global state contamination</li> <li>Low-allocation design - struct messages passed by-ref, minimizes boxing and GC pressure</li> <li>Auto-constructor generation - <code>[DxAutoConstructor]</code> eliminates boilerplate while keeping type safety</li> <li>Unity-first helpers - <code>EmitGameObjectTargeted()</code>, <code>EmitComponentBroadcast()</code> feel natural</li> <li>Inspector diagnostics - see message history, registrations, call counts in real-time</li> </ul>"},{"location":"getting-started/overview/#related-documentation","title":"Related Documentation","text":""},{"location":"getting-started/overview/#start-here","title":"Start Here","text":"<ul> <li>-> Mental Model (10 min) - Philosophy and first principles</li> <li>-> Visual Guide (5 min) - Beginner-friendly introduction</li> <li>-> Getting Started (10 min) - Complete guide</li> <li>-> Quick Start (5 min) - Working example</li> </ul>"},{"location":"getting-started/overview/#go-deeper","title":"Go Deeper","text":"<ul> <li>-> Message Types - When to use Untargeted/Targeted/Broadcast</li> <li>-> Comparisons - DxMessaging vs alternatives</li> <li>-> Design & Architecture - How it works</li> </ul>"},{"location":"getting-started/overview/#install--setup","title":"Install & Setup","text":"<ul> <li>-> Install - Installation guide</li> <li>-> Compatibility - Unity versions</li> </ul>"},{"location":"getting-started/quick-start/","title":"Quick Start - Your First Message in 5 Minutes","text":"<p>Back to Index | Getting Started | Visual Guide | Samples</p> <p>Goal: Get a working message system in 5 minutes. Copy, paste, run.</p> <p>Stuck? -> Troubleshooting | FAQ</p>"},{"location":"getting-started/quick-start/#step-0-install-30-seconds","title":"Step 0: Install (30 seconds)","text":"<p>Unity Package Manager -> Add package from git URL:</p> Text Only<pre><code>https://github.com/wallstop/DxMessaging.git\n</code></pre> <p>Requirements: Unity 2021.3+ | .NET Standard 2.1 | All render pipelines supported</p>"},{"location":"getting-started/quick-start/#your-first-message-3-steps","title":"Your First Message (3 Steps)","text":""},{"location":"getting-started/quick-start/#step-1-define-messages","title":"Step 1: Define messages","text":"C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\n\n// Recommended: attributes + auto constructor (beginner-friendly)\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct WorldRegenerated\n{\n public readonly int seed;\n}\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct VideoSettingsChanged\n{\n public readonly int width;\n public readonly int height;\n}\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage\n{\n public readonly int amount;\n}\n\n// Performance option: keep [DxTargetedMessage] on a readonly struct to stay zero-boxing friendly, and drop [DxAutoConstructor] only if you need custom constructor logic.\n\n// Optional parameters with custom defaults\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct HealAdvanced\n{\n public readonly int amount;\n [DxOptionalParameter(true)] // Custom default value\n public readonly bool showEffect;\n [DxOptionalParameter(Expression = \"Color.green\")] // Expression for any type\n public readonly Color effectColor;\n}\n</code></pre>"},{"location":"getting-started/quick-start/#step-2-add-a-messaging-component","title":"Step 2: Add a messaging component","text":"C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core.Messages;\nusing UnityEngine;\n\npublic sealed class HealthUI : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<WorldRegenerated>(OnWorld);\n _ = Token.RegisterComponentBroadcast<TookDamage>(this, OnDamage);\n }\n\n private void OnWorld(ref WorldRegenerated m) { /* update UI */ }\n private void OnDamage(ref TookDamage m) { /* flash damage */ }\n}\n</code></pre>"},{"location":"getting-started/quick-start/#step-3-send-messages","title":"Step 3: Send messages","text":"C#<pre><code>using DxMessaging.Core.Extensions; // Emit helpers\nusing UnityEngine;\n\n// Untargeted (global)\nvar world = new WorldRegenerated(42);\nworld.Emit();\n\n// Targeted (to a specific component or GameObject)\nvar heal = new Heal(10);\nheal.EmitGameObjectTargeted(gameObject); // no InstanceId cast needed\n\n// Broadcast (from a specific source)\nvar hit = new TookDamage(5);\nhit.EmitGameObjectBroadcast(gameObject); // no InstanceId cast needed\n\n// String convenience (global)\n\"Saved\".Emit();\n</code></pre>"},{"location":"getting-started/quick-start/#summary","title":"Summary","text":"<p>You have:</p> <ol> <li>Defined 4 message types</li> <li>Created a component that listens</li> <li>Sent messages from anywhere</li> </ol> <p>Registration cleanup is automatic. Messages are type-safe.</p>"},{"location":"getting-started/quick-start/#what-you-built","title":"What You Built","text":"<ul> <li>Untargeted messages (<code>WorldRegenerated</code>, <code>VideoSettingsChanged</code>) -> Global announcements anyone can hear</li> <li>Targeted messages (<code>Heal</code>) -> Commands to a specific object</li> <li>Broadcast messages (<code>TookDamage</code>) -> Events from a source that others observe</li> </ul>"},{"location":"getting-started/quick-start/#next-steps","title":"Next Steps","text":"<ul> <li>Understand What You Did</li> <li>-> Mental Model (10 min) - Philosophy and first principles</li> <li>-> Getting Started Guide (10 min) - Full explanation with examples</li> <li>-> Visual Guide (5 min) - Pictures and analogies</li> <li>Try Real Examples</li> <li>-> Mini Combat sample - Working combat example</li> <li>-> UI Buttons + Inspector sample - See diagnostics in action</li> <li>Go Deeper</li> <li>-> Message Types (10 min) - When to use which type</li> <li>-> Common Patterns (15 min) - Real-world solutions</li> <li>-> Interceptors & Ordering (10 min) - Advanced control</li> <li>Reference</li> <li>-> Quick Reference - Cheat sheet</li> <li>-> API Reference - Complete API</li> <li>-> Troubleshooting - Fix common issues</li> </ul>"},{"location":"getting-started/quick-start/#quick-tips","title":"Quick Tips","text":""},{"location":"getting-started/quick-start/#dos","title":"Do's","text":"<ul> <li>Use <code>MessageAwareComponent</code> for Unity components (automatic lifecycle)</li> <li>Store struct in variable before emitting: <code>var msg = new Heal(10); msg.Emit();</code></li> <li>Call <code>base.RegisterMessageHandlers()</code> when overriding</li> </ul>"},{"location":"getting-started/quick-start/#donts","title":"Don'ts","text":"<ul> <li>Don't emit from temporaries: <code>new Heal(10).Emit()</code> won't compile (struct emit methods require <code>ref this</code>)</li> <li>Don't use Untargeted for commands to one object (use Targeted instead)</li> <li>Don't forget <code>using DxMessaging.Core.Extensions;</code> for <code>Emit*</code> methods</li> </ul>"},{"location":"getting-started/visual-guide/","title":"DxMessaging Visual Guide for Beginners","text":"<p>If you're brand new to messaging systems, this visual guide will help you understand DxMessaging in minutes.</p>"},{"location":"getting-started/visual-guide/#what-problem-does-it-solve","title":"What Problem Does It Solve","text":""},{"location":"getting-started/visual-guide/#the-old-way-spaghetti-code","title":"The Old Way (Spaghetti Code)","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart LR\n Player[Player]\n Enemy[Enemy]\n Inventory[Inventory]\n UI[UI]\n Audio[Audio]\n\n Player -->|direct ref| UI\n Player -->|direct ref| Audio\n Enemy -->|direct ref| UI\n Enemy -->|direct ref| Audio\n Inventory -->|direct ref| Audio\n\n classDef danger stroke-width:2px\n class Player,Enemy,Inventory danger\n classDef success stroke-width:2px\n class UI,Audio success</code></pre>"},{"location":"getting-started/visual-guide/#problems","title":"Problems","text":"<ul> <li>Everyone needs to know everyone else</li> <li>Hard to add/remove systems</li> <li>Memory leaks from forgotten unsubscribes</li> </ul>"},{"location":"getting-started/visual-guide/#the-dxmessaging-way-clean-separation","title":"The DxMessaging Way (Clean Separation)","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TB\n Player[Player]\n Enemy[Enemy]\n Inventory[Inventory]\n Bus((Message<br/>Bus))\n UI[UI]\n Audio[Audio]\n Analytics[Analytics]\n\n Player -->|message| Bus\n Enemy -->|message| Bus\n Inventory -->|message| Bus\n\n Bus -->|notify| UI\n Bus -->|notify| Audio\n Bus -->|notify| Analytics\n\n classDef primary stroke-width:2px\n class Player,Enemy,Inventory primary\n classDef warning stroke-width:3px\n class Bus warning\n classDef success stroke-width:2px\n class UI,Audio,Analytics success</code></pre>"},{"location":"getting-started/visual-guide/#benefits","title":"Benefits","text":"<ul> <li>Nobody knows about anyone else</li> <li>Easy to add/remove systems</li> <li>Automatic cleanup (prevents common leaks)</li> </ul>"},{"location":"getting-started/visual-guide/#the-three-message-types-simple","title":"The Three Message Types (Simple!)","text":"<p>Think of messages like different kinds of mail:</p>"},{"location":"getting-started/visual-guide/#1-untargeted-announcement-to-everyone","title":"1. Untargeted (Announcement to Everyone)","text":"<p>Like a megaphone announcement in a stadium - everyone hears it.</p> C#<pre><code>// Define the announcement\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct GamePaused { }\n\n// Anyone can announce\nvar msg = new GamePaused();\nmsg.Emit();\n\n// Anyone can listen\n_ = token.RegisterUntargeted<GamePaused>(OnPause);\n</code></pre>"},{"location":"getting-started/visual-guide/#real-world-uses","title":"Real-world uses","text":"<ul> <li>\"Game paused!\"</li> <li>\"Settings changed!\"</li> <li>\"Level loaded!\"</li> </ul>"},{"location":"getting-started/visual-guide/#2-targeted-letter-to-one-person","title":"2. Targeted (Letter to One Person)","text":"<p>Like mailing a letter to a specific address - only that recipient gets it.</p> C#<pre><code>// Define the letter\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n// Send to specific person\nvar heal = new Heal(50);\nheal.EmitGameObjectTargeted(playerObject);\n\n// Only the player listens\n_ = token.RegisterComponentTargeted<Heal>(this, OnHeal);\n</code></pre>"},{"location":"getting-started/visual-guide/#real-world-uses_1","title":"Real-world uses","text":"<ul> <li>\"Player, heal yourself!\"</li> <li>\"Enemy #3, take damage!\"</li> <li>\"Button, update your text!\"</li> </ul>"},{"location":"getting-started/visual-guide/#3-broadcast-news-from-one-source","title":"3. Broadcast (News from One Source)","text":"<p>Like a news broadcast - comes from one source, anyone can tune in.</p> C#<pre><code>// Define the news\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\n// Broadcast from enemy\nvar dmg = new TookDamage(25);\ndmg.EmitGameObjectBroadcast(enemyObject);\n\n// UI can listen to specific enemy\n_ = token.RegisterGameObjectBroadcast<TookDamage>(enemyObject, OnThisEnemy);\n\n// OR achievement system can listen to ALL enemies\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyEnemy);\n</code></pre>"},{"location":"getting-started/visual-guide/#real-world-uses_2","title":"Real-world uses","text":"<ul> <li>\"I (player) took damage!\"</li> <li>\"I (enemy) died!\"</li> <li>\"I (chest) was opened!\"</li> </ul>"},{"location":"getting-started/visual-guide/#the-message-journey-step-by-step","title":"The Message Journey (Step by Step)","text":"<p>When you send a message, here's what happens:</p> <pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant You as Your Code\n participant Msg as Message\n participant Int as Interceptors<br/>(Optional)\n participant H0 as Handler<br/>priority: 0\n participant H5 as Handler<br/>priority: 5\n participant H10 as Handler<br/>priority: 10\n participant PP as Post-Processors<br/>(Optional)\n\n Note over You: 1. Create message\n You->>Msg: var heal = new Heal(10);\n\n Note over You,Msg: 2. Emit message\n You->>Msg: heal.EmitGameObjectTargeted(player);\n\n Note over Int: 3. Validate & Normalize\n Msg->>Int: Check message\n Int->>Int: Is valid? (>0)<br/>Clamp if needed (<999)\n alt Invalid message\n Int--xMsg: Cancel\n else Valid message\n Int->>H0: Continue\n end\n\n Note over H0,H10: 4. Execute handlers (by priority)\n H0->>H0: SaveSystem runs first\n H0->>H5: Next priority\n H5->>H5: AudioSystem runs\n H5->>H10: Next priority\n H10->>H10: UISystem runs last\n\n Note over PP: 5. Analytics & Logging\n H10->>PP: All handlers complete\n PP->>PP: Analytics.Track(...)<br/>Debug.Log(...)</code></pre>"},{"location":"getting-started/visual-guide/#key-points","title":"Key points","text":"<ul> <li>Step 1-2: You create and emit the message</li> <li>Step 3 (Optional): Interceptors can validate, modify, or cancel</li> <li>Step 4: Handlers run in priority order (lower number = earlier)</li> <li>Step 5 (Optional): Post-processors run after everything (suitable for analytics)</li> </ul>"},{"location":"getting-started/visual-guide/#your-first-message-3-easy-steps","title":"Your First Message (3 Easy Steps)","text":""},{"location":"getting-started/visual-guide/#step-1-define-it","title":"Step 1: Define It","text":"C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxTargetedMessage] // <- What kind of message?\n[DxAutoConstructor] // <- Auto-make a constructor\npublic readonly partial struct Heal {\n public readonly int amount;\n}\n</code></pre>"},{"location":"getting-started/visual-guide/#what-are-those-dxsomething-tags","title":"What are those <code>[DxSomething]</code> tags?","text":"<p>These are attributes that work with C# source generators to produce boilerplate code at compile time:</p> <ul> <li><code>[DxTargetedMessage]</code> - Marks this struct as a targeted message and generates the required emit methods</li> <li><code>[DxAutoConstructor]</code> - Generates a constructor that initializes all fields</li> </ul> <p>For example, <code>[DxAutoConstructor]</code> generates this constructor automatically:</p> C#<pre><code>public Heal(int amount) { this.amount = amount; }\n</code></pre> <p>Why <code>partial</code>? The <code>partial</code> keyword allows the source generator to add the generated code to your type in a separate file during compilation.</p> <p>Want to learn more? See Helpers & Source Generation for the full explanation!</p>"},{"location":"getting-started/visual-guide/#step-2-listen-for-it","title":"Step 2: Listen for It","text":"C#<pre><code>using DxMessaging.Unity;\n\npublic class Player : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n // \"When someone sends Heal to ME, call OnHeal\"\n _ = Token.RegisterComponentTargeted<Heal>(this, OnHeal);\n }\n\n void OnHeal(ref Heal msg) {\n health += msg.amount;\n Debug.Log($\"Healed {msg.amount}!\");\n }\n}\n</code></pre> <p>Automatic: <code>MessageAwareComponent</code> handles all the lifecycle automatically.</p> <ul> <li>Creates registration in <code>Awake()</code></li> <li>Activates in <code>OnEnable()</code></li> <li>Deactivates in <code>OnDisable()</code></li> <li>Cleans up in <code>OnDestroy()</code></li> </ul>"},{"location":"getting-started/visual-guide/#step-3-send-it","title":"Step 3: Send It","text":"C#<pre><code>// From anywhere in your code:\nvar healMsg = new Heal(50);\nhealMsg.EmitComponentTargeted(playerComponent);\n\n// Player will receive this message.\n</code></pre>"},{"location":"getting-started/visual-guide/#common-patterns-visualized","title":"Common Patterns Visualized","text":""},{"location":"getting-started/visual-guide/#pattern-scene-transition","title":"Pattern: Scene Transition","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant SM as SceneManager\n participant Bus as Message Bus\n participant Audio as AudioSystem\n participant Save as SaveSystem\n\n Note over SM: Scene is changing\n SM->>Bus: SceneChanged(sceneIndex: 2)\n\n Note over Bus: Message broadcast to all listeners\n\n Bus->>Audio: SceneChanged received\n Audio->>Audio: FadeOutMusic()\n\n Bus->>Save: SceneChanged received\n Save->>Save: SaveGame()\n\n Note over Audio,Save: All independent,<br/>no coupling</code></pre> <p>Why this works: AudioSystem and SaveSystem don't know about SceneManager or each other. They just listen for <code>SceneChanged</code> messages and react independently.</p> <p>Code:</p> C#<pre><code>// Define\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneChanged { public readonly int sceneIndex; }\n\n// Anyone can send\nvar msg = new SceneChanged(2);\nmsg.Emit();\n\n// Many can listen independently\n_ = audioToken.RegisterUntargeted<SceneChanged>(OnScene);\n_ = saveToken.RegisterUntargeted<SceneChanged>(OnScene);\n</code></pre>"},{"location":"getting-started/visual-guide/#pattern-player-input---action","title":"Pattern: Player Input -> Action","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant Input as InputSystem\n participant Bus as Message Bus\n participant Player as Player\n\n Note over Input: User presses Space\n Input->>Bus: Jump(force: 10f)<br/>[Targeted to Player]\n\n Bus->>Player: Jump message received\n Player->>Player: ApplyForce()<br/>rb.AddForce(...)\n\n Note over Input,Player: Decoupled!<br/>Input doesn't reference Player</code></pre> <p>Why this works: InputSystem doesn't need a reference to Player. It just sends a <code>Jump</code> message targeted at the player, and the player responds.</p> <p>Code:</p> C#<pre><code>// Input system (doesn't know about Player!)\nvoid Update() {\n if (Input.GetKeyDown(KeyCode.Space)) {\n var jump = new Jump(10f);\n jump.EmitComponentTargeted(playerController);\n }\n}\n\n// Player (doesn't know about Input system!)\n_ = token.RegisterComponentTargeted<Jump>(this, OnJump);\nvoid OnJump(ref Jump msg) {\n rb.AddForce(Vector3.up * msg.force, ForceMode.Impulse);\n}\n</code></pre>"},{"location":"getting-started/visual-guide/#pattern-achievement-tracking","title":"Pattern: Achievement Tracking","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nsequenceDiagram\n participant E as Enemy\n participant P as Player\n participant C as Chest\n participant Bus as Message Bus\n participant Ach as Achievement System\n\n Note over Ach: Listens to ALL messages\n\n E->>Bus: EnemyKilled\n Bus->>Ach: CheckProgress()<br/>UnlockIfReady()\n\n P->>Bus: LevelCompleted\n Bus->>Ach: CheckProgress()<br/>UnlockIfReady()\n\n C->>Bus: ChestOpened\n Bus->>Ach: CheckProgress()<br/>UnlockIfReady()\n\n Note over Ach: Sees EVERYTHING<br/>without coupling!</code></pre> <p>Why this works: Achievement System uses <code>RegisterGlobalAcceptAll()</code> to observe every message type, tracking progress across the entire game without any system knowing about it.</p> <p>Code:</p> C#<pre><code>public class AchievementSystem : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n // Listen to EVERYTHING\n _ = Token.RegisterGlobalAcceptAll(\n (ref IUntargetedMessage m) => Check(m),\n (ref InstanceId t, ref ITargetedMessage m) => Check(m),\n (ref InstanceId s, ref IBroadcastMessage m) => Check(m)\n );\n }\n}\n</code></pre>"},{"location":"getting-started/visual-guide/#when-to-use-which-message-type","title":"When to Use Which Message Type","text":""},{"location":"getting-started/visual-guide/#use-untargeted-when","title":"Use Untargeted When","text":"<ul> <li>Global game state changes (pause, settings, scene load)</li> <li>System-wide announcements</li> <li>Configuration updates</li> </ul>"},{"location":"getting-started/visual-guide/#use-targeted-when","title":"Use Targeted When","text":"<ul> <li>Commanding a specific object (\"You, do this!\")</li> <li>UI updates for specific elements</li> <li>Direct communication (A -> B)</li> </ul>"},{"location":"getting-started/visual-guide/#use-broadcast-when","title":"Use Broadcast When","text":"<ul> <li>Events others should know about (\"I did this!\")</li> <li>Analytics tracking</li> <li>Achievement triggers</li> <li>Notifications from specific sources</li> </ul>"},{"location":"getting-started/visual-guide/#mental-model-restaurant-analogy","title":"Mental Model: Restaurant Analogy","text":"<p>Think of DxMessaging like a restaurant:</p>"},{"location":"getting-started/visual-guide/#untargeted--restaurant-announcement","title":"Untargeted = Restaurant Announcement","text":"<p>\"Attention all customers: We're closing in 10 minutes!\"</p> <p>-> Everyone hears it</p>"},{"location":"getting-started/visual-guide/#targeted--waiter-delivering-food","title":"Targeted = Waiter Delivering Food","text":"<p>\"Order for table 5: Here's your burger\"</p> <p>-> Only table 5 gets it</p>"},{"location":"getting-started/visual-guide/#broadcast--customer-calling-waiter","title":"Broadcast = Customer Calling Waiter","text":"<p>\"Excuse me, I need a refill!\" (from table 3)</p> <p>-> Comes from table 3</p> <p>-> Any available waiter can respond</p> <p>-> Manager might track it for statistics</p>"},{"location":"getting-started/visual-guide/#debugging-visualized","title":"Debugging Visualized","text":"<p>DxMessaging has built-in Inspector support!</p>"},{"location":"getting-started/visual-guide/#messagingcomponent-inspector","title":"MessagingComponent Inspector","text":""},{"location":"getting-started/visual-guide/#message-history-last-10","title":"Message History (last 10)","text":"<ul> <li><code>12:34:05 - Heal -> Player (50)</code></li> <li><code>12:34:03 - Jump -> Player</code></li> <li><code>12:34:01 - GamePaused (global)</code></li> </ul>"},{"location":"getting-started/visual-guide/#registrations","title":"Registrations","text":"<ul> <li>Heal (priority: 0, 5 calls)</li> <li>Jump (priority: 0, 2 calls)</li> <li>TookDamage (priority: 10)</li> </ul>"},{"location":"getting-started/visual-guide/#performance-at-a-glance","title":"Performance at a Glance","text":"Metric Traditional C# Events DxMessaging Speed (baseline) (~10ns slower, negligible) Memory Can leak! Automatic cleanup (struct messages) Coupling Tight coupling Zero coupling <p>Bottom line: Slightly slower than raw events, but:</p> <ul> <li>Prevents common memory leaks</li> <li>Zero coupling</li> <li>Full observability</li> <li>Predictable ordering</li> </ul>"},{"location":"getting-started/visual-guide/#learning-path","title":"Learning Path","text":"<pre><code>%%{init: {'theme': 'dark'}}%%\nflowchart TD\n Start[START HERE<br/>Read this Visual Guide<br/>5 min]\n Start --> Step2[Try Quick Start example<br/>5 min<br/>Define -> Listen -> Send]\n Step2 --> Step3[Import Mini Combat sample<br/>10 min<br/>See it in action!]\n Step3 --> Step4[Read Common Patterns<br/>15 min<br/>Real-world solutions]\n Step4 --> Step5[Build your first feature!<br/>30 min<br/>You're ready!]\n\n classDef primary stroke-width:3px\n class Start primary\n classDef success stroke-width:2px\n class Step5 success\n classDef secondary stroke-width:2px\n class Step2,Step3,Step4 secondary</code></pre>"},{"location":"getting-started/visual-guide/#common-beginner-questions","title":"Common Beginner Questions","text":""},{"location":"getting-started/visual-guide/#do-i-always-need-messageawarecomponent","title":"\"Do I always need MessageAwareComponent?\"","text":"<p>For Unity: Yes! It's the easiest way. Think of it like <code>MonoBehaviour</code> - you inherit from it and it handles all the messy lifecycle stuff automatically.</p> <p>For pure C#: No, you can use <code>MessageRegistrationToken</code> directly if you're not in Unity.</p> <p>Bottom line: If you're in Unity, use <code>MessageAwareComponent</code>. It handles subscription lifecycle automatically, which can reduce debugging related to memory leaks.</p>"},{"location":"getting-started/visual-guide/#can-i-send-a-message-to-multiple-targets","title":"\"Can I send a message to multiple targets?\"","text":"<p>No - Targeted messages go to ONE specific entity (like mailing a letter to one address).</p>"},{"location":"getting-started/visual-guide/#instead-use","title":"Instead, use","text":"<ul> <li>Untargeted if literally everyone should hear it (like a megaphone announcement)</li> <li>Broadcast if it's from one source and many can observe (like a news broadcast)</li> </ul>"},{"location":"getting-started/visual-guide/#example","title":"Example","text":"C#<pre><code>// DON'T: Try to target multiple entities\nmsg.EmitComponentTargeted(player1);\nmsg.EmitComponentTargeted(player2); // Feels wrong, right?\n\n// DO: Use broadcast so everyone can listen\nmsg.EmitGameObjectBroadcast(enemy); // Now anyone can observe this enemy\n</code></pre>"},{"location":"getting-started/visual-guide/#what-if-i-forget-to-unsubscribe","title":"\"What if I forget to unsubscribe?\"","text":""},{"location":"getting-started/visual-guide/#the-system-handles-cleanup-automatically","title":"The system handles cleanup automatically","text":"<p>When your component is destroyed, DxMessaging cleans up registrations for you. No <code>OnDestroy()</code> needed. This reduces the likelihood of common memory leak patterns.</p>"},{"location":"getting-started/visual-guide/#old-way-easy-to-forget","title":"Old way (easy to forget)","text":"C#<pre><code>void OnEnable() { GameManager.OnScoreChanged += Update; }\nvoid OnDisable() { GameManager.OnScoreChanged -= Update; } // Forgot this? LEAK!\n</code></pre>"},{"location":"getting-started/visual-guide/#dxmessaging-way-automatic-management","title":"DxMessaging way (automatic management)","text":"C#<pre><code>protected override void RegisterMessageHandlers() {\n _ = Token.RegisterUntargeted<ScoreChanged>(Update);\n}\n// Automatic cleanup when component is destroyed.\n</code></pre>"},{"location":"getting-started/visual-guide/#is-it-slower-than-regular-events","title":"\"Is it slower than regular events?\"","text":"<p>Barely (~10ns per handler = 0.00001 milliseconds).</p>"},{"location":"getting-started/visual-guide/#benchmark-comparison","title":"Benchmark comparison","text":"<ul> <li>Regular C# event: ~50ns</li> <li>DxMessaging: ~60ns</li> <li>Difference: ~10ns per invocation</li> </ul> <p>You get automatic lifecycle, automatic cleanup, full observability, and predictable ordering for approximately 20% overhead compared to direct method calls.</p>"},{"location":"getting-started/visual-guide/#can-i-cancel-a-message","title":"\"Can I cancel a message?\"","text":""},{"location":"getting-started/visual-guide/#yes-thats-what-interceptors-are-for","title":"Yes! That's what interceptors are for","text":"C#<pre><code>// Cancel invalid damage\n_ = token.RegisterBroadcastInterceptor<TookDamage>(\n (ref InstanceId source, ref TookDamage msg) => {\n if (msg.amount <= 0) return false; // Cancel invalid damage\n if (IsInvincible(source)) return false; // Cancel during invincibility\n return true; // Allow\n }\n);\n</code></pre>"},{"location":"getting-started/visual-guide/#real-world-uses_3","title":"Real-world uses","text":"<ul> <li>Block input during cutscenes</li> <li>Cancel damage when invincible</li> <li>Prevent cheating (clamp values)</li> <li>Enforce game rules globally</li> </ul>"},{"location":"getting-started/visual-guide/#can-i-see-what-messages-are-firing","title":"\"Can I see what messages are firing?\"","text":""},{"location":"getting-started/visual-guide/#yes-open-any-component-in-the-inspector-and-scroll-down","title":"Yes! Open any component in the Inspector and scroll down","text":"<p>You'll see:</p> <ul> <li>Message history (last 50 messages with timestamps)</li> <li>Active registrations (what you're listening to)</li> <li>Call counts (how many times each handler ran)</li> </ul> <p>No more guessing. You can literally see your event flow in real-time.</p>"},{"location":"getting-started/visual-guide/#quick-checklist-am-i-doing-it-right","title":"Quick Checklist: Am I Doing It Right","text":"<ul> <li> Using <code>MessageAwareComponent</code> for Unity components?</li> <li> Defining messages as <code>readonly struct</code>?</li> <li> Using <code>[DxAutoConstructor]</code> to avoid boilerplate?</li> <li> Storing struct in variable before emitting?</li> <li> Choosing the right message type (Untargeted/Targeted/Broadcast)?</li> <li> Using GameObject/Component emit helpers?</li> </ul> <p>If you checked all these, you are following best practices.</p>"},{"location":"getting-started/visual-guide/#next-steps","title":"Next Steps","text":"<p>Ready to dive deeper?</p> <ol> <li>Mental Model - Understand the philosophy</li> <li>Getting Started Guide - Full guide with more details</li> <li>Common Patterns - Real-world examples</li> <li>Message Types - Deep dive into when to use what</li> <li>Diagnostics - Master the Inspector tools</li> </ol> <p>Summary: DxMessaging provides a structured approach to inter-component communication. You define the message, specify recipients, and the system handles delivery.</p>"},{"location":"guides/advanced/","title":"Advanced Usage: Lifecycles, Safety, and Manual Control","text":"<p>Short intro</p> <p>This page covers advanced patterns: manual lifetimes, token control, Unity integration switches, string message defaults, and safety tips. If you\u2019re new, start with Quick Start, then return here as you scale up.</p> <p>Table of contents</p> <ul> <li>Lifecycles and tokens</li> <li>Manual enable/disable and UnregisterAll</li> <li>Unity integration knobs: MessageAwareComponent and MessagingComponent</li> <li>String messages opt\u2011in/out</li> <li>Local bus islands (subsystems/tests)</li> <li>Safety and troubleshooting</li> <li>Side\u2011by\u2011side patterns: before vs after</li> <li>More advanced use cases</li> </ul> <p>Lifecycles and tokens</p> <ul> <li>Token creation</li> <li>MessageAwareComponent: created for you in Awake.</li> <li>Manual: <code>var token = messagingComponent.Create(this);</code></li> <li>Enable/Disable</li> <li>Call <code>token.Enable()</code> in <code>OnEnable</code> and <code>token.Disable()</code> in <code>OnDisable</code>.</li> <li>Idempotent; safe to call multiple times.</li> <li>Clearing</li> <li><code>token.UnregisterAll()</code> disables and clears staged registrations.</li> <li><code>token.RemoveRegistration(handle)</code> removes a single registration.</li> <li>Diagnostics</li> <li><code>token.DiagnosticMode = true</code> to record per\u2011registration call counts and emissions.</li> </ul> <p>Example: manual lifetime</p> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core;\nusing UnityEngine;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class PauseOverlay : MonoBehaviour\n{\n private MessagingComponent _messaging;\n private MessageRegistrationToken _token;\n\n private void Awake()\n {\n _messaging = GetComponent<MessagingComponent>();\n _token = _messaging.Create(this);\n _ = _token.RegisterUntargeted<GamePaused>(OnPaused);\n _ = _token.RegisterUntargeted<GameResumed>(OnResumed);\n }\n\n private void OnEnable() => _token.Enable();\n private void OnDisable() => _token.Disable();\n\n private void OnPaused(ref GamePaused m) => Show();\n private void OnResumed(ref GameResumed m) { Hide(); _token.UnregisterAll(); }\n}\n</code></pre> <p>Unity integration knobs</p> <p>MessageAwareComponent</p> <ul> <li><code>protected virtual bool MessageRegistrationTiedToEnableStatus => true</code>.</li> <li>Set to <code>false</code> to manage <code>Enable()</code>/<code>Disable()</code> yourself (e.g., persistent listeners on disabled components).</li> <li><code>protected virtual bool RegisterForStringMessages => true</code>.</li> <li>Set to <code>false</code> to not auto\u2011register string message demos.</li> </ul> C#<pre><code>using DxMessaging.Unity;\n\npublic sealed class PersistentListener : MessageAwareComponent\n{\n protected override bool MessageRegistrationTiedToEnableStatus => false; // stays enabled when component disables\n protected override bool RegisterForStringMessages => false; // opt out of string demos\n\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<MyEvent>(OnMyEvent);\n Token.Enable(); // explicitly enable once\n }\n\n private void OnMyEvent(ref MyEvent m) => DoWork();\n}\n</code></pre> <p>MessagingComponent</p> <ul> <li><code>emitMessagesWhenDisabled</code>: continue emitting while the GameObject is disabled.</li> <li><code>ToggleMessageHandler(bool)</code>: explicitly toggle the underlying handler.</li> </ul> C#<pre><code>using DxMessaging.Unity;\n\npublic sealed class EmissionControl : MonoBehaviour\n{\n public MessagingComponent messaging;\n\n public void PauseEmissions()\n {\n messaging.emitMessagesWhenDisabled = false;\n messaging.ToggleMessageHandler(false); // fully pause emissions\n }\n\n public void ResumeEmissions()\n {\n messaging.ToggleMessageHandler(true);\n }\n}\n</code></pre> <p>Emit while disabled (opt\u2011in)</p> C#<pre><code>// Keep emitting even when this GameObject is disabled\nmessaging.emitMessagesWhenDisabled = true;\n// Now ToggleMessageHandler(false) will be ignored while emitMessagesWhenDisabled is true\n</code></pre> <p>String messages: opt\u2011in/out</p> <ul> <li>MessageAwareComponent registers string demos by default. Override <code>RegisterForStringMessages</code> to disable.</li> <li>See String Messages page for using <code>StringMessage</code> and <code>GlobalStringMessage</code> during prototyping.</li> </ul> <p>Local bus islands (subsystems/tests)</p> <ul> <li>Create an isolated bus and pass it to the token factory to keep flows contained.</li> </ul> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nvar localBus = new MessageBus();\nvar handler = new MessageHandler(new InstanceId(123), localBus) { active = true };\nvar token = MessageRegistrationToken.Create(handler, localBus);\n// stage registrations on token; emit to localBus in tests\n// optional: replace the global singleton so shorthands use your DI bus\nMessageHandler.SetGlobalMessageBus(localBus);\n</code></pre>"},{"location":"guides/advanced/#temporarily-override-the-global-bus","title":"Temporarily override the global bus","text":"<p>When legacy shorthands or static helpers must run against a scoped bus (for example inside an integration test), use <code>MessageHandler.OverrideGlobalMessageBus</code> to push a temporary override that restores automatically:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\n\nIMessageBus testBus = new MessageBus();\n\nusing (MessageHandler.OverrideGlobalMessageBus(testBus))\n{\n // All static helpers (Emit shorthands, GlobalMessageBusProvider, etc.) resolve testBus.\n RunTestScenario();\n}\n// previous global bus restored here\n</code></pre> <p>The scope throws if you pass <code>null</code> and guarantees the prior bus returns even if the wrapped code throws. Pair this with <code>IMessageRegistrationBuilder</code> for clean test lifecycles.</p> <p>Editor note: When the global bus is replaced with a decorated implementation, certain inspector diagnostics that rely on the concrete <code>MessageBus</code> type (e.g., registration graphs) are temporarily unavailable until the override scope ends.</p> <p>Need a stable reference to the original bus regardless of overrides? Use <code>InitialGlobalMessageBusProviderAsset</code> (Create Asset \u2192 \u201cDxMessaging/Message Bus Providers/Initial Global Message Bus\u201d) to expose the startup instance.</p> <p>Scoped interceptors (debug)</p> C#<pre><code>using DxMessaging.Core; // MessageHandler, InstanceId\nusing DxMessaging.Core.MessageBus;\n\nvar bus = MessageHandler.MessageBus;\nAction remove = bus.RegisterBroadcastInterceptor<TookDamage>((ref InstanceId src, ref TookDamage m) =>\n{\n Debug.Log($\"Intercept {src}: {m.amount}\");\n return true; // allow\n}, priority: -100);\n\n// Later\nremove();\n</code></pre> <p>Safety and troubleshooting</p> <p>Do's</p> <ul> <li>CRITICAL: When overriding <code>MessageAwareComponent</code> hooks, you MUST call the base method: <code>base.Awake()</code>, <code>base.OnEnable()</code>, <code>base.OnDisable()</code>, <code>base.RegisterMessageHandlers()</code>.</li> <li>Always call <code>base.RegisterMessageHandlers()</code> first in your override to ensure parent class registrations happen before yours.</li> <li>Prefer overriding <code>RegisterForStringMessages => false</code> instead of removing <code>base.RegisterMessageHandlers()</code> if you don't want string demos.</li> <li> <p>Don't hide Unity methods with <code>new</code> (e.g., <code>new void OnEnable()</code>); always <code>override</code> and call <code>base.*</code>.</p> </li> <li> <p>Prefer <code>Awake()</code> for registration rather than <code>Start()</code>. <code>MessageAwareComponent</code> calls <code>RegisterMessageHandlers()</code> in <code>Awake()</code>.</p> </li> <li>Register once; enable/disable with component state.</li> <li>Prefer named handler methods for clarity and reuse.</li> <li>Use diagnostics in Editor; disable for release if not needed.</li> <li>Use GameObject/Component emit helpers (no manual <code>InstanceId</code>).</li> </ul>"},{"location":"guides/advanced/#important-behavior-note-snapshot-semantics","title":"Important behavior note: Snapshot Semantics","text":"<ul> <li>When a message is emitted, DxMessaging takes a snapshot of all current listeners (handlers, interceptors, post-processors).</li> <li>Listeners registered during an emission will not run for that emission \u2014 they only become active for the next emission.</li> <li>This prevents infinite loops (e.g., a handler that registers itself won't recurse).</li> <li>This applies to all message types (Untargeted, Targeted, Broadcast) and all listener types.</li> <li>See Interceptors & Ordering for detailed examples.</li> </ul> <p>Don\u2019ts</p> <ul> <li>Don\u2019t register in <code>Update</code>/<code>FixedUpdate</code> every frame.</li> <li>Don\u2019t double\u2011create tokens on the same component (MessagingComponent logs a warning).</li> <li>Don\u2019t forget to clear or disable tokens on destruction if you manage them manually.</li> </ul> <p>Side\u2011by\u2011side: manual events vs DxMessaging</p> <p>Before (manual C# events)</p> C#<pre><code>public sealed class Spawner\n{\n public event Action Spawned;\n public void Spawn() => Spawned?.Invoke();\n}\npublic sealed class UI\n{\n private Spawner _spawner;\n void Awake() { _spawner.Spawned += OnSpawned; }\n void OnDestroy() { _spawner.Spawned -= OnSpawned; } // easy to forget\n void OnSpawned() => Refresh();\n}\n</code></pre> <p>After (DxMessaging + token lifecycle)</p> C#<pre><code>using DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing DxMessaging.Core.Messages;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Spawned { }\n\n// Producer\nvar evt = new Spawned();\nevt.Emit();\n\n// Consumer (Unity)\n_ = token.RegisterUntargeted<Spawned>(OnSpawned);\nvoid OnSpawned(ref Spawned m) => Refresh();\n</code></pre> <p>More advanced use cases</p> <ul> <li>Scene transitions: create a local bus per scene (or sub\u2011system), pass it to tokens. Emit globally for cross\u2011scene untargeted notifications, but isolate targeted/broadcast to the scene\u2019s bus.</li> <li>Analytics layer: use <code>RegisterTargetedWithoutTargeting</code> and <code>RegisterBroadcastWithoutSource</code> to observe all flows; record in a buffer or file; disable in release.</li> <li>Pausable systems: set <code>emitMessagesWhenDisabled</code> for senders you want alive while disabled; use <code>ToggleMessageHandler</code> to pause entire listeners.</li> </ul> <p>Related</p> <ul> <li>Unity Integration</li> <li>Diagnostics (Inspector)</li> <li>String Messages</li> </ul>"},{"location":"guides/diagnostics/","title":"Diagnostics","text":"<p>DxMessaging emphasizes visibility. You can enable diagnostics globally or per token, inspect recent emissions, page through registrations, and even view contexts (targets/sources) \u2014 all from the MessagingComponent inspector.</p>"},{"location":"guides/diagnostics/#diagnosticstarget-enum","title":"DiagnosticsTarget Enum","text":"<p>The <code>DiagnosticsTarget</code> enum is a flags enum that controls when diagnostics are enabled. It allows fine-grained control over which execution environments collect diagnostic data.</p> Value Description <code>Off</code> Diagnostics are disabled in all environments. <code>Editor</code> Diagnostics run only while in the Unity Editor. <code>Runtime</code> Diagnostics run only in player/runtime builds (not the Editor). <code>All</code> Diagnostics run in both Editor and runtime environments. <p>Because <code>DiagnosticsTarget</code> is a flags enum, you can combine values:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Enable diagnostics only in the Unity Editor\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Editor;\n\n// Enable diagnostics only in runtime builds\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Runtime;\n\n// Enable diagnostics everywhere\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.All;\n\n// Disable diagnostics completely\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Off;\n</code></pre>"},{"location":"guides/diagnostics/#configuration-toggles","title":"Configuration Toggles","text":"<p>DxMessaging provides multiple levels of diagnostics control:</p>"},{"location":"guides/diagnostics/#global-defaults","title":"Global Defaults","text":"<ul> <li><code>IMessageBus.GlobalDiagnosticsTargets</code> \u2014 Sets the default diagnostics mode for newly created buses and tokens. Uses the <code>DiagnosticsTarget</code> flags enum.</li> <li><code>IMessageBus.GlobalMessageBufferSize</code> \u2014 Sets the default ring buffer size for emission history (default: 100).</li> </ul>"},{"location":"guides/diagnostics/#per-bus-and-per-token","title":"Per-Bus and Per-Token","text":"<ul> <li><code>IMessageBus.DiagnosticsMode</code> \u2014 Read-only property indicating whether diagnostics are active for a specific bus instance.</li> <li><code>MessageRegistrationToken.DiagnosticMode</code> \u2014 Controls diagnostics for an individual registration token.</li> </ul> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n// Configure global defaults before creating buses/tokens\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Editor;\nIMessageBus.GlobalMessageBufferSize = 200;\n\n// Check if diagnostics are enabled for a specific bus\nIMessageBus bus = MessageHandler.MessageBus;\nif (bus.DiagnosticsMode)\n{\n Debug.Log(\"Diagnostics are active on this bus.\");\n}\n</code></pre>"},{"location":"guides/diagnostics/#registrationlog-api","title":"RegistrationLog API","text":"<p>The <code>RegistrationLog</code> class tracks all messaging registrations and deregistrations for a message bus. This is invaluable for debugging subscription issues and understanding message flow.</p>"},{"location":"guides/diagnostics/#properties","title":"Properties","text":"Property Type Description <code>Enabled</code> <code>bool</code> Get/set whether logging is active. Disabled by default for performance. <code>Registrations</code> <code>IReadOnlyList<MessagingRegistration></code> Read-only access to all logged registrations."},{"location":"guides/diagnostics/#methods","title":"Methods","text":""},{"location":"guides/diagnostics/#logmessagingregistration-registration","title":"<code>Log(MessagingRegistration registration)</code>","text":"<p>Records a registration event. Called automatically by the message bus when <code>Enabled</code> is true.</p>"},{"location":"guides/diagnostics/#getregistrationsinstanceid-instanceid","title":"<code>GetRegistrations(InstanceId instanceId)</code>","text":"<p>Returns all registrations for a specific instance. Useful for inspecting what a particular component has registered for.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nIMessageBus bus = MessageHandler.MessageBus;\nbus.Log.Enabled = true;\n\n// After some registrations occur...\nInstanceId myComponent = GetComponent<MonoBehaviour>();\nforeach (MessagingRegistration reg in bus.Log.GetRegistrations(myComponent))\n{\n Debug.Log($\"Registered for {reg.type.Name} via {reg.registrationMethod}\");\n}\n</code></pre>"},{"location":"guides/diagnostics/#tostring-and-tostringfunc-serializer","title":"<code>ToString()</code> and <code>ToString(Func<MessagingRegistration, string> serializer)</code>","text":"<p>Returns a string representation of all logged registrations. You can provide a custom serializer for formatted output.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nIMessageBus bus = MessageHandler.MessageBus;\nbus.Log.Enabled = true;\n\n// ... after some registrations/deregistrations\nDebug.Log(bus.Log.ToString());\n\n// Custom formatting\nstring formatted = bus.Log.ToString(reg =>\n $\"[{reg.registrationType}] {reg.type.Name} @ {reg.time:F2}s\"\n);\nDebug.Log(formatted);\n</code></pre>"},{"location":"guides/diagnostics/#clearpredicate-shouldremove--null","title":"<code>Clear(Predicate<MessagingRegistration> shouldRemove = null)</code>","text":"<p>Removes registrations from the log. Pass <code>null</code> to clear all, or provide a predicate to selectively remove entries.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nIMessageBus bus = MessageHandler.MessageBus;\n\n// Clear all registrations\nint cleared = bus.Log.Clear();\n\n// Clear only deregistrations\nint deregistrationsCleared = bus.Log.Clear(\n reg => reg.registrationType == RegistrationType.Deregister\n);\n</code></pre>"},{"location":"guides/diagnostics/#messagingregistration-struct","title":"MessagingRegistration Struct","text":"<p>Each logged registration is stored as a <code>MessagingRegistration</code> struct containing:</p> Field Type Description <code>id</code> <code>InstanceId</code> The handler's unique identifier. <code>type</code> <code>Type</code> The message type being registered for. <code>registrationType</code> <code>RegistrationType</code> Whether this was a <code>Register</code> or <code>Deregister</code> event. <code>registrationMethod</code> <code>RegistrationMethod</code> The exact registration category (Targeted, Broadcast, etc.). <code>time</code> <code>float</code> Unity time when the registration occurred (Unity only)."},{"location":"guides/diagnostics/#registrationmethod-values","title":"RegistrationMethod Values","text":"<p>The <code>RegistrationMethod</code> enum captures how the handler was wired up:</p> <ul> <li><code>Targeted</code> \u2014 Bound to a specific recipient</li> <li><code>Untargeted</code> \u2014 Global untargeted handler</li> <li><code>Broadcast</code> \u2014 Bound to a specific source</li> <li><code>BroadcastWithoutSource</code> \u2014 Broadcast handler without explicit source</li> <li><code>TargetedWithoutTargeting</code> \u2014 Targeted handler ignoring runtime target</li> <li><code>GlobalAcceptAll</code> \u2014 Catch-all handler</li> <li><code>Interceptor</code> \u2014 Message interceptor</li> <li><code>UntargetedPostProcessor</code>, <code>TargetedPostProcessor</code>, <code>BroadcastPostProcessor</code> \u2014 Post-processors</li> <li><code>TargetedWithoutTargetingPostProcessor</code> \u2014 Post-processor for targeted messages ignoring runtime target</li> <li><code>BroadcastWithoutSourcePostProcessor</code> \u2014 Post-processor for broadcasts without explicit source</li> </ul>"},{"location":"guides/diagnostics/#emission-history","title":"Emission History","text":"<p>When diagnostics are enabled, buses and tokens record message emissions in a ring buffer:</p> <ul> <li>Buffer size is controlled by <code>IMessageBus.GlobalMessageBufferSize</code> (default: 100).</li> <li>Setting buffer size to 0 disables history retention (emissions are silently discarded).</li> <li>Inspect recent emissions per token via built-in diagnostics or build custom tools using post-processors.</li> </ul> C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Increase buffer size for more history\nIMessageBus.GlobalMessageBufferSize = 500;\n</code></pre>"},{"location":"guides/diagnostics/#logging-integration","title":"Logging Integration","text":"<p>Integrate DxMessaging with your logging framework:</p> C#<pre><code>using DxMessaging.Core;\n\nMessagingDebug.enabled = true;\nMessagingDebug.LogFunction = (level, msg) =>\n UnityEngine.Debug.Log($\"[DxMessaging:{level}] {msg}\");\n</code></pre>"},{"location":"guides/diagnostics/#per-environment-configuration","title":"Per-Environment Configuration","text":"<p>A common pattern is enabling diagnostics only in the Editor for development visibility while keeping runtime builds lean.</p>"},{"location":"guides/diagnostics/#editor-only-diagnostics","title":"Editor-Only Diagnostics","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Enable diagnostics only when running in the Unity Editor\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Editor;\n</code></pre> <p>This is the recommended default for most projects. You get full visibility during development without any performance cost in production builds.</p>"},{"location":"guides/diagnostics/#runtime-diagnostics-for-qa-builds","title":"Runtime Diagnostics for QA Builds","text":"<p>For QA or debug builds where you need diagnostics in the player:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\n\n#if DEVELOPMENT_BUILD || UNITY_EDITOR\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.All;\n#else\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Off;\n#endif\n</code></pre>"},{"location":"guides/diagnostics/#conditional-logging-based-on-build-type","title":"Conditional Logging Based on Build Type","text":"C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\npublic static class DiagnosticsBootstrap\n{\n [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]\n private static void Initialize()\n {\n#if UNITY_EDITOR\n IMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Editor;\n IMessageBus.GlobalMessageBufferSize = 200;\n MessageHandler.MessageBus.Log.Enabled = true;\n#elif DEVELOPMENT_BUILD\n IMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Runtime;\n IMessageBus.GlobalMessageBufferSize = 50;\n#else\n IMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Off;\n#endif\n }\n}\n</code></pre>"},{"location":"guides/diagnostics/#performance-considerations","title":"Performance Considerations","text":"<p>Diagnostics add overhead. Consider these factors when enabling them:</p>"},{"location":"guides/diagnostics/#memory-impact","title":"Memory Impact","text":"<ul> <li>Each <code>MessagingRegistration</code> struct consumes memory for the registration log.</li> <li>The emission ring buffer stores <code>MessageEmissionData</code> records (controlled by <code>GlobalMessageBufferSize</code>).</li> <li>Larger buffer sizes consume more memory but provide more history.</li> </ul>"},{"location":"guides/diagnostics/#cpu-impact","title":"CPU Impact","text":"<ul> <li>Registration logging adds overhead to every <code>Register</code> and <code>Deregister</code> call.</li> <li>Emission recording adds overhead to every message broadcast.</li> <li>Post-processor chains for diagnostics run after each message dispatch.</li> </ul>"},{"location":"guides/diagnostics/#recommendations","title":"Recommendations","text":"Environment Recommended Setting Buffer Size Development/Editor <code>DiagnosticsTarget.Editor</code> 100-200 QA/Debug Builds <code>DiagnosticsTarget.All</code> 50-100 Release Builds <code>DiagnosticsTarget.Off</code> N/A Automated Tests <code>DiagnosticsTarget.All</code> + <code>Log.Enabled</code> 100 C#<pre><code>using DxMessaging.Core.MessageBus;\n\n// Production-safe defaults\nIMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Off;\nIMessageBus.GlobalMessageBufferSize = 0; // No history retention\n</code></pre>"},{"location":"guides/diagnostics/#editor-integration-inspector","title":"Editor Integration (Inspector)","text":"<p>Attach <code>MessagingComponent</code> to a GameObject. In the Unity Inspector:</p> <ul> <li>Enable/Disable Global Diagnostics: Toggles bus-wide recording.</li> <li>Global Buffer: Paged view of recent emissions (type and context). Matching listeners are highlighted.</li> <li>Local Buffer: Per-listener ring buffer; enable per-token diagnostics to populate.</li> <li>Registrations: Paged list of what each listener registered for (type, priority, context).</li> </ul>"},{"location":"guides/diagnostics/#tips","title":"Tips","text":"<ul> <li>Turn on diagnostics while developing; turn off for release builds if you don't need runtime recording.</li> <li>Use <code>RegisterTargetedWithoutTargeting</code> or <code>RegisterBroadcastWithoutSource</code> for custom monitoring dashboards.</li> <li>Set <code>Log.Enabled = true</code> in tests to verify registration behavior.</li> <li>Use <code>Log.Clear()</code> between test cases to isolate registration tracking.</li> </ul>"},{"location":"guides/diagnostics/#related","title":"Related","text":"<ul> <li>Listening Patterns</li> <li>Troubleshooting</li> </ul>"},{"location":"guides/migration-guide/","title":"Migration Guide: Adopting DxMessaging Incrementally","text":"<p>This guide helps you introduce DxMessaging into an existing Unity project gradually and pragmatically. You don't need to rewrite everything at once.</p>"},{"location":"guides/migration-guide/#philosophy-start-small-prove-value","title":"Philosophy: Start Small, Prove Value","text":""},{"location":"guides/migration-guide/#dont-do-this","title":"Don't do this","text":"<ul> <li>L Rip out all C# events and rewrite everything</li> <li>L Force the whole team to learn it before trying it</li> <li>L Commit to full adoption before seeing benefits</li> </ul>"},{"location":"guides/migration-guide/#do-this-instead","title":"Do this instead","text":"<ul> <li>\u0005 Pick ONE system to migrate (low risk, high visibility)</li> <li>\u0005 Let old and new approaches coexist</li> <li>\u0005 Expand usage as team comfort grows</li> <li>\u0005 Evaluate after each migration step</li> </ul>"},{"location":"guides/migration-guide/#phase-0-install-and-experiment-1-2-hours","title":"Phase 0: Install and Experiment (1-2 hours)","text":""},{"location":"guides/migration-guide/#goal-get-comfortable-without-touching-production-code","title":"Goal: Get comfortable without touching production code","text":"<ol> <li>Install DxMessaging via Package Manager</li> <li>Read the Visual Guide (5 minutes)</li> <li>Import the Mini Combat sample from Package Manager</li> <li>Create a throwaway test scene and try:</li> </ol> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct TestMessage { public readonly int value; }\n\npublic class TestListener : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<TestMessage>(OnTest);\n }\n void OnTest(ref TestMessage m) => Debug.Log($\"Got {m.value}\");\n}\n\n// In another script:\nvar msg = new TestMessage(42);\nmsg.Emit();\n</code></pre> <p>Success criteria: You understand the basic flow and have no build errors.</p>"},{"location":"guides/migration-guide/#phase-1-add-to-a-new-feature-1-week","title":"Phase 1: Add to a New Feature (1 week)","text":""},{"location":"guides/migration-guide/#goal-prove-value-without-refactoring-existing-code","title":"Goal: Prove value without refactoring existing code","text":""},{"location":"guides/migration-guide/#best-candidates-for-first-adoption","title":"Best candidates for first adoption","text":"<ul> <li>\u0005 New UI system - Add a new settings menu that reacts to game state</li> <li>\u0005 Achievement/analytics system - Listen to existing events without coupling</li> <li>\u0005 New game mode - Implement it with DxMessaging from scratch</li> </ul>"},{"location":"guides/migration-guide/#example-adding-an-achievement-system","title":"Example: Adding an Achievement System","text":"C#<pre><code>// 1. Define messages for interesting events (don't touch existing code yet)\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct EnemyKilled {\n public readonly string enemyType;\n public readonly int playerLevel;\n}\n\n// 2. Make your NEW achievement system listen\npublic class AchievementSystem : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n _ = Token.RegisterBroadcastWithoutSource<EnemyKilled>(OnEnemyKilled);\n }\n\n void OnEnemyKilled(InstanceId source, EnemyKilled msg) {\n // Track kills, unlock achievements\n if (msg.enemyType == \"Boss\") UnlockAchievement(\"BossSlayer\");\n }\n}\n\n// 3. Bridge from existing code (minimal change)\npublic class Enemy : MonoBehaviour {\n public event Action OnDied; // OLD - keep for now\n\n void Die() {\n OnDied?.Invoke(); // OLD code still works\n\n // NEW: Emit DxMessage too\n var msg = new EnemyKilled(enemyType, PlayerStats.Level);\n msg.EmitGameObjectBroadcast(gameObject);\n }\n}\n</code></pre>"},{"location":"guides/migration-guide/#why-this-works","title":"Why this works","text":"<ul> <li>\u0005 Old code still works (zero risk)</li> <li>\u0005 New system is decoupled</li> <li>\u0005 Team sees immediate value (achievements without wiring)</li> <li>\u0005 Easy to roll back if needed</li> </ul>"},{"location":"guides/migration-guide/#phase-2-migrate-high-pain-areas-2-4-weeks","title":"Phase 2: Migrate High-Pain Areas (2-4 weeks)","text":""},{"location":"guides/migration-guide/#goal-replace-the-systems-causing-the-most-problems","title":"Goal: Replace the systems causing the most problems","text":""},{"location":"guides/migration-guide/#high-value-migration-targets","title":"High-value migration targets","text":"<ol> <li>UI that references too many systems - Replace with message listeners</li> <li>Global static event buses - Convert to DxMessaging</li> <li>Memory-leak prone event chains - Eliminate manual unsubscribe</li> </ol>"},{"location":"guides/migration-guide/#strategy-parallel-paths-during-transition","title":"Strategy: Parallel Paths During Transition","text":""},{"location":"guides/migration-guide/#step-1-add-dxmessages-alongside-existing-events","title":"Step 1: Add DxMessages alongside existing events","text":"C#<pre><code>// Old event (keep for now)\npublic event Action<int> OnHealthChanged;\n\n// New message\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct HealthChanged { public readonly int newHealth; }\n\nvoid TakeDamage(int amount) {\n health -= amount;\n\n // Fire both during migration\n OnHealthChanged?.Invoke(health); // OLD\n var msg = new HealthChanged(health);\n msg.EmitGameObjectBroadcast(gameObject); // NEW\n}\n</code></pre>"},{"location":"guides/migration-guide/#step-2-migrate-listeners-one-by-one","title":"Step 2: Migrate listeners one-by-one","text":"C#<pre><code>// OLD listener (comment out when ready)\n// void Awake() { player.OnHealthChanged += UpdateBar; }\n// void OnDestroy() { player.OnHealthChanged -= UpdateBar; }\n\n// NEW listener\npublic class HealthBar : MessageAwareComponent {\n [SerializeField] private GameObject playerObject;\n\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n _ = Token.RegisterGameObjectBroadcast<HealthChanged>(playerObject, OnHealthChanged);\n }\n\n void OnHealthChanged(ref HealthChanged msg) => UpdateBar(msg.newHealth);\n}\n</code></pre>"},{"location":"guides/migration-guide/#step-3-remove-old-events-once-all-listeners-migrated","title":"Step 3: Remove old events once all listeners migrated","text":"C#<pre><code>// Delete after confirming no one uses it:\n// public event Action<int> OnHealthChanged; L\n\nvoid TakeDamage(int amount) {\n health -= amount;\n var msg = new HealthChanged(health);\n msg.EmitGameObjectBroadcast(gameObject); // Only this now\n}\n</code></pre>"},{"location":"guides/migration-guide/#migration-checklist-template","title":"Migration Checklist Template","text":"<p>Use this for each system you migrate:</p> Text Only<pre><code>System: _________________\n\n[ ] Identified all listeners to migrate\n[ ] Defined DxMessages for all events\n[ ] Added DxMessage emissions (parallel with old events)\n[ ] Migrated listeners one-by-one\n[ ] Tested thoroughly\n[ ] Removed old event declarations\n[ ] Updated documentation/comments\n</code></pre>"},{"location":"guides/migration-guide/#phase-3-adopt-for-all-new-code-ongoing","title":"Phase 3: Adopt for All New Code (Ongoing)","text":""},{"location":"guides/migration-guide/#goal-make-dxmessaging-the-default-for-new-features","title":"Goal: Make DxMessaging the default for new features","text":""},{"location":"guides/migration-guide/#team-guidelines","title":"Team guidelines","text":"<ul> <li>\u0005 All new cross-system communication uses DxMessaging</li> <li>\u0005 Old code migrates opportunistically (when touched)</li> <li>\u0005 Code reviews check for messaging best practices</li> </ul>"},{"location":"guides/migration-guide/#example-team-policy","title":"Example team policy","text":"Text Only<pre><code>When to use DxMessaging (for new code):\n- Any UI listening to game state \ufffd DxMessaging\n- Any analytics/logging \ufffd DxMessaging\n- Any cross-scene communication \ufffd DxMessaging\n- Any event with 2+ listeners \ufffd DxMessaging\n\nWhen to use direct references/events:\n- Simple UI button \ufffd method call (use UnityEvents)\n- Single listener, same GameObject \ufffd direct reference\n- Private implementation details \ufffd keep internal\n</code></pre>"},{"location":"guides/migration-guide/#coexistence-patterns","title":"Coexistence Patterns","text":""},{"location":"guides/migration-guide/#pattern-1-event-to-message-bridge","title":"Pattern 1: Event-to-Message Bridge","text":"C#<pre><code>public class LegacyBridge : MonoBehaviour {\n [SerializeField] private LegacySystem legacySystem;\n\n void Awake() {\n // Old system fires event, we convert to message\n legacySystem.OnSomethingHappened += (args) => {\n var msg = new SomethingHappened(args);\n msg.Emit();\n };\n }\n}\n</code></pre>"},{"location":"guides/migration-guide/#pattern-2-message-to-event-bridge","title":"Pattern 2: Message-to-Event Bridge","text":"C#<pre><code>public class ModernBridge : MessageAwareComponent {\n public event Action<int> LegacyEvent; // For old code that needs events\n\n protected override void RegisterMessageHandlers() {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<NewMessage>(OnMessage);\n }\n\n void OnMessage(ref NewMessage msg) {\n LegacyEvent?.Invoke(msg.value); // Fire old event\n }\n}\n</code></pre>"},{"location":"guides/migration-guide/#pattern-3-gradual-gameobject-migration","title":"Pattern 3: Gradual GameObject Migration","text":"C#<pre><code>// Phase 1: Keep old inspector references, emit messages\npublic class Player : MonoBehaviour {\n [SerializeField] private HealthBar healthBar; // OLD - will remove later\n\n void TakeDamage(int amount) {\n health -= amount;\n healthBar.UpdateHealth(health); // OLD direct call\n var msg = new HealthChanged(health);\n msg.EmitGameObjectBroadcast(gameObject); // NEW message\n }\n}\n\n// Phase 2: Remove direct references\npublic class Player : MonoBehaviour {\n // [SerializeField] private HealthBar healthBar; \ufffd DELETED\n\n void TakeDamage(int amount) {\n health -= amount;\n var msg = new HealthChanged(health);\n msg.EmitGameObjectBroadcast(gameObject); // Only this\n }\n}\n</code></pre>"},{"location":"guides/migration-guide/#what-to-migrate-first-vs-last","title":"What to Migrate First vs. Last","text":""},{"location":"guides/migration-guide/#migrate-first-high-value-low-risk","title":"Migrate FIRST (High Value, Low Risk)","text":"<ol> <li>New systems - No refactor needed, immediate win</li> <li>Analytics/logging - Decoupled observers, zero disruption</li> <li>UI that needs to listen to many systems - Eliminate reference spaghetti</li> <li>Global event buses - Direct replacement, clear improvement</li> </ol>"},{"location":"guides/migration-guide/#migrate-later-lower-priority","title":"Migrate LATER (Lower Priority)","text":"<ol> <li>Stable, working code - If it ain't broke, don't rush</li> <li>Performance-critical paths - Validate overhead first</li> <li>Code that rarely changes - Low ROI for migration</li> <li>Third-party integrations - Keep adapters simple</li> </ol>"},{"location":"guides/migration-guide/#dont-migrate-keep-as-is","title":"DON'T Migrate (Keep As-Is)","text":"<ol> <li>Simple button onClick \ufffd method - UnityEvents are fine</li> <li>Private implementation details - Internal events are okay</li> <li>Single-listener, same-GameObject - Direct references are clearer</li> <li>Legacy systems about to be deleted - Why bother?</li> </ol>"},{"location":"guides/migration-guide/#common-migration-pitfalls","title":"Common Migration Pitfalls","text":""},{"location":"guides/migration-guide/#l-pitfall-1-boiling-the-ocean","title":"L Pitfall 1: Boiling the Ocean","text":"<p>Problem: \"Let's rewrite the entire codebase!\"</p> <p>Solution: Migrate incrementally. Set a rule: \"One system per sprint\" or \"New features only.\"</p>"},{"location":"guides/migration-guide/#l-pitfall-2-no-rollback-plan","title":"L Pitfall 2: No Rollback Plan","text":"<p>Problem: Full commit before proving value.</p> <p>Solution: Keep old code commented for 1-2 sprints:</p> C#<pre><code>// OLD (keep until 2024-02-01)\n// player.OnHealthChanged += UpdateBar;\n\n// NEW\n_ = Token.RegisterBroadcast<HealthChanged>(...);\n</code></pre>"},{"location":"guides/migration-guide/#l-pitfall-3-mixing-message-types-incorrectly","title":"L Pitfall 3: Mixing Message Types Incorrectly","text":"<p>Problem: Using Untargeted for everything because it's \"simpler.\"</p> <p>Solution: Follow message type guidelines:</p> <ul> <li>Global state? Untargeted</li> <li>Command to one? Targeted</li> <li>Event from one? Broadcast</li> </ul>"},{"location":"guides/migration-guide/#l-pitfall-4-over-messaging","title":"L Pitfall 4: Over-Messaging","text":"<p>Problem: Converting every method call to a message.</p> <p>Solution: Keep simple things simple:</p> C#<pre><code>// L OVERKILL - Just call the method!\nvar msg = new CloseDoorMessage(doorId);\nmsg.Emit();\n\n// BETTER - Direct reference is fine\ndoor.Close();\n</code></pre>"},{"location":"guides/migration-guide/#l-pitfall-5-not-training-the-team","title":"L Pitfall 5: Not Training the Team","text":"<p>Problem: Team doesn't understand when/how to use it.</p>"},{"location":"guides/migration-guide/#solution","title":"Solution","text":"<ul> <li>Schedule a 30-minute walkthrough</li> <li>Share the Visual Guide</li> <li>Pair program on first migrations</li> <li>Document team conventions in your wiki</li> </ul>"},{"location":"guides/migration-guide/#success-metrics","title":"Success Metrics","text":"<p>Track these to validate migration is worthwhile:</p>"},{"location":"guides/migration-guide/#quantitative","title":"Quantitative","text":"<ul> <li>Lines of event subscribe/unsubscribe code removed</li> <li>Number of SerializedField references eliminated</li> <li>Memory leaks fixed (profiler)</li> </ul>"},{"location":"guides/migration-guide/#qualitative","title":"Qualitative","text":"<ul> <li>Time to add new observers (before/after)</li> <li>Ease of debugging message flow</li> <li>Team satisfaction (survey)</li> </ul>"},{"location":"guides/migration-guide/#example","title":"Example","text":"<p>\"Before: Adding achievement tracking required touching 12 files. After: Added achievement system with zero changes to existing code.\"</p>"},{"location":"guides/migration-guide/#timeline-examples","title":"Timeline Examples","text":""},{"location":"guides/migration-guide/#small-project-10k-lines","title":"Small Project (10k lines)","text":"<ul> <li>Week 1: Experiment + add to one new feature</li> <li>Week 2-3: Migrate high-pain UI systems</li> <li>Week 4+: New code uses DxMessaging</li> </ul>"},{"location":"guides/migration-guide/#medium-project-50k-lines","title":"Medium Project (50k lines)","text":"<ul> <li>Month 1: Pilot with 2-3 systems</li> <li>Month 2-4: Gradual migration of problem areas</li> <li>Month 5+: Standard practice for new code</li> </ul>"},{"location":"guides/migration-guide/#large-project-100k-lines","title":"Large Project (100k+ lines)","text":"<ul> <li>Quarter 1: Pilot + evangelize</li> <li>Quarter 2-3: Migrate critical systems</li> <li>Quarter 4+: Opportunistic refactors</li> </ul>"},{"location":"guides/migration-guide/#getting-team-buy-in","title":"Getting Team Buy-In","text":""},{"location":"guides/migration-guide/#for-managers","title":"For Managers","text":"<ul> <li>\"Reduces memory leaks and hard-to-debug issues\"</li> <li>\"Faster feature development (decoupled systems)\"</li> <li>\"Easier onboarding (clear message contracts)\"</li> </ul>"},{"location":"guides/migration-guide/#for-developers","title":"For Developers","text":"<ul> <li>\"No more manual unsubscribe hell\"</li> <li>\"Built-in debugging (Inspector shows message history)\"</li> <li>\"Add features without touching existing code\"</li> </ul>"},{"location":"guides/migration-guide/#for-qa","title":"For QA","text":"<ul> <li>\"Easier to reproduce bugs (message logs)\"</li> <li>\"Fewer null reference errors\"</li> <li>\"Clear system boundaries\"</li> </ul>"},{"location":"guides/migration-guide/#faq-migration-edition","title":"FAQ: Migration Edition","text":""},{"location":"guides/migration-guide/#do-we-need-to-migrate-everything","title":"\"Do we need to migrate everything?\"","text":"<p>No! DxMessaging coexists happily with C# events, UnityEvents, and direct references. Migrate what benefits, leave what works.</p>"},{"location":"guides/migration-guide/#what-if-we-decide-its-not-for-us","title":"\"What if we decide it's not for us?\"","text":"<p>Keep old events during migration. If you hate it, delete the DxMessaging parts and uncomment the old code.</p>"},{"location":"guides/migration-guide/#how-do-we-handle-prefabs-with-inspector-references","title":"\"How do we handle prefabs with Inspector references?\"","text":"<p>Phase them out gradually:</p> <ol> <li>Keep references during transition</li> <li>Emit messages alongside old calls</li> <li>Migrate listeners</li> <li>Remove references in next refactor pass</li> </ol>"},{"location":"guides/migration-guide/#adopt-the-di-registration-builder","title":"Adopt the DI Registration Builder","text":"<p>Once the bus/provider abstractions are in place, wire listeners through the registration builder instead of hand-rolling handler lifecycles. Benefits:</p> <ul> <li>Container-managed lifetimes (<code>IDisposable</code>, <code>IInitializable</code>, <code>IStartable</code>, etc.) automatically enable/disable registrations.</li> <li>Centralises diagnostics toggles and message bus selection.</li> <li>Keeps MonoBehaviours and pure C# services on the same path.</li> </ul> C#<pre><code>public sealed class InventoryService : IStartable, IDisposable\n{\n private readonly MessageRegistrationLease lease;\n\n public InventoryService(IMessageRegistrationBuilder registrationBuilder)\n {\n lease = registrationBuilder.Build(new MessageRegistrationBuildOptions\n {\n Configure = token =>\n {\n _ = token.RegisterUntargeted<InventoryChanged>(OnInventoryChanged);\n }\n });\n }\n\n public void Start()\n {\n lease.Activate();\n }\n\n public void Dispose()\n {\n lease.Dispose();\n }\n\n private static void OnInventoryChanged(ref InventoryChanged message)\n {\n // respond to updates\n }\n}\n</code></pre> <ul> <li>Unity scene code: Call <code>MessagingComponent.CreateRegistrationBuilder()</code> during dependency injection and share the lease across helper services or pooled objects.</li> <li>Container shims: Define <code>ZENJECT_PRESENT</code>, <code>VCONTAINER_PRESENT</code>, or <code>REFLEX_PRESENT</code> to enable the built-in installers/extensions that register the builder automatically when those frameworks are present.</li> <li>Tests: Prefer the builder to create isolated tokens tied to the test fixture lifecycle.</li> </ul>"},{"location":"guides/migration-guide/#should-we-migrate-tests","title":"\"Should we migrate tests?\"","text":"<p>Yes! Tests benefit from isolated message buses:</p> C#<pre><code>var testBus = new MessageBus();\nvar token = MessageRegistrationToken.Create(handler, testBus);\n// Test in isolation\n</code></pre>"},{"location":"guides/migration-guide/#what-about-mobile-performance","title":"\"What about mobile performance?\"","text":"<p>Enable diagnostics only in Editor:</p> C#<pre><code>#if UNITY_EDITOR\nIMessageBus.GlobalDiagnosticsMode = true;\n#endif\n</code></pre> <p>Profile early, measure impact.</p>"},{"location":"guides/migration-guide/#next-steps","title":"Next Steps","text":"<ol> <li>Try Phase 0 - Install and experiment (today)</li> <li>Pick one system - Choose a low-risk, high-value target (this week)</li> <li>Timebox it - Give yourself 2 weeks to evaluate</li> <li>Measure results - Did it make life better?</li> <li>Expand or abort - Based on evidence, not hope</li> </ol> <p>Remember: Migration is a journey, not a destination. Go at your own pace.</p> <p>Questions? See FAQ | Need patterns? See Common Patterns</p>"},{"location":"guides/patterns/","title":"DxMessaging Patterns: Real-World Solutions","text":"<p>\u2190 Back to Index | Getting Started | Message Types | Samples</p> <p>You're here because: You understand DxMessaging basics, now you want to see \"How do I actually build X?\"</p>"},{"location":"guides/patterns/#what-youll-find","title":"What you'll find","text":"<ul> <li>Basic Patterns - Fundamental building blocks (scene transitions, commands, observability)</li> <li>Advanced Patterns - Power user techniques (diagnostics, testing, legacy integration)</li> <li>Scale Patterns - Examples for larger systems (100+ entities, cross-scene systems, large UI)</li> </ul>"},{"location":"guides/patterns/#reading-guide","title":"Reading guide","text":"<ul> <li>New to DxMessaging? Start with Basic Patterns 1-8</li> <li>Intermediate user? Jump to Advanced Patterns 9-12</li> <li>Building at scale? Go straight to Real-World Scale Patterns</li> <li>Specific problem? Use Ctrl+F / Cmd+F to search</li> </ul> <p>Philosophy: These patterns address common challenges teams face when building messaging systems.</p>"},{"location":"guides/patterns/#quick-links-i-want-to","title":"Quick Links: \"I Want To...\"","text":""},{"location":"guides/patterns/#find-your-use-case-jump-to-the-pattern","title":"Find your use case, jump to the pattern","text":"I want to... Go to Make UI react to gameplay Pattern 2: Directed Commands Coordinate scene transitions Pattern 1: Scene-wide Events Build an achievement system Pattern 3: Observability + Global Accept-All Validate input/damage before it happens Pattern 4: Interceptors Add analytics without touching gameplay Pattern 5: Post-Processors Track ALL damage from ANY entity Pattern: Managing 100+ Entities Build a large UI system (20+ panels) Pattern: Large-Scale UI Make systems run in a specific order Pattern: Priority Ordering Test in isolation Pattern 6: Local Bus Islands Migrate from C# events Pattern 9: Bridging Legacy Handle persistent systems across scene loads Pattern: Cross-Scene Persistent See what's happening (debug message flow) Pattern 11: Diagnostics Build a battle royale / large multiplayer Pattern: Battle Royale Example Use with Scriptable Object Architecture Pattern 14: SOA Compatibility"},{"location":"guides/patterns/#table-of-contents","title":"Table of Contents","text":""},{"location":"guides/patterns/#basic-patterns","title":"Basic Patterns","text":"<ul> <li>Scene-wide Events (Untargeted)</li> <li>Directed Commands (Targeted)</li> <li>Observability (Broadcast)</li> <li>Validation and Normalization (Interceptors)</li> <li>Analytics and Logging (Post-Processors)</li> <li>Local Bus Islands</li> <li>Lifecycle Pattern in Unity</li> <li>Cross-Scene Messaging</li> </ul>"},{"location":"guides/patterns/#advanced-patterns","title":"Advanced Patterns","text":"<ul> <li>Bridging Legacy Unity Messaging</li> <li>Global Accept-All Handlers</li> <li>Diagnostics and Tuning</li> <li>Testing</li> <li>Compatibility with Scriptable Object Architecture (SOA)</li> </ul>"},{"location":"guides/patterns/#real-world-scale-patterns","title":"Real-World Scale Patterns","text":"<ul> <li>Managing 100+ Combat Entities</li> <li>Cross-Scene Persistent Systems</li> <li>Large-Scale UI System (20+ Panels)</li> <li>Priority-Ordered Execution</li> <li>Efficient Interception at Scale</li> <li>Post-Processing for Analytics at Scale</li> <li>Performance Optimization Patterns</li> <li>Production Example: Battle Royale Game</li> </ul>"},{"location":"guides/patterns/#important-inheritance-with-messageawarecomponent","title":"Important: Inheritance with MessageAwareComponent","text":"<ul> <li>Many examples derive from <code>MessageAwareComponent</code>. When overriding hooks, you MUST call the base method.</li> <li>Always call <code>base.RegisterMessageHandlers()</code> FIRST in your override to preserve default string\u2011message registrations and parent class registrations.</li> <li>CRITICAL: Call <code>base.OnEnable()</code> / <code>base.OnDisable()</code> if you override lifecycle methods; otherwise your token may never enable/disable.</li> <li>CRITICAL: Call <code>base.Awake()</code> if you override <code>Awake()</code>; otherwise your token won't be created.</li> <li>To opt out of string demos, override <code>RegisterForStringMessages => false</code> instead of skipping the base call.</li> <li>Don't use <code>new</code> to hide methods (e.g., <code>new void OnEnable()</code>); always use <code>override</code> and call <code>base.*</code>.</li> </ul> <p>Registration timing (pit of success)</p> <ul> <li>Prefer <code>Awake()</code> for all message handler registration\u2014this is when <code>MessageAwareComponent</code> calls <code>RegisterMessageHandlers()</code>.</li> <li>Avoid registering in <code>Start()</code> unless you have a specific order-of-execution reason.</li> <li>Early registration in <code>Awake()</code> ensures your handlers are ready before other components' <code>Start()</code> methods run.</li> </ul>"},{"location":"guides/patterns/#1-scene-wide-events-untargeted","title":"1) Scene-wide Events (Untargeted)","text":"<p>Use untargeted messages for global state changes that any system might care about. Keep messages small and immutable.</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneLoaded\n{\n public readonly int buildIndex;\n}\n\n// Sender\nvar evt = new SceneLoaded(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex);\nevt.Emit();\n\n// Listener\n_ = token.RegisterUntargeted<SceneLoaded>(OnSceneLoaded);\nvoid OnSceneLoaded(ref SceneLoaded m) => RefreshUI();\n</code></pre>"},{"location":"guides/patterns/#2-directed-commands-targeted","title":"2) Directed Commands (Targeted)","text":"<p>Target a specific <code>GameObject</code>/<code>Component</code> when you want direct control.</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n// Sender\nvar heal = new Heal(10);\nheal.EmitComponentTargeted(this);\n\n// Listener (on the hero)\n_ = token.RegisterComponentTargeted<Heal>(this, OnHeal);\nvoid OnHeal(ref Heal m) => ApplyHeal(m.amount);\n</code></pre>"},{"location":"guides/patterns/#3-observability-broadcast","title":"3) Observability (Broadcast)","text":"<p>Broadcast from a source; any listener can observe.</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing DxMessaging.Core; // InstanceId\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n\n// Sender\nvar hit = new TookDamage(5);\nhit.EmitGameObjectBroadcast(enemyGO);\n\n// Listener: observe every source\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyTookDamage);\nvoid OnAnyTookDamage(InstanceId src, TookDamage m) => Log(src, m);\n</code></pre>"},{"location":"guides/patterns/#4-validation-and-normalization-interceptors","title":"4) Validation and Normalization (Interceptors)","text":"<p>Use interceptors to enforce rules before handlers run.</p> C#<pre><code>using DxMessaging.Core; // MessageHandler\n\nvar bus = MessageHandler.MessageBus;\n_ = token.RegisterBroadcastInterceptor<TookDamage>((ref InstanceId source, ref TookDamage m) =>\n{\n if (m.amount <= 0) return false; // cancel\n m = new TookDamage(Math.Min(m.amount, 999)); // clamp\n return true;\n}, priority: 0);\n</code></pre>"},{"location":"guides/patterns/#5-analyticslogging-post-processors","title":"5) Analytics/Logging (Post-Processors)","text":"<p>Post-processors run after handlers\u2014ideal for metrics.</p> C#<pre><code>_ = token.RegisterUntargetedPostProcessor<SceneLoaded>((ref SceneLoaded m) => Metrics.TrackScene(m.buildIndex));\n</code></pre>"},{"location":"guides/patterns/#6-local-bus-islands","title":"6) Local Bus Islands","text":"<p><code>MessageHandler.MessageBus</code> is the global bus. For isolation, use your own <code>MessageBus</code> instance and pass it to the token factory.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\nvar localBus = new MessageBus();\nvar token = MessageRegistrationToken.Create(handler, localBus);\n</code></pre>"},{"location":"guides/patterns/#7-lifecycle-pattern-in-unity","title":"7) Lifecycle Pattern in Unity","text":"<ul> <li>Stage registrations in <code>Awake()</code> (preferred) or <code>Start()</code> (only if order-dependent).</li> <li>Call <code>token.Enable()</code> in <code>OnEnable</code> and <code>token.Disable()</code> in <code>OnDisable</code>.</li> <li>Use <code>MessageAwareComponent</code> to avoid boilerplate\u2014it handles all of this automatically.</li> </ul> <p>Why Awake over Start?</p> <ul> <li><code>Awake()</code> runs before any <code>Start()</code> methods, ensuring your handlers are ready early.</li> <li>Other components may emit messages in their <code>Start()</code> methods\u2014registering in <code>Awake()</code> ensures you don't miss them.</li> <li><code>MessageAwareComponent</code> automatically calls <code>RegisterMessageHandlers()</code> in <code>Awake()</code>, following this best practice.</li> </ul> <p>Side\u2011by\u2011side: lifecycle</p> <p>Before</p> C#<pre><code>void Awake() { /* register */ }\nvoid OnDestroy() { /* maybe unregister (often forgotten) */ }\n</code></pre> <p>After (token)</p> C#<pre><code>void Awake() { /* stage registrations - PREFERRED */ }\nvoid OnEnable() { token.Enable(); }\nvoid OnDisable() { token.Disable(); }\n</code></pre>"},{"location":"guides/patterns/#8-cross-scene-messaging","title":"8) Cross-Scene Messaging","text":"<p>Untargeted messages flow anywhere; targeted/broadcast require a valid <code>InstanceId</code>. For cross-scene, ensure the target/source object exists when you emit.</p>"},{"location":"guides/patterns/#9-bridging-legacy-unity-messaging","title":"9) Bridging Legacy Unity Messaging","text":"<p><code>ReflexiveMessage</code> mirrors <code>SendMessage*</code> patterns but keeps you in the bus pipeline.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\nvar msg = new ReflexiveMessage(\"OnHit\", ReflexiveSendMode.Upwards, 10);\nmsg.EmitGameObjectTargeted(someGameObject);\n</code></pre>"},{"location":"guides/patterns/#10-global-accept-all-handlers","title":"10) Global Accept-All Handlers","text":"<p>Use when building tools or inspectors that want to observe everything.</p> C#<pre><code>_ = token.RegisterGlobalAcceptAll(\n (ref IUntargetedMessage m) => Debug.Log($\"Untargeted {m.MessageType}\"),\n (ref InstanceId target, ref ITargetedMessage m) => Debug.Log($\"Targeted {m.MessageType} to {target}\"),\n (ref InstanceId source, ref IBroadcastMessage m) => Debug.Log($\"Broadcast {m.MessageType} from {source}\")\n);\n\nDo\u2019s\n\n- Use global accept\u2011all in tooling and debug inspectors.\n- Prefer specific registrations for gameplay code to avoid surprises.\n</code></pre>"},{"location":"guides/patterns/#11-diagnostics-and-tuning","title":"11) Diagnostics and Tuning","text":"<ul> <li>Enable <code>IMessageBus.GlobalDiagnosticsMode</code> in Editor or per-token.</li> <li>Adjust <code>IMessageBus.GlobalMessageBufferSize</code> for deeper history (Editor settings UI provided).</li> <li>Wire <code>MessagingDebug.LogFunction</code> to Unity\u2019s console to see warnings/errors.</li> </ul>"},{"location":"guides/patterns/#12-testing","title":"12) Testing","text":"<ul> <li>Construct messages directly and emit via extension helpers.</li> <li>Use a local <code>MessageBus</code> per test to avoid global state.</li> <li>Wrap handlers to increment counters or assert payloads.</li> </ul>"},{"location":"guides/patterns/#13-real-world-scale-beyond-toy-examples","title":"13) Real-World Scale: Beyond Toy Examples","text":"<p>Most documentation shows simple, small-scale examples. Here's how DxMessaging behaves at production scale.</p>"},{"location":"guides/patterns/#pattern-managing-100-combat-entities","title":"Pattern: Managing 100+ Combat Entities","text":"<p>Challenge: Track damage, healing, status effects for a large battlefield without performance collapse.</p> C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing DxMessaging.Core;\n\n// Messages\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct EntityDamaged {\n public readonly int amount;\n public readonly string damageType;\n}\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct EntityHealed { public readonly int amount; }\n\n// Entity: Each of 100+ entities emits events\npublic class CombatEntity : MessageAwareComponent {\n public void TakeDamage(int amount, string type) {\n health -= amount;\n var msg = new EntityDamaged(amount, type);\n msg.EmitGameObjectBroadcast(gameObject);\n }\n}\n\n// Combat UI: Tracks specific enemy (targeted observation)\npublic class EnemyHealthBar : MessageAwareComponent {\n [SerializeField] private GameObject trackedEnemy;\n\n protected override void RegisterMessageHandlers() {\n // Only listens to ONE enemy, not all 100\n _ = Token.RegisterGameObjectBroadcast<EntityDamaged>(trackedEnemy, OnDamaged);\n }\n\n void OnDamaged(ref EntityDamaged msg) => UpdateBar();\n}\n\n// Analytics: Observes ALL entities (global observation)\npublic class CombatAnalytics : MessageAwareComponent {\n private readonly Dictionary<InstanceId, int> totalDamageByEntity = new();\n\n protected override void RegisterMessageHandlers() {\n // Listens to all 100+ entities with ONE registration\n _ = Token.RegisterBroadcastWithoutSource<EntityDamaged>(OnAnyDamage);\n }\n\n void OnAnyDamage(InstanceId source, EntityDamaged msg) {\n if (!totalDamageByEntity.ContainsKey(source))\n totalDamageByEntity[source] = 0;\n totalDamageByEntity[source] += msg.amount;\n }\n}\n</code></pre>"},{"location":"guides/patterns/#scale-characteristics","title":"Scale characteristics","text":"<ul> <li>\u2705 Each entity broadcasts ~10-50 messages/second \u2192 No GC allocations (struct messages)</li> <li>\u2705 Targeted listeners (health bars) only receive relevant messages \u2192 O(1) lookup</li> <li>\u2705 Global listeners (analytics) receive all messages \u2192 Single handler, not N handlers</li> <li>\u2705 Adding/removing entities doesn't break registrations (no manual wiring)</li> </ul>"},{"location":"guides/patterns/#performance-notes","title":"Performance notes","text":"<ul> <li>Disable diagnostics in production (<code>IMessageBus.GlobalDiagnosticsMode = false</code>)</li> <li>Use <code>RegisterBroadcastWithoutSource</code> sparingly (it's called for every emit)</li> <li>Profile with Unity Profiler to find hotspots</li> </ul>"},{"location":"guides/patterns/#pattern-cross-scene-persistent-systems","title":"Pattern: Cross-Scene Persistent Systems","text":"<p>Challenge: Maintain event flow across scene loads (achievements, save system, analytics).</p> C#<pre><code>using UnityEngine;\nusing DxMessaging.Unity;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\n\n// Persistent singleton that survives scene loads\npublic class PersistentAchievementSystem : MessageAwareComponent {\n private static PersistentAchievementSystem instance;\n\n void Awake() {\n if (instance != null) {\n Destroy(gameObject);\n return;\n }\n instance = this;\n DontDestroyOnLoad(gameObject);\n }\n\n protected override void RegisterMessageHandlers() {\n // Listen to ALL broadcasts from ANY scene\n _ = Token.RegisterBroadcastWithoutSource<EntityDamaged>(OnAnyDamage);\n _ = Token.RegisterUntargeted<LevelCompleted>(OnLevelComplete);\n }\n\n void OnAnyDamage(InstanceId src, EntityDamaged msg) {\n totalDamage += msg.amount;\n CheckAchievements();\n }\n\n void OnLevelComplete(ref LevelCompleted msg) {\n UnlockAchievement($\"Complete_Level_{msg.levelIndex}\");\n }\n}\n\n// Scene-specific objects emit messages normally\npublic class Boss : MessageAwareComponent {\n void Die() {\n var msg = new BossDefeated(bossName);\n msg.EmitGameObjectBroadcast(gameObject);\n // PersistentAchievementSystem hears this even though it's in DontDestroyOnLoad\n }\n}\n</code></pre> <p>Key insight: Persistent listeners (<code>DontDestroyOnLoad</code>) stay registered across scenes. Scene-specific emitters come/go, but the observer remains.</p> <p>Gotcha: If you emit <code>Targeted</code> or <code>Broadcast</code> messages, ensure the target/source <code>InstanceId</code> is still valid after scene loads. For cross-scene communication, prefer <code>Untargeted</code> messages.</p>"},{"location":"guides/patterns/#pattern-large-scale-ui-system-20-panels","title":"Pattern: Large-Scale UI System (20+ Panels)","text":"<p>Challenge: Coordinate 20+ UI panels reacting to game state without tight coupling.</p> C#<pre><code>// Game state messages (global)\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerStatsChanged {\n public readonly int health;\n public readonly int mana;\n public readonly int gold;\n}\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct InventoryChanged { public readonly int itemCount; }\n\n// Each UI panel listens independently\npublic class HealthPanel : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterUntargeted<PlayerStatsChanged>(OnStats);\n }\n void OnStats(ref PlayerStatsChanged msg) => UpdateHealth(msg.health);\n}\n\npublic class ManaPanel : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterUntargeted<PlayerStatsChanged>(OnStats);\n }\n void OnStats(ref PlayerStatsChanged msg) => UpdateMana(msg.mana);\n}\n\npublic class GoldPanel : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterUntargeted<PlayerStatsChanged>(OnStats);\n }\n void OnStats(ref PlayerStatsChanged msg) => UpdateGold(msg.gold);\n}\n\npublic class InventoryPanel : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterUntargeted<InventoryChanged>(OnInventory);\n }\n void OnInventory(ref InventoryChanged msg) => Refresh();\n}\n\n// Game systems emit once, all panels update\npublic class PlayerStats : MonoBehaviour {\n void UpdateStats() {\n var msg = new PlayerStatsChanged(health, mana, gold);\n msg.Emit(); // All 3 panels update automatically\n }\n}\n</code></pre>"},{"location":"guides/patterns/#benefits-at-scale","title":"Benefits at scale","text":"<ul> <li>\u2705 Add/remove panels without touching game logic</li> <li>\u2705 Panels can be enabled/disabled freely (tokens handle lifecycle)</li> <li>\u2705 Easy to add \"observer panels\" (e.g., debug overlays) without modifying existing code</li> </ul>"},{"location":"guides/patterns/#anti-pattern-to-avoid","title":"Anti-pattern to avoid","text":"C#<pre><code>// \u274c DON'T: Separate message per UI element (too granular)\n[DxUntargetedMessage] public struct HealthChanged { public int health; }\n[DxUntargetedMessage] public struct ManaChanged { public int mana; }\n[DxUntargetedMessage] public struct GoldChanged { public int gold; }\n// This creates 3x message traffic and registration overhead\n\n// \u2705 DO: Batch related updates into one message\n[DxUntargetedMessage] public struct PlayerStatsChanged {\n public int health;\n public int mana;\n public int gold;\n}\n</code></pre>"},{"location":"guides/patterns/#pattern-priority-ordered-execution-for-complex-systems","title":"Pattern: Priority-Ordered Execution for Complex Systems","text":"<p>Challenge: Ensure SaveSystem runs before SceneLoader, AudioSystem fades before UI transitions.</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct GameExit { }\n\npublic class SaveSystem : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n // Priority 0 = runs FIRST\n _ = Token.RegisterUntargeted<GameExit>(OnExit, priority: 0);\n }\n\n void OnExit(ref GameExit msg) {\n SaveGame(); // Must complete before audio fades\n }\n}\n\npublic class AudioSystem : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n // Priority 5 = runs AFTER SaveSystem\n _ = Token.RegisterUntargeted<GameExit>(OnExit, priority: 5);\n }\n\n void OnExit(ref GameExit msg) {\n FadeOutMusic(); // Runs after save\n }\n}\n\npublic class UISystem : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n // Priority 10 = runs LAST\n _ = Token.RegisterUntargeted<GameExit>(OnExit, priority: 10);\n }\n\n void OnExit(ref GameExit msg) {\n ShowExitAnimation(); // Runs after audio fade starts\n }\n}\n\n// Emit once, all systems execute in priority order\nvar msg = new GameExit();\nmsg.Emit();\n</code></pre> <p>Key insight: Lower priority numbers run first. Use priority to eliminate race conditions and ensure deterministic ordering.</p>"},{"location":"guides/patterns/#recommended-priority-ranges","title":"Recommended priority ranges","text":"<ul> <li>-100 to -50: Critical systems (save, validation)</li> <li>-10 to 0: Core gameplay logic</li> <li>0 to 10: UI updates</li> <li>10 to 20: Visual effects</li> <li>20+: Analytics, logging, non-critical</li> </ul>"},{"location":"guides/patterns/#pattern-efficient-interception-at-scale","title":"Pattern: Efficient Interception at Scale","text":"<p>Challenge: Validate 1000s of messages without duplicating logic.</p> C#<pre><code>// Single interceptor validates ALL damage\npublic class DamageValidator : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n // Interceptors run BEFORE handlers\n _ = Token.RegisterBroadcastInterceptor<EntityDamaged>(ValidateDamage, priority: -100);\n }\n\n bool ValidateDamage(ref InstanceId source, ref EntityDamaged msg) {\n // Validation logic runs once per message, not per handler\n if (msg.amount <= 0) return false; // Cancel invalid\n if (msg.amount > 9999) {\n msg = new EntityDamaged(9999, msg.damageType); // Clamp\n }\n return true; // Allow to proceed to handlers\n }\n}\n\n// Handlers can trust data is valid (no duplicate checks needed)\npublic class CombatEntity : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterComponentBroadcast<EntityDamaged>(this, OnDamaged);\n }\n\n void OnDamaged(ref EntityDamaged msg) {\n // No need to validate - interceptor already did it\n health -= msg.amount;\n }\n}\n</code></pre> <p>Scale benefit: Validation runs O(1) per message, not O(N) per handler. For 100 handlers, this saves 99 validation calls per message.</p>"},{"location":"guides/patterns/#pattern-post-processing-for-analytics-at-scale","title":"Pattern: Post-Processing for Analytics at Scale","text":"<p>Challenge: Track metrics without polluting gameplay code.</p> C#<pre><code>public class GameAnalytics : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n // Post-processors run AFTER all handlers\n _ = Token.RegisterBroadcastWithoutSourcePostProcessor<EntityDamaged>(LogDamage, priority: 100);\n _ = Token.RegisterUntargetedPostProcessor<LevelCompleted>(LogLevelComplete, priority: 100);\n }\n\n void LogDamage(InstanceId source, EntityDamaged msg) {\n // Analytics code is completely isolated from gameplay\n SendAnalyticsEvent(\"damage_dealt\", new {\n source = source.ToString(),\n amount = msg.amount,\n type = msg.damageType\n });\n }\n\n void LogLevelComplete(ref LevelCompleted msg) {\n SendAnalyticsEvent(\"level_complete\", new { level = msg.levelIndex });\n }\n}\n</code></pre> <p>Key insight: Post-processors let you add analytics, logging, and telemetry WITHOUT touching existing handlers. Add/remove analytics system without modifying game logic.</p>"},{"location":"guides/patterns/#performance-optimization-patterns-at-scale","title":"Performance Optimization Patterns at Scale","text":""},{"location":"guides/patterns/#optimization-1-disable-diagnostics-in-builds","title":"Optimization 1: Disable Diagnostics in Builds","text":"C#<pre><code>#if UNITY_EDITOR\nIMessageBus.GlobalDiagnosticsMode = true;\nIMessageBus.GlobalMessageBufferSize = 100; // Keep history for debugging\n#else\nIMessageBus.GlobalDiagnosticsMode = false; // Production builds\n#endif\n</code></pre> <p>Impact: Diagnostics adds ~5-10% overhead per message. Disable in production.</p>"},{"location":"guides/patterns/#optimization-2-use-specific-registrations-over-globalacceptall","title":"Optimization 2: Use Specific Registrations Over GlobalAcceptAll","text":"C#<pre><code>// \u274c SLOW: Receives ALL messages, even irrelevant ones\n_ = Token.RegisterGlobalAcceptAll(\n (ref IUntargetedMessage m) => { /* called for every untargeted */ },\n (ref InstanceId t, ref ITargetedMessage m) => { /* called for every targeted */ },\n (ref InstanceId s, ref IBroadcastMessage m) => { /* called for every broadcast */ }\n);\n\n// \u2705 FAST: Only receives relevant messages\n_ = Token.RegisterBroadcastWithoutSource<EntityDamaged>(OnDamage);\n_ = Token.RegisterUntargeted<LevelCompleted>(OnLevelComplete);\n</code></pre> <p>Impact: Specific registrations use type-indexed lookups (O(1)). GlobalAcceptAll checks every message (O(N)).</p>"},{"location":"guides/patterns/#optimization-3-batch-message-emissions","title":"Optimization 3: Batch Message Emissions","text":"C#<pre><code>// \u274c WASTEFUL: Emit after every tiny change\nvoid TakeDamage(int amount) {\n health -= amount;\n var msg = new HealthChanged(health);\n msg.Emit(); // Emits every frame\n}\n\n// \u2705 EFFICIENT: Batch updates, emit once per frame\nprivate bool healthDirty = false;\n\nvoid TakeDamage(int amount) {\n health -= amount;\n healthDirty = true;\n}\n\nvoid LateUpdate() {\n if (healthDirty) {\n var msg = new HealthChanged(health);\n msg.Emit(); // Emits once per frame max\n healthDirty = false;\n }\n}\n</code></pre> <p>Impact: Reduces message traffic by 10-100x in high-frequency scenarios.</p>"},{"location":"guides/patterns/#scaling-guidelines","title":"Scaling Guidelines","text":"Entity Count Message Rate Recommendations 1-10 Any Default settings fine 10-50 <1000/sec Disable diagnostics in builds 50-100 <5000/sec Batch emissions, avoid GlobalAcceptAll 100-500 <10k/sec Use local buses for subsystems, profile carefully 500+ <20k/sec Consider ECS or native code for hot paths <p>Rule of thumb: If you're emitting >20,000 messages/second, you're likely using DxMessaging for something better suited to ECS or direct method calls.</p>"},{"location":"guides/patterns/#real-world-production-example-battle-royale-game","title":"Real-World Production Example: Battle Royale Game","text":"<p>Scenario: 100 players, each with health/armor/weapons. UI needs to show:</p> <ul> <li>Your health/armor</li> <li>Teammate health (4 players)</li> <li>Kill feed (all 100 players)</li> <li>Match stats (damage dealt, kills, etc.)</li> </ul> C#<pre><code>// Messages\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerDamaged {\n public readonly int playerId;\n public readonly int newHealth;\n public readonly int newArmor;\n}\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerKilled {\n public readonly int victimId;\n public readonly int killerId;\n}\n\n// YOUR health bar (targeted observation)\npublic class SelfHealthUI : MessageAwareComponent {\n [SerializeField] private GameObject localPlayerObject;\n\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterGameObjectBroadcast<PlayerDamaged>(localPlayerObject, OnDamage);\n }\n\n void OnDamage(ref PlayerDamaged msg) => UpdateHealthBar(msg.newHealth, msg.newArmor);\n}\n\n// TEAMMATE health bars (selective observation)\npublic class TeamHealthUI : MessageAwareComponent {\n [SerializeField] private List<GameObject> teammateObjects;\n\n protected override void RegisterMessageHandlers() {\n foreach (var teammate in teammateObjects) {\n _ = Token.RegisterGameObjectBroadcast<PlayerDamaged>(teammate, OnTeammateDamage);\n }\n }\n\n void OnTeammateDamage(ref PlayerDamaged msg) => UpdateTeammateBar(msg.playerId, msg.newHealth);\n}\n\n// KILL FEED (global observation)\npublic class KillFeedUI : MessageAwareComponent {\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterBroadcastWithoutSource<PlayerKilled>(OnAnyKill);\n }\n\n void OnAnyKill(InstanceId source, PlayerKilled msg) {\n ShowKillFeedEntry(msg.killerId, msg.victimId);\n }\n}\n\n// MATCH STATS (analytics)\npublic class MatchStats : MessageAwareComponent {\n private Dictionary<int, int> damageByPlayer = new();\n\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterBroadcastWithoutSourcePostProcessor<PlayerDamaged>(TrackDamage);\n }\n\n void TrackDamage(InstanceId source, PlayerDamaged msg) {\n // Track for post-game stats\n }\n}\n</code></pre>"},{"location":"guides/patterns/#key-insights","title":"Key insights","text":"<ul> <li>Self health UI: 1 registration, receives ~10 messages/sec</li> <li>Team health UI: 4 registrations, receives ~40 messages/sec</li> <li>Kill feed UI: 1 registration, receives ALL kills (~5 messages/sec)</li> <li>Match stats: Post-processor, doesn't affect gameplay latency</li> </ul> <p>Total: ~100 players \u00d7 10 damage/sec = 1000 messages/sec. DxMessaging handles this with negligible overhead (~0.06ms/frame).</p> <p>Summary: DxMessaging scales from small prototypes to large production games. Use targeted observation for specific entities, global observation for analytics, and post-processors for metrics. Disable diagnostics in production and batch emissions for optimal performance.</p>"},{"location":"guides/patterns/#14-compatibility-with-scriptable-object-architecture-soa","title":"14) Compatibility with Scriptable Object Architecture (SOA)","text":"<p>Note: Scriptable Object Architecture (SOA) is a debated pattern in the Unity community. It has both proponents who value its designer-friendly workflow and critics who raise concerns about scalability and maintainability. See Anti-ScriptableObject Architecture for one perspective on the criticisms. Teams should evaluate SOA based on their specific needs. Alternatives include dependency injection (Zenject, VContainer), reactive systems (UniRx), or messaging systems (DxMessaging, MessagePipe).</p> <p>That said, if your project uses or requires SOA, DxMessaging can work alongside it.</p>"},{"location":"guides/patterns/#what-is-scriptable-object-architecture","title":"What is Scriptable Object Architecture?","text":"<p>SOA Background: Popularized by Ryan Hipple's Unite Austin 2017 talk, SOA uses ScriptableObject assets as:</p> <ol> <li>Shared Variables - <code>FloatVariable</code>, <code>IntVariable</code>, etc. (ScriptableObject assets that hold runtime state)</li> <li>Event Channels - <code>GameEvent</code> + <code>GameEventListener</code> pattern (designer-created events)</li> <li>Runtime Sets - Collections of active game objects (e.g., all enemies)</li> </ol> <p>Core idea: Systems communicate through serialized SO assets instead of direct references.</p>"},{"location":"guides/patterns/#key-resources","title":"Key resources","text":"<ul> <li>Unite 2017 Talk by Ryan Hipple</li> <li>Official Unity Guide</li> <li>Reference Implementation</li> <li>Community Package</li> </ul>"},{"location":"guides/patterns/#why-soa-is-controversial","title":"Why SOA is Controversial","text":"<p>From Anti-ScriptableObject Architecture, key criticisms include:</p> <ol> <li>Wrong Purpose - ScriptableObjects are designed for immutable design data, not runtime mutable state</li> <li>Redundant Complexity - Standard C# objects achieve the same goals without SO restrictions</li> <li>Inspector Dependency - Binds architecture to Unity's GUI, complicating debugging and maintenance</li> <li>Limited Scalability - Runtime-created variables undermine the pattern; managing numerous assets becomes unwieldy</li> <li>Domain Reload Issues - Disabled domain reloading causes ScriptableObjects to retain values unpredictably</li> <li>Testability Concerns - SO assets persist between tests, requiring manual cleanup</li> </ol>"},{"location":"guides/patterns/#recommended-alternatives","title":"Recommended alternatives","text":"<ul> <li>Dependency Injection - Zenject, VContainer, Reflex (see DxMessaging DI Integrations)</li> <li>Reactive Systems - UniRx, UniTask</li> <li>Messaging - DxMessaging (this framework), MessagePipe</li> <li>Configuration Data - Spreadsheet-based solutions (e.g., BakingSheet)</li> </ul> <p>Use ScriptableObjects for their intended purpose: immutable design-time data (configs, balance tables, prefab references).</p>"},{"location":"guides/patterns/#can-dxmessaging-work-with-soa","title":"Can DxMessaging Work with SOA?","text":"<p>Yes, but with caveats. DxMessaging and SOA solve similar problems (decoupling, communication) with different philosophies:</p> Aspect SOA DxMessaging Paradigm Asset-based, persistent state Runtime message passing, transient Designer-Centric \u2705 High (create events in Inspector) \u274c Low (code-driven) Type Safety \u26a0\ufe0f Mixed (SO refs typed, but UnityEvent inspector wiring loses compile-time safety) \u2705 Strong (compile-time validation) Lifecycle \u26a0\ufe0f Manual (SO assets persist) \u2705 Automatic (tokens clean up) Debugging \u26a0\ufe0f Inspector-dependent \u2705 Built-in diagnostics Performance \u26a0\ufe0f List iteration, UnityAction overhead \u2705 Zero-allocation structs Use Case Shared state, designer-driven configs Event-driven communication, runtime logic Testability \u26a0\ufe0f Requires SO asset cleanup \u2705 Isolated buses per test <p>Summary: For new projects, evaluate DxMessaging, DI frameworks, or other messaging approaches based on your needs. If you have existing SOA code, the patterns below show coexistence strategies.</p>"},{"location":"guides/patterns/#pattern-overview","title":"Pattern Overview","text":"Pattern What it shows When to use SOA involvement A SOA Events (GameEvent) forwarding to DxMessaging Designer-created event assets, modern code downstream \u2705 Yes - SOA Event pattern B ScriptableObjects for configs + DxMessaging for events New projects / best practice \u274c No - proper SO usage only"},{"location":"guides/patterns/#pattern-a-soa--dxmessaging-event-forwarding","title":"Pattern A: SOA \u2192 DxMessaging (Event Forwarding)","text":"<p>Use case: Designer-created SOA events, but you want DxMessaging benefits downstream.</p> <p>Strategy: SOA GameEventListener forwards to DxMessaging.</p> <p>This uses the SOA GameEvent pattern: If you're NOT using designer-created GameEvent assets with <code>Raise()</code>/listener management, you don't need this pattern. For immutable config data, see Pattern B instead.</p>"},{"location":"guides/patterns/#example-scene-transitions","title":"Example: Scene Transitions","text":"C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Core.Extensions;\nusing UnityEngine;\nusing UnityEngine.Events;\n\n// Traditional SOA event\n[CreateAssetMenu(menuName = \"Events/Game Event\")]\npublic class GameEvent : ScriptableObject\n{\n private readonly List<UnityAction> listeners = new();\n\n public void Raise()\n {\n for (int i = listeners.Count - 1; i >= 0; i--)\n listeners[i]?.Invoke();\n }\n\n public void RegisterListener(UnityAction listener) => listeners.Add(listener);\n public void UnregisterListener(UnityAction listener) => listeners.Remove(listener);\n}\n\n// DxMessaging message (modern, type-safe)\n[DxUntargetedMessage]\npublic readonly partial struct SceneTransitionRequested { }\n\n// Bridge: SOA Event \u2192 DxMessaging\npublic class SOAEventBridge : MonoBehaviour\n{\n [SerializeField] private GameEvent onSceneTransitionSO; // Designer-created asset\n\n void OnEnable()\n {\n onSceneTransitionSO.RegisterListener(OnSOAEvent);\n }\n\n void OnDisable()\n {\n onSceneTransitionSO.UnregisterListener(OnSOAEvent);\n }\n\n void OnSOAEvent()\n {\n // Forward to DxMessaging\n var message = new SceneTransitionRequested();\n message.Emit();\n }\n}\n\n// Modern DxMessaging listeners (no SO dependency)\npublic class AudioSystem : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<SceneTransitionRequested>(OnTransition);\n }\n\n void OnTransition(ref SceneTransitionRequested msg)\n {\n FadeOutMusic(); // Type-safe, debuggable via DxMessaging Inspector\n }\n}\n</code></pre>"},{"location":"guides/patterns/#benefits","title":"Benefits","text":"<ul> <li>\u2705 Designers create events in Inspector (SOA workflow preserved)</li> <li>\u2705 Code uses DxMessaging (type-safe, lifecycle-safe)</li> </ul>"},{"location":"guides/patterns/#drawbacks","title":"Drawbacks","text":"<ul> <li>\u26a0\ufe0f Bridge boilerplate for each SOA event</li> <li>\u26a0\ufe0f Double registration (SOA listener + DxMessaging handler)</li> </ul>"},{"location":"guides/patterns/#pattern-b-proper-scriptableobject-usage-recommended","title":"Pattern B: Proper ScriptableObject Usage (Recommended)","text":"<p>Use case: ScriptableObjects for immutable config data, DxMessaging for runtime events.</p> <p>Strategy: Use each tool for its intended purpose; avoid bridging.</p> <p>Important: This pattern uses ScriptableObjects CORRECTLY (immutable design data), NOT as SOA (mutable runtime state). This is standard Unity best practice, not SOA. If you're only using SOs for config data like this, you don't need SOA patterns at all.</p>"},{"location":"guides/patterns/#example-combat-with-designer-tunable-data","title":"Example: Combat with Designer-Tunable Data","text":"C#<pre><code>using DxMessaging.Core.Messages;\nusing DxMessaging.Core.Attributes;\nusing DxMessaging.Unity;\nusing UnityEngine;\n\n// ScriptableObject: Designer-tunable balance data (immutable at runtime)\n// This is CORRECT SO usage, NOT SOA\n[CreateAssetMenu(menuName = \"Config/Weapon Stats\")]\npublic class WeaponStats : ScriptableObject\n{\n public int baseDamage = 10; // Designer tweaks in Inspector\n public float critMultiplier = 2f;\n}\n\n// DxMessaging: Runtime event (code-driven)\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct DamageDealt\n{\n public readonly int amount;\n public readonly bool wasCrit;\n}\n\n// Combat system: Reads immutable SO data, emits DxMessaging events\npublic class Weapon : MessageAwareComponent\n{\n [SerializeField] private WeaponStats stats; // Immutable config (correct SO usage)\n\n public void Fire()\n {\n bool crit = Random.value < 0.1f;\n int damage = Mathf.RoundToInt(stats.baseDamage * (crit ? stats.critMultiplier : 1f));\n\n var msg = new DamageDealt(damage, crit);\n msg.EmitComponentBroadcast(this); // Runtime event via DxMessaging\n }\n}\n\n// Analytics: Pure DxMessaging (no SOA dependency)\npublic class CombatAnalytics : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterBroadcastWithoutSource<DamageDealt>(OnDamage);\n }\n\n void OnDamage(InstanceId source, DamageDealt msg)\n {\n Debug.Log($\"{source} dealt {msg.amount} damage (crit: {msg.wasCrit})\");\n }\n}\n</code></pre>"},{"location":"guides/patterns/#benefits_1","title":"Benefits","text":"<ul> <li>\u2705 Best of both worlds - ScriptableObjects for static configs, DxMessaging for runtime events</li> <li>\u2705 No bridging overhead</li> <li>\u2705 Uses each system correctly: SOs for their intended purpose (immutable design data), messaging for runtime communication</li> <li>\u2705 This is NOT SOA - it's proper Unity architecture</li> </ul>"},{"location":"guides/patterns/#this-pattern-separates-concerns-clearly","title":"This pattern separates concerns clearly","text":""},{"location":"guides/patterns/#when-to-use-each-pattern","title":"When to Use Each Pattern","text":"Pattern Use When Complexity Performance A: SOA \u2192 DxMessaging Designers create SOA events, modern code uses messaging Medium \u26a0\ufe0f Medium B: Proper SO Usage Immutable configs only, messaging for events Low \u2705 Good None (Pure DxMessaging) Greenfield project or full SOA migration Lowest \u2705 Best"},{"location":"guides/patterns/#migration-path-soa--dxmessaging","title":"Migration Path: SOA \u2192 DxMessaging","text":"<p>If you're moving away from SOA:</p> <ol> <li>Phase 1: Identify SOA event usage (GameEvent/GameEventListener patterns)</li> <li>Phase 2: Create equivalent DxMessaging messages (Untargeted/Broadcast)</li> <li>Phase 3: Add bridges (Pattern A or B) to maintain compatibility</li> <li>Phase 4: Migrate listeners to DxMessaging incrementally</li> <li>Phase 5: Remove bridges and SOA assets once all references gone</li> </ol> <p>For SOA variables:</p> <ol> <li>Convert read-only SO configs \u2192 Keep as-is (correct SO usage) or move to JSON/ScriptableObjects for data</li> <li>Convert mutable SO variables \u2192 DxMessaging messages or DI-injected services</li> <li>Convert RuntimeSets \u2192 DxMessaging global observers (<code>RegisterBroadcastWithoutSource</code>)</li> </ol>"},{"location":"guides/patterns/#final-recommendations","title":"Final Recommendations","text":""},{"location":"guides/patterns/#if-youre-using-soa","title":"If you're using SOA","text":"<ul> <li>\u2705 Do: Use Pattern B (Proper SO Usage) - SOs for immutable configs ONLY, DxMessaging for runtime events</li> <li>\u2705 Do: Use Pattern A to bridge existing SOA GameEvent assets to DxMessaging during migration</li> <li>\u2705 Do: Read Anti-ScriptableObject Architecture to understand risks</li> <li>\u2705 Do: Consider gradual migration to DxMessaging or DI frameworks</li> <li>\u274c Don't: Use SOs for mutable runtime state (health, scores, etc.)</li> <li>\u274c Don't: Create new SOA event assets\u2014use DxMessaging messages instead</li> </ul>"},{"location":"guides/patterns/#if-youre-starting-fresh","title":"If you're starting fresh","text":"<ul> <li>\u2705 Do: Use DxMessaging for all messaging/events</li> <li>\u2705 Do: Use ScriptableObjects ONLY for immutable design data (weapon stats, level configs)</li> <li>\u2705 Do: Consider DI frameworks (Zenject/VContainer) for service dependencies</li> <li>\u274c Don't: Adopt SOA's GameEvent/Variable patterns\u2014they're superseded by better tools</li> </ul>"},{"location":"guides/patterns/#resources","title":"Resources","text":"<ul> <li>Anti-ScriptableObject Architecture - Detailed critique</li> <li>Ryan Hipple Unite 2017 Talk - Original SOA presentation</li> <li>Unity Official Guide - Unity's perspective</li> <li>DxMessaging DI Integrations - Better alternatives for dependency management</li> <li>Zenject - Recommended DI framework</li> <li>VContainer - Lightweight DI alternative</li> </ul>"},{"location":"guides/patterns/#related-documentation","title":"Related Documentation","text":""},{"location":"guides/patterns/#learn-the-basics-first","title":"Learn the Basics First?","text":"<ul> <li>\u2192 Getting Started (10 min) \u2014 Complete introduction</li> <li>\u2192 Message Types (10 min) \u2014 When to use what</li> <li>\u2192 Visual Guide (5 min) \u2014 Beginner-friendly pictures</li> </ul>"},{"location":"guides/patterns/#try-real-examples","title":"Try Real Examples","text":"<ul> <li>\u2192 Mini Combat sample \u2014 Working combat example</li> <li>\u2192 UI Buttons + Inspector sample \u2014 Interactive diagnostics</li> <li>\u2192 End-to-End Example \u2014 Complete feature walkthrough</li> </ul>"},{"location":"guides/patterns/#deep-dives","title":"Deep Dives","text":"<ul> <li>\u2192 Interceptors & Ordering \u2014 Control execution flow</li> <li>\u2192 Design & Architecture \u2014 Internals and optimizations</li> <li>\u2192 Performance \u2014 Benchmarks and tuning</li> </ul>"},{"location":"guides/patterns/#reference","title":"Reference","text":"<ul> <li>\u2192 Quick Reference \u2014 Cheat sheet</li> <li>\u2192 API Reference \u2014 Complete API</li> </ul>"},{"location":"guides/testing/","title":"Testing with DxMessaging","text":"<p><- Back to Index | Patterns | Runtime Configuration</p> <p>You're here because: You want to write reliable, isolated tests for systems that use DxMessaging.</p>"},{"location":"guides/testing/#key-testing-principles","title":"Key Testing Principles","text":"<ol> <li>Use isolated MessageBus instances - Avoid global state pollution between tests</li> <li>Manage token lifecycles explicitly - Enable/disable tokens to control when handlers are active</li> <li>Clean up spawned objects - Destroy test GameObjects to prevent registration leaks</li> </ol>"},{"location":"guides/testing/#1-isolated-messagebus-for-unit-tests","title":"1) Isolated MessageBus for Unit Tests","text":"<p>The global <code>MessageHandler.MessageBus</code> is shared across your application. For unit tests, create a dedicated <code>MessageBus</code> instance to ensure complete isolation.</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing NUnit.Framework;\nusing UnityEngine;\n\n[Test]\npublic void IsolatedBusDoesNotAffectGlobalBus()\n{\n // Create isolated bus and handler\n MessageBus testBus = new();\n GameObject testObject = new(\"TestObject\");\n MessageHandler handler = new(testObject) { active = true };\n MessageRegistrationToken token = MessageRegistrationToken.Create(handler, testBus);\n token.Enable();\n\n int localCount = 0;\n _ = token.RegisterUntargeted<MyMessage>(_ => localCount++);\n\n // Emit to isolated bus only\n MyMessage msg = new();\n msg.EmitUntargeted(testBus);\n\n Assert.AreEqual(1, localCount, \"Local handler should receive message.\");\n\n // Cleanup\n token.UnregisterAll();\n Object.DestroyImmediate(testObject);\n}\n</code></pre>"},{"location":"guides/testing/#2-test-base-class-pattern","title":"2) Test Base Class Pattern","text":"<p>Create a reusable base class for common setup/teardown.</p> C#<pre><code>public abstract class MessagingTestBase\n{\n protected readonly List<GameObject> _spawned = new();\n\n [TearDown]\n public virtual void Cleanup()\n {\n foreach (GameObject spawned in _spawned)\n if (spawned != null) Object.Destroy(spawned);\n _spawned.Clear();\n }\n\n [UnitySetUp]\n public virtual IEnumerator UnitySetup()\n {\n IMessageBus bus = MessageHandler.MessageBus;\n while (bus.RegisteredUntargeted != 0 || bus.RegisteredTargeted != 0)\n yield return null;\n }\n\n protected MessageRegistrationToken GetToken(MessageAwareComponent c) => c.Token;\n}\n</code></pre>"},{"location":"guides/testing/#3-verifying-message-emission-and-handling","title":"3) Verifying Message Emission and Handling","text":"<p>Use counters or captured values to verify messages are emitted and handled correctly.</p> C#<pre><code>[UnityTest]\npublic IEnumerator TargetedMessageReachesCorrectTarget()\n{\n GameObject target = new(\"Target\", typeof(EmptyMessageAwareComponent));\n GameObject nonTarget = new(\"NonTarget\", typeof(EmptyMessageAwareComponent));\n _spawned.Add(target);\n _spawned.Add(nonTarget);\n\n MessageRegistrationToken targetToken = GetToken(target.GetComponent<EmptyMessageAwareComponent>());\n MessageRegistrationToken nonTargetToken = GetToken(nonTarget.GetComponent<EmptyMessageAwareComponent>());\n\n int targetReceived = 0;\n int nonTargetReceived = 0;\n\n _ = targetToken.RegisterGameObjectTargeted<DamageMessage>(target, _ => targetReceived++);\n _ = nonTargetToken.RegisterGameObjectTargeted<DamageMessage>(nonTarget, _ => nonTargetReceived++);\n\n DamageMessage msg = new() { amount = 10 };\n msg.EmitGameObjectTargeted(target);\n\n Assert.AreEqual(1, targetReceived, \"Target should receive message.\");\n Assert.AreEqual(0, nonTargetReceived, \"Non-target should NOT receive message.\");\n yield break;\n}\n</code></pre>"},{"location":"guides/testing/#4-testing-interceptors","title":"4) Testing Interceptors","text":"<p>Interceptors can cancel or modify messages. Test both scenarios.</p> C#<pre><code>[UnityTest]\npublic IEnumerator InterceptorCanCancelMessage()\n{\n GameObject host = new(\"Host\", typeof(EmptyMessageAwareComponent));\n _spawned.Add(host);\n MessageRegistrationToken token = GetToken(host.GetComponent<EmptyMessageAwareComponent>());\n\n int handlerCalled = 0;\n _ = token.RegisterUntargeted<TestMessage>(_ => handlerCalled++);\n _ = token.RegisterUntargetedInterceptor((ref TestMessage _) => false); // Cancel\n\n TestMessage msg = new();\n msg.EmitUntargeted();\n\n Assert.AreEqual(0, handlerCalled, \"Handler must not run when interceptor cancels.\");\n yield break;\n}\n\n[UnityTest]\npublic IEnumerator InterceptorCanModifyMessage()\n{\n GameObject host = new(\"Host\", typeof(EmptyMessageAwareComponent));\n _spawned.Add(host);\n MessageRegistrationToken token = GetToken(host.GetComponent<EmptyMessageAwareComponent>());\n\n int receivedAmount = 0;\n _ = token.RegisterUntargeted<DamageMessage>(msg => receivedAmount = msg.amount);\n _ = token.RegisterUntargetedInterceptor((ref DamageMessage msg) =>\n {\n if (msg.amount > 100) msg = new DamageMessage { amount = 100 };\n return true;\n });\n\n DamageMessage msg = new() { amount = 999 };\n msg.EmitUntargeted();\n\n Assert.AreEqual(100, receivedAmount, \"Interceptor should clamp damage to 100.\");\n yield break;\n}\n</code></pre>"},{"location":"guides/testing/#5-testing-component-lifecycle","title":"5) Testing Component Lifecycle","text":"<p>Verify handlers respect Unity's enable/disable lifecycle.</p> C#<pre><code>[UnityTest]\npublic IEnumerator DisabledComponentDoesNotReceiveMessages()\n{\n GameObject host = new(\"Host\", typeof(EmptyMessageAwareComponent));\n _spawned.Add(host);\n EmptyMessageAwareComponent component = host.GetComponent<EmptyMessageAwareComponent>();\n MessageRegistrationToken token = GetToken(component);\n\n int count = 0;\n _ = token.RegisterUntargeted<TestMessage>(_ => count++);\n\n TestMessage msg1 = new();\n msg1.EmitUntargeted();\n Assert.AreEqual(1, count, \"Enabled component should receive message.\");\n\n component.enabled = false;\n yield return null;\n\n TestMessage msg2 = new();\n msg2.EmitUntargeted();\n Assert.AreEqual(1, count, \"Disabled component should NOT receive message.\");\n\n component.enabled = true;\n yield return null;\n\n TestMessage msg3 = new();\n msg3.EmitUntargeted();\n Assert.AreEqual(2, count, \"Re-enabled component should receive message.\");\n}\n</code></pre>"},{"location":"guides/testing/#6-integration-testing","title":"6) Integration Testing","text":"<p>For integration tests, use the global bus with proper cleanup. Create GameObjects with components, emit messages, and verify system-wide behavior across multiple components.</p>"},{"location":"guides/testing/#best-practices","title":"Best Practices","text":"Practice Why Use isolated <code>MessageBus</code> for unit tests Prevents test pollution and flakiness Track spawned objects in a list Ensures cleanup even if test fails Use <code>yield return null</code> after state changes Allows Unity lifecycle methods to execute Test both positive and negative cases Verify messages reach correct targets AND miss incorrect ones Use expressive assertion messages Makes test failures easier to diagnose"},{"location":"guides/testing/#quick-reference-test-message-setup","title":"Quick Reference: Test Message Setup","text":"C#<pre><code>// Test messages\n[DxUntargetedMessage]\npublic partial struct TestMessage { }\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct DamageMessage { public readonly int amount; }\n\n// Minimal test component - registers no handlers by default\npublic sealed class EmptyMessageAwareComponent : MessageAwareComponent { }\n</code></pre>"},{"location":"guides/testing/#see-also","title":"See Also","text":"<ul> <li>Patterns - Pattern #6 (Local Bus Islands) and #12 (Testing)</li> <li>Runtime Configuration - MessageBus configuration options</li> <li>Back to Documentation Hub - Browse all docs</li> </ul>"},{"location":"guides/unity-integration/","title":"Unity Integration","text":"<p>Unity\u2011centric helpers make registration lifecycles explicit and safe.</p>"},{"location":"guides/unity-integration/#messagingcomponent","title":"MessagingComponent","text":"<ul> <li>Attach to any GameObject that will send or receive messages.</li> <li>Creates a per-owner <code>MessageHandler</code> and offers <code>Create(this)</code> to get a <code>MessageRegistrationToken</code>.</li> <li>Call <code>Configure(IMessageBus, MessageBusRebindMode)</code> before <code>Create</code> if you want the component to use a custom bus (e.g., one resolved from a DI container). Passing <code>MessageBusRebindMode.RebindActive</code> migrates current registrations; <code>PreserveRegistrations</code> defers the swap until the next enable.</li> <li>Optional: set <code>emitMessagesWhenDisabled</code> if you need to emit while disabled.</li> </ul>"},{"location":"guides/unity-integration/#messageawarecomponent","title":"MessageAwareComponent","text":"<ul> <li>Derive for a batteries\u2011included pattern; it manages a token for you.</li> <li>Override <code>RegisterMessageHandlers()</code> to stage registrations.</li> <li>The token is enabled/disabled with the component\u2019s enable state.</li> <li>Call <code>ConfigureMessageBus(IMessageBus, MessageBusRebindMode)</code> before <code>base.Awake()</code> (or shortly after via a DI bootstrapper) to ensure the token is created against your container-provided bus.</li> </ul> C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core.Messages;\n\npublic sealed class HealthComponent : MessageAwareComponent\n{\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterComponentTargeted<TookDamage>(this, OnTookDamage);\n _ = Token.RegisterUntargeted<WorldRegenerated>(OnWorldRegenerated);\n }\n\n private void OnTookDamage(ref TookDamage m) => Apply(m.amount);\n private void OnWorldRegenerated(ref WorldRegenerated m) => Reset();\n}\n</code></pre>"},{"location":"guides/unity-integration/#dos","title":"Do's","text":"<ul> <li>Use <code>MessageAwareComponent</code> when possible to avoid boilerplate.</li> <li>Override <code>RegisterMessageHandlers()</code> and bind to named methods.</li> <li>Keep handlers small and fast; offload heavy work.</li> </ul>"},{"location":"guides/unity-integration/#donts","title":"Don'ts","text":"<ul> <li>Don\u2019t register in Update; register once and enable/disable with component state.</li> <li>Don\u2019t forget to call <code>base.RegisterMessageHandlers()</code> if your subclass relies on base registrations.</li> </ul>"},{"location":"guides/unity-integration/#important-inheritance-and-base-calls","title":"Important: Inheritance and base calls","text":"<ul> <li><code>MessageAwareComponent</code> uses many virtual methods (e.g., <code>Awake</code>, <code>OnEnable</code>, <code>OnDisable</code>, <code>RegisterMessageHandlers</code>).</li> <li>CRITICAL: If you override any of these, you MUST call the base method: <code>base.Awake()</code>, <code>base.OnEnable()</code>, <code>base.OnDisable()</code>, <code>base.RegisterMessageHandlers()</code>.</li> <li>Always call <code>base.RegisterMessageHandlers()</code> first in your override\u2014this ensures parent class registrations happen before yours.</li> <li>Skipping base calls can break core setup (token creation/enable) and default string\u2011message registrations.</li> <li>If you need to opt out of string demos, prefer overriding <code>RegisterForStringMessages => false</code> rather than removing the base call.</li> <li>Don't hide Unity methods with <code>new</code> (e.g., <code>new void OnEnable()</code>); always <code>override</code> and call <code>base.*</code>.</li> </ul>"},{"location":"guides/unity-integration/#registration-timing","title":"Registration timing","text":"<ul> <li>Prefer <code>Awake()</code> for registration rather than <code>Start()</code>.</li> <li><code>MessageAwareComponent</code> automatically calls <code>RegisterMessageHandlers()</code> in <code>Awake()</code>.</li> <li>Early registration in <code>Awake()</code> ensures handlers are ready before other components' <code>Start()</code> methods run.</li> <li>If you need custom setup before registration, override <code>Awake()</code>, do your setup, then call <code>base.Awake()</code>.</li> </ul>"},{"location":"guides/unity-integration/#manual-token-management","title":"Manual token management","text":"C#<pre><code>using DxMessaging.Unity;\nusing DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class InventoryUI : UnityEngine.MonoBehaviour\n{\n private MessagingComponent _messaging;\n private MessageRegistrationToken _token;\n\n private void Awake()\n {\n _messaging = GetComponent<MessagingComponent>();\n _token = _messaging.Create(this);\n _ = _token.RegisterUntargeted<WorldRegenerated>(OnWorld);\n _ = _token.RegisterComponentTargeted<TookDamage>(this, OnDamage);\n }\n\n private void OnEnable() => _token.Enable();\n private void OnDisable() => _token.Disable();\n\n private void OnWorld(ref WorldRegenerated m) { /* update UI */ }\n private void OnDamage(ref TookDamage m) { /* flash damage */ }\n}\n</code></pre>"},{"location":"guides/unity-integration/#manual-enabledisable-advanced","title":"Manual enable/disable (advanced)","text":"C#<pre><code>public sealed class AlwaysListening : MessageAwareComponent\n{\n protected override bool MessageRegistrationTiedToEnableStatus => false; // keep token enabled\n\n protected override void RegisterMessageHandlers()\n {\n base.RegisterMessageHandlers();\n _ = Token.RegisterUntargeted<MyEvent>(OnEvent);\n Token.Enable(); // explicitly enable once\n }\n\n private void OnEvent(ref MyEvent m) { /* ... */ }\n}\n</code></pre>"},{"location":"guides/unity-integration/#string-message-demos-optout","title":"String message demos (opt\u2011out)","text":"C#<pre><code>public sealed class NoStringDemos : MessageAwareComponent\n{\n protected override bool RegisterForStringMessages => false;\n\n protected override void RegisterMessageHandlers()\n {\n // only your registrations\n }\n}\n</code></pre>"},{"location":"guides/unity-integration/#reflexivemessage-bridging-legacy-sendmessage","title":"ReflexiveMessage (bridging legacy SendMessage)","text":"C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.Messages;\n\nvar msg = new ReflexiveMessage(\"OnHit\", ReflexiveSendMode.Upwards, 10);\nmsg.EmitGameObjectTargeted(gameObject);\n</code></pre> <p>Related</p> <ul> <li>Quick Start</li> <li>Patterns</li> <li>Diagnostics (Inspector)</li> </ul>"},{"location":"integrations/","title":"DI Framework Integrations","text":"<p>DxMessaging integrates seamlessly with popular Unity dependency injection (DI) frameworks. This section covers how to combine DxMessaging's reactive event system with constructor-based dependency injection for a powerful, scalable architecture.</p>"},{"location":"integrations/#why-combine-di--messaging","title":"Why Combine DI + Messaging?","text":"<p>Use the best of both worlds:</p> <ul> <li>Constructor Injection \u2014 for service dependencies (repositories, managers, configuration)</li> <li>Messaging \u2014 for reactive events (damage taken, item collected, game state changes)</li> </ul> <p>This combination provides:</p> <ul> <li>Explicit dependencies with testable constructors</li> <li>Loose coupling between systems via events</li> <li>Scoped message buses for scene isolation</li> <li>Clean separation of concerns</li> </ul>"},{"location":"integrations/#supported-frameworks","title":"Supported Frameworks","text":""},{"location":"integrations/#vcontainer","title":"VContainer","text":"<p>Fast, lightweight DI with minimal overhead. Features scoped lifetimes for per-scene message buses.</p> C#<pre><code>builder.Register<IMessageBus>(Lifetime.Scoped)\n .AsImplementedInterfaces()\n .AsSelf();\n</code></pre>"},{"location":"integrations/#zenject","title":"Zenject","text":"<p>Feature-rich DI with extensive Unity integration. Supports complex binding scenarios and sub-containers.</p> C#<pre><code>Container.Bind<IMessageBus>()\n .FromInstance(MessageHandler.MessageBus)\n .AsSingle();\n</code></pre>"},{"location":"integrations/#reflex","title":"Reflex","text":"<p>Modern, reflection-free DI designed for performance. Zero-allocation resolution with AOT support.</p> C#<pre><code>Container.Singleton<IMessageBus>(MessageHandler.MessageBus);\n</code></pre>"},{"location":"integrations/#choosing-a-framework","title":"Choosing a Framework","text":"Framework Best For Performance VContainer Most projects, additive scenes \u2b50\u2b50\u2b50 Fast Zenject Complex projects, existing Zenject codebases \u2b50\u2b50 Good Reflex Performance-critical, AOT platforms \u2b50\u2b50\u2b50 Fastest"},{"location":"integrations/#common-patterns","title":"Common Patterns","text":"<p>All frameworks support the same core patterns:</p> <ol> <li>Global Bus \u2014 Share <code>MessageHandler.MessageBus</code> across all systems</li> <li>Scoped Bus \u2014 Create per-scene buses for isolation</li> <li>Builder Injection \u2014 Inject <code>IMessageRegistrationBuilder</code> for flexible handler registration</li> <li>Testable Design \u2014 Mock <code>IMessageBus</code> in unit tests</li> </ol> <p>See each framework's guide for detailed examples and best practices.</p>"},{"location":"integrations/reflex/","title":"DxMessaging + Reflex","text":"<p>\u2190 Back to Integrations Overview</p>"},{"location":"integrations/reflex/#overview","title":"Overview","text":"<p>Reflex is a minimal, lightweight dependency injection framework for Unity. DxMessaging integrates with Reflex, allowing you to:</p> <ul> <li>Inject <code>IMessageBus</code> in any class with minimal overhead</li> <li>Use DI for construction + DxMessaging for events (best of both worlds)</li> <li>Minimal API surface \u2014 small number of concepts to understand</li> <li>Compatible \u2014 Reflex and DxMessaging can be used together</li> </ul> <p>Why combine DI + Messaging? Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected), combining both approaches.</p>"},{"location":"integrations/reflex/#quick-start","title":"Quick Start","text":""},{"location":"integrations/reflex/#prerequisites","title":"Prerequisites","text":"<ul> <li>DxMessaging installed via UPM</li> <li>Reflex installed (<code>gustavopsantos/Reflex</code>) via source or UPM</li> </ul>"},{"location":"integrations/reflex/#1-create-a-reflex-installer","title":"1. Create a Reflex Installer","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity.Integrations.Reflex;\nusing Reflex.Core;\n\n// Option A: Use IInstaller interface (recommended for explicit registration)\npublic sealed class DxMessagingInstaller : IInstaller\n{\n public void InstallBindings(ContainerBuilder containerBuilder)\n {\n // Bind MessageBus as singleton implementing IMessageBus\n containerBuilder.AddSingleton(\n typeof(MessageBus),\n typeof(MessageBus),\n typeof(IMessageBus)\n );\n\n // Optional: Enable automatic IMessageRegistrationBuilder binding\n // Install the DxMessagingRegistrationInstaller to get IMessageRegistrationBuilder\n #if REFLEX_PRESENT\n new DxMessagingRegistrationInstaller().InstallBindings(containerBuilder);\n #endif\n }\n}\n\n// Option B: Extend Installer base class (common pattern)\npublic sealed class DxMessagingInstallerAlt : Installer\n{\n protected override void InstallBindings()\n {\n Container.Bind<MessageBus>().AsSingleton();\n Container.Bind<IMessageBus>().FromContainer<MessageBus>();\n }\n}\n</code></pre> <p>Note: You must import the <code>DxMessaging.Unity.Integrations.Reflex</code> namespace to access <code>DxMessagingRegistrationInstaller</code>.</p>"},{"location":"integrations/reflex/#add-to-your-scene","title":"Add to your scene","text":"<ol> <li>Create a <code>SceneContext</code> or <code>ProjectContext</code> in your scene</li> <li>Add <code>DxMessagingInstaller</code> to the installers list</li> <li>Reflex will now inject <code>IMessageBus</code> automatically</li> </ol>"},{"location":"integrations/reflex/#usage-patterns","title":"Usage Patterns","text":""},{"location":"integrations/reflex/#pattern-1-inject-into-plain-classes-recommended-for-services","title":"Pattern 1: Inject into Plain Classes (Recommended for Services)","text":"<p>Use <code>IMessageRegistrationBuilder</code> to create message handlers in non-MonoBehaviour classes:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Core.Attributes;\n\n// Define a message\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerDamaged\n{\n public readonly int damage;\n}\n\n// Service that listens to messages\npublic sealed class DamageService\n{\n private readonly MessageRegistrationLease _lease;\n\n // Builder is injected automatically when using the installer\n public DamageService(IMessageRegistrationBuilder registrationBuilder)\n {\n var options = new MessageRegistrationBuildOptions\n {\n Configure = token =>\n {\n _ = token.RegisterUntargeted<PlayerDamaged>(OnPlayerDamaged);\n }\n };\n\n _lease = registrationBuilder.Build(options);\n }\n\n public void Initialize()\n {\n _lease.Activate(); // Start listening\n }\n\n public void Dispose()\n {\n _lease.Dispose(); // Clean up\n }\n\n private static void OnPlayerDamaged(ref PlayerDamaged message)\n {\n UnityEngine.Debug.Log($\"Player took {message.damage} damage!\");\n }\n}\n</code></pre>"},{"location":"integrations/reflex/#register-the-service-in-your-installer","title":"Register the service in your installer","text":"C#<pre><code>// Using IInstaller interface\npublic void InstallBindings(ContainerBuilder containerBuilder)\n{\n containerBuilder.AddSingleton(typeof(DamageService), typeof(DamageService));\n // Call Initialize() from a bootstrap MonoBehaviour\n}\n\n// Or using Installer base class\nprotected override void InstallBindings()\n{\n Container.Bind<DamageService>().AsSingleton();\n // Call Initialize() from a bootstrap MonoBehaviour\n}\n</code></pre> <p>Note: Reflex doesn't have lifecycle interfaces like <code>IInitializable</code>. Call <code>Initialize()</code> and <code>Dispose()</code> manually from a controlling MonoBehaviour or bootstrap script.</p>"},{"location":"integrations/reflex/#pattern-2-configure-messagingcomponents-for-existing-monobehaviours","title":"Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing Reflex.Attributes;\nusing UnityEngine;\n\n[DisallowMultipleComponent]\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class MessagingComponentConfigurator : MonoBehaviour\n{\n [Inject]\n private IMessageBus _messageBus;\n\n private void Awake()\n {\n GetComponent<MessagingComponent>().Configure(\n _messageBus,\n MessageBusRebindMode.RebindActive\n );\n }\n}\n</code></pre>"},{"location":"integrations/reflex/#usage","title":"Usage","text":"<ol> <li>Add <code>MessagingComponentConfigurator</code> alongside any <code>MessagingComponent</code> in your prefabs</li> <li>Reflex will inject the bus in <code>Awake()</code> before handlers are registered</li> <li>Your message handlers now use the container-managed bus</li> </ol>"},{"location":"integrations/reflex/#pattern-3-inject-imessagebus-directly","title":"Pattern 3: Inject IMessageBus Directly","text":"<p>For simple emission without listening, inject <code>IMessageBus</code> directly:</p> C#<pre><code>public sealed class GameBootstrap : MonoBehaviour\n{\n [Inject]\n private IMessageBus _messageBus;\n\n private void Start()\n {\n var message = new GameStarted();\n _messageBus.EmitUntargeted(ref message);\n }\n}\n</code></pre>"},{"location":"integrations/reflex/#advanced-object-pooling","title":"Advanced: Object Pooling","text":"<p>When using object pooling with Reflex:</p> C#<pre><code>public sealed class EnemyPool\n{\n private readonly Container _container;\n private readonly Queue<Enemy> _pool = new();\n\n public EnemyPool(Container container)\n {\n _container = container;\n }\n\n public Enemy Spawn()\n {\n Enemy enemy;\n if (_pool.Count > 0)\n {\n enemy = _pool.Dequeue();\n }\n else\n {\n enemy = Object.Instantiate(enemyPrefab);\n _container.Inject(enemy); // Inject dependencies\n }\n return enemy;\n }\n\n public void Return(Enemy enemy)\n {\n _pool.Enqueue(enemy);\n }\n}\n</code></pre>"},{"location":"integrations/reflex/#testing-with-reflex","title":"Testing with Reflex","text":""},{"location":"integrations/reflex/#unit-tests","title":"Unit Tests","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing Reflex.Core;\nusing NUnit.Framework;\n\n[TestFixture]\npublic class DamageServiceTests\n{\n [Test]\n public void Initialize_ListensToMessages()\n {\n // Arrange\n var builder = new ContainerBuilder();\n var bus = new MessageBus();\n builder.AddSingleton<IMessageBus>(bus);\n builder.AddSingleton<DamageService>();\n var container = builder.Build();\n\n bool messageReceived = false;\n var handler = new MessageHandler(new InstanceId(1), bus) { active = true };\n var token = MessageRegistrationToken.Create(handler, bus);\n _ = token.RegisterUntargeted<PlayerDamaged>(ref msg => messageReceived = true);\n token.Enable();\n\n // Act\n var service = container.Resolve<DamageService>();\n service.Initialize();\n var message = new PlayerDamaged(25);\n bus.EmitUntargeted(ref message);\n\n // Assert\n Assert.IsTrue(messageReceived);\n }\n}\n</code></pre>"},{"location":"integrations/reflex/#checklist","title":"Checklist","text":""},{"location":"integrations/reflex/#initial-setup","title":"Initial Setup","text":"<ul> <li> Install DxMessaging and Reflex</li> <li> Create <code>DxMessagingInstaller</code> with bus bindings</li> <li> Add installer to your <code>SceneContext</code> or <code>ProjectContext</code></li> <li> Import <code>DxMessaging.Unity.Integrations.Reflex</code> and install <code>DxMessagingRegistrationInstaller</code> for <code>IMessageRegistrationBuilder</code> support</li> </ul>"},{"location":"integrations/reflex/#integration","title":"Integration","text":"<ul> <li> Use <code>IMessageRegistrationBuilder</code> in plain classes</li> <li> Call <code>Initialize()</code> and <code>Dispose()</code> manually from a bootstrap script</li> <li> Add <code>MessagingComponentConfigurator</code> to prefabs with <code>MessagingComponent</code></li> <li> Replace <code>MessageHandler.MessageBus</code> references with injected <code>IMessageBus</code></li> </ul>"},{"location":"integrations/reflex/#pooling","title":"Pooling","text":"<ul> <li> Use <code>container.Inject(instance)</code> for pooled objects</li> <li> Ensure injection happens before message handlers are registered</li> </ul>"},{"location":"integrations/reflex/#testing","title":"Testing","text":"<ul> <li> Create isolated <code>ContainerBuilder</code> instances in tests</li> <li> Use <code>builder.AddSingleton<IMessageBus>(new MessageBus())</code> for test buses</li> </ul>"},{"location":"integrations/reflex/#next-steps","title":"Next Steps","text":"<ul> <li>Zenject Integration \u2014 Full-featured DI with extensive Unity support</li> <li>VContainer Integration \u2014 Lightweight alternative with scoped lifetimes</li> <li>Back to Documentation Hub \u2014 Browse all docs</li> </ul>"},{"location":"integrations/vcontainer/","title":"DxMessaging + VContainer","text":"<p>\u2190 Back to Integrations Overview</p>"},{"location":"integrations/vcontainer/#overview","title":"Overview","text":"<p>VContainer is a fast, lightweight dependency injection framework for Unity with minimal overhead. DxMessaging integrates with VContainer, allowing you to:</p> <ul> <li>Inject <code>IMessageBus</code> in any class with deterministic lifetimes</li> <li>Create per-scope buses for scene isolation (perfect for additive scenes)</li> <li>Use DI for construction + DxMessaging for events (best of both worlds)</li> <li>Compatible \u2014 VContainer and DxMessaging can be used together</li> </ul> <p>Why combine DI + Messaging? Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected), combining both approaches. VContainer's scoped lifetimes support per-scene message buses.</p>"},{"location":"integrations/vcontainer/#quick-start","title":"Quick Start","text":""},{"location":"integrations/vcontainer/#prerequisites","title":"Prerequisites","text":"<ul> <li>DxMessaging installed via UPM</li> <li>VContainer installed (Git URL or OpenUPM)</li> </ul>"},{"location":"integrations/vcontainer/#1-create-a-lifetimescope-with-dxmessaging","title":"1. Create a LifetimeScope with DxMessaging","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity.Integrations.VContainer; // Required for RegisterMessageRegistrationBuilder()\nusing UnityEngine;\nusing VContainer;\nusing VContainer.Unity;\n\npublic sealed class MessagingLifetimeScope : LifetimeScope\n{\n protected override void Configure(IContainerBuilder builder)\n {\n // Register MessageBus as both concrete and interface\n builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();\n\n // Optional: Enable automatic IMessageRegistrationBuilder binding\n // Requires VCONTAINER_PRESENT define (auto-added by DxMessaging when VContainer detected)\n #if VCONTAINER_PRESENT\n builder.RegisterMessageRegistrationBuilder();\n #endif\n }\n}\n</code></pre> <p>Note: You must import the <code>DxMessaging.Unity.Integrations.VContainer</code> namespace to access the <code>RegisterMessageRegistrationBuilder()</code> extension method.</p>"},{"location":"integrations/vcontainer/#add-to-your-scene","title":"Add to your scene","text":"<ol> <li>Create an empty GameObject in your scene</li> <li>Add the <code>MessagingLifetimeScope</code> component</li> <li>This creates a singleton bus for the entire scene</li> </ol> <p>Tip: Use <code>Lifetime.Singleton</code> for project-wide buses, or <code>Lifetime.Scoped</code> for isolated scene/feature buses.</p>"},{"location":"integrations/vcontainer/#usage-patterns","title":"Usage Patterns","text":""},{"location":"integrations/vcontainer/#pattern-1-inject-into-plain-classes-recommended-for-services","title":"Pattern 1: Inject into Plain Classes (Recommended for Services)","text":"<p>Use <code>IMessageRegistrationBuilder</code> to create message handlers in non-MonoBehaviour classes:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Core.Attributes;\nusing VContainer.Unity;\n\n// Define a message\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerSpawned\n{\n public readonly int playerId;\n}\n\n// Service that listens to messages\npublic sealed class PlayerService : IStartable, IDisposable\n{\n private readonly MessageRegistrationLease _lease;\n\n // Builder is injected automatically when using the scope\n public PlayerService(IMessageRegistrationBuilder registrationBuilder)\n {\n var options = new MessageRegistrationBuildOptions\n {\n Configure = token =>\n {\n _ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);\n }\n };\n\n _lease = registrationBuilder.Build(options);\n }\n\n public void Start()\n {\n _lease.Activate(); // Start listening when container starts\n }\n\n public void Dispose()\n {\n _lease.Dispose(); // Clean up when container disposes\n }\n\n private static void OnPlayerSpawned(ref PlayerSpawned message)\n {\n UnityEngine.Debug.Log($\"Player {message.playerId} spawned!\");\n }\n}\n</code></pre>"},{"location":"integrations/vcontainer/#register-the-service-in-your-scope","title":"Register the service in your scope","text":"C#<pre><code>using DxMessaging.Unity.Integrations.VContainer; // Required for extension method\n\n// ...\n\nprotected override void Configure(IContainerBuilder builder)\n{\n builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();\n #if VCONTAINER_PRESENT\n builder.RegisterMessageRegistrationBuilder();\n #endif\n\n // Register your service\n builder.RegisterEntryPoint<PlayerService>();\n}\n</code></pre>"},{"location":"integrations/vcontainer/#pattern-2-configure-messagingcomponents-for-existing-monobehaviours","title":"Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing UnityEngine;\nusing VContainer;\nusing VContainer.Unity;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class MessagingComponentConfigurator : MonoBehaviour, IStartable\n{\n [Inject]\n private readonly IMessageBus _messageBus;\n\n private MessagingComponent _messagingComponent;\n\n private void Awake()\n {\n _messagingComponent = GetComponent<MessagingComponent>();\n }\n\n public void Start()\n {\n _messagingComponent.Configure(_messageBus, MessageBusRebindMode.RebindActive);\n }\n}\n</code></pre>"},{"location":"integrations/vcontainer/#usage","title":"Usage","text":"<ol> <li>Add <code>MessagingComponentConfigurator</code> alongside any <code>MessagingComponent</code> in your prefabs</li> <li>VContainer will inject the bus via <code>IStartable.Start()</code> before handlers are registered</li> <li>Your message handlers now use the container-managed bus</li> </ol>"},{"location":"integrations/vcontainer/#pattern-3-inject-imessagebus-directly","title":"Pattern 3: Inject IMessageBus Directly","text":"<p>For simple emission without listening, inject <code>IMessageBus</code> directly:</p> C#<pre><code>public sealed class GameInitializer : IStartable\n{\n private readonly IMessageBus _messageBus;\n\n public GameInitializer(IMessageBus messageBus)\n {\n _messageBus = messageBus;\n }\n\n public void Start()\n {\n var message = new GameStarted();\n _messageBus.EmitUntargeted(ref message);\n }\n}\n</code></pre>"},{"location":"integrations/vcontainer/#advanced-scene-scopes-and-isolation","title":"Advanced: Scene Scopes and Isolation","text":"<p>VContainer's scoped lifetimes support per-scene message buses. This is useful for additive scenes or isolated gameplay features:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity.Integrations.VContainer; // Required for extension method\nusing VContainer;\nusing VContainer.Unity;\n\npublic sealed class LevelLoader\n{\n private readonly LifetimeScope _parentScope;\n\n public LevelLoader(LifetimeScope parentScope)\n {\n _parentScope = parentScope;\n }\n\n public LifetimeScope LoadLevel(GameObject lifetimeScopePrefab)\n {\n // Create a child scope with its own MessageBus\n return _parentScope.CreateChildFromPrefab(lifetimeScopePrefab, builder =>\n {\n builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();\n #if VCONTAINER_PRESENT\n builder.RegisterMessageRegistrationBuilder();\n #endif\n });\n }\n}\n</code></pre>"},{"location":"integrations/vcontainer/#benefits","title":"Benefits","text":"<ul> <li>Each scene gets its own isolated message bus</li> <li>Messages don't leak between scenes</li> <li>Suitable for multiplayer lobbies, mini-games, or feature-scoped events</li> </ul>"},{"location":"integrations/vcontainer/#testing-with-vcontainer","title":"Testing with VContainer","text":""},{"location":"integrations/vcontainer/#unit-tests","title":"Unit Tests","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing VContainer;\nusing VContainer.Unity;\nusing NUnit.Framework;\n\n[TestFixture]\npublic class GameInitializerTests\n{\n [Test]\n public void Initialize_EmitsGameStarted()\n {\n // Arrange\n var builder = new ContainerBuilder();\n var bus = new MessageBus();\n builder.RegisterInstance<IMessageBus>(bus);\n builder.RegisterEntryPoint<GameInitializer>();\n var container = builder.Build();\n\n bool messageReceived = false;\n var handler = new MessageHandler(new InstanceId(1), bus) { active = true };\n var token = MessageRegistrationToken.Create(handler, bus);\n _ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);\n token.Enable();\n\n // Act\n container.Resolve<GameInitializer>().Start();\n\n // Assert\n Assert.IsTrue(messageReceived);\n }\n}\n</code></pre>"},{"location":"integrations/vcontainer/#play-mode-tests","title":"Play-Mode Tests","text":"<p>For play-mode tests, create a temporary <code>LifetimeScope</code>:</p> C#<pre><code>[UnityTest]\npublic IEnumerator PlayMode_MessageBusIsolation()\n{\n // Create isolated scope for this test\n var scope = LifetimeScope.Create(builder =>\n {\n builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();\n });\n\n var bus = scope.Container.Resolve<IMessageBus>();\n // ... test logic ...\n\n scope.Dispose(); // Clean up\n yield return null;\n}\n</code></pre>"},{"location":"integrations/vcontainer/#checklist","title":"Checklist","text":""},{"location":"integrations/vcontainer/#initial-setup","title":"Initial Setup","text":"<ul> <li> Install DxMessaging and VContainer</li> <li> Create <code>MessagingLifetimeScope</code> with <code>builder.Register<MessageBus>().As<IMessageBus>()</code></li> <li> Add scope to your scene as a GameObject component</li> <li> Import <code>DxMessaging.Unity.Integrations.VContainer</code> namespace for extension methods</li> <li> Add <code>#if VCONTAINER_PRESENT</code> check and call <code>builder.RegisterMessageRegistrationBuilder()</code></li> </ul>"},{"location":"integrations/vcontainer/#integration","title":"Integration","text":"<ul> <li> Use <code>IMessageRegistrationBuilder</code> in plain classes with <code>IStartable</code>/<code>IDisposable</code></li> <li> Add <code>MessagingComponentConfigurator</code> to prefabs with <code>MessagingComponent</code></li> <li> Replace <code>MessageHandler.MessageBus</code> references with injected <code>IMessageBus</code></li> <li> Consider using scoped buses for scene isolation</li> </ul>"},{"location":"integrations/vcontainer/#testing","title":"Testing","text":"<ul> <li> Create isolated <code>ContainerBuilder</code> instances in tests</li> <li> Use <code>builder.RegisterInstance<IMessageBus>(new MessageBus())</code> for test buses</li> <li> Dispose scopes after tests to ensure clean teardown</li> </ul>"},{"location":"integrations/vcontainer/#next-steps","title":"Next Steps","text":"<ul> <li>Zenject Integration \u2014 Full-featured DI with extensive Unity support</li> <li>Reflex Integration \u2014 Minimal DI framework</li> <li>Back to Documentation Hub \u2014 Browse all docs</li> </ul>"},{"location":"integrations/zenject/","title":"DxMessaging + Zenject","text":"<p>\u2190 Back to Integrations Overview</p>"},{"location":"integrations/zenject/#overview","title":"Overview","text":"<p>Zenject (also known as Extenject) is a powerful dependency injection framework for Unity. DxMessaging integrates with Zenject, allowing you to:</p> <ul> <li>Inject <code>IMessageBus</code> as a singleton dependency in any class</li> <li>Use DI for construction + DxMessaging for events (best of both worlds)</li> <li>Create per-scope message buses for scene or gameplay isolation</li> <li>Bridge to SignalBus for gradual migration from Zenject Signals</li> </ul> <p>Why combine DI + Messaging? Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected), combining both approaches.</p>"},{"location":"integrations/zenject/#quick-start","title":"Quick Start","text":""},{"location":"integrations/zenject/#prerequisites","title":"Prerequisites","text":"<ul> <li>DxMessaging installed via UPM</li> <li>Zenject/Extenject installed (source or UPM)</li> </ul>"},{"location":"integrations/zenject/#1-create-an-installer","title":"1. Create an Installer","text":"<p>Create a <code>DxMessagingInstaller</code> to bind the message bus to your Zenject container:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing Zenject;\n\npublic sealed class DxMessagingInstaller : MonoInstaller\n{\n public override void InstallBindings()\n {\n // Bind MessageBus as a singleton and expose IMessageBus interface\n Container.BindInterfacesAndSelfTo<MessageBus>().AsSingle();\n\n // Optional: Enable automatic IMessageRegistrationBuilder binding\n // Requires ZENJECT_PRESENT define (auto-added by DxMessaging when Zenject detected)\n #if ZENJECT_PRESENT\n Container.RegisterMessageRegistrationBuilder();\n #endif\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#add-to-your-projectcontext","title":"Add to your ProjectContext","text":"<ol> <li>Select (or create) your <code>ProjectContext</code> prefab</li> <li>Add <code>DxMessagingInstaller</code> as a MonoInstaller</li> <li>Save the prefab</li> </ol>"},{"location":"integrations/zenject/#usage-patterns","title":"Usage Patterns","text":""},{"location":"integrations/zenject/#pattern-1-inject-into-plain-classes-recommended-for-services","title":"Pattern 1: Inject into Plain Classes (Recommended for Services)","text":"<p>Use <code>IMessageRegistrationBuilder</code> to create message handlers in non-MonoBehaviour classes:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Core.Attributes;\nusing Zenject;\n\n// Define a message\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerSpawned\n{\n public readonly int playerId;\n}\n\n// Service that listens to messages\npublic sealed class PlayerController : IInitializable, IDisposable\n{\n private readonly MessageRegistrationLease _lease;\n\n // Builder is injected automatically when using the installer\n public PlayerController(IMessageRegistrationBuilder registrationBuilder)\n {\n var options = new MessageRegistrationBuildOptions\n {\n Configure = token =>\n {\n _ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);\n }\n };\n\n _lease = registrationBuilder.Build(options);\n }\n\n public void Initialize()\n {\n _lease.Activate(); // Start listening when container initializes\n }\n\n public void Dispose()\n {\n _lease.Dispose(); // Clean up when container disposes\n }\n\n private static void OnPlayerSpawned(ref PlayerSpawned message)\n {\n UnityEngine.Debug.Log($\"Player {message.playerId} spawned!\");\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#register-the-service-in-your-installer","title":"Register the service in your installer","text":"C#<pre><code>public sealed class GameInstaller : MonoInstaller\n{\n public override void InstallBindings()\n {\n Container.BindInterfacesAndSelfTo<PlayerController>().AsSingle();\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#pattern-2-configure-messagingcomponents-for-existing-monobehaviours","title":"Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)","text":"<p>If you have existing <code>MessageAwareComponent</code> scripts, you can inject the container-managed bus into them:</p> C#<pre><code>using DxMessaging.Core.MessageBus;\nusing DxMessaging.Unity;\nusing UnityEngine;\nusing Zenject;\n\n[RequireComponent(typeof(MessagingComponent))]\npublic sealed class MessagingComponentConfigurator : MonoBehaviour\n{\n [Inject]\n private IMessageBus _messageBus;\n\n private void Awake()\n {\n MessagingComponent component = GetComponent<MessagingComponent>();\n component.Configure(_messageBus, MessageBusRebindMode.RebindActive);\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#usage","title":"Usage","text":"<ol> <li>Add <code>MessagingComponentConfigurator</code> alongside any <code>MessagingComponent</code> in your prefabs</li> <li>Zenject will inject the bus before <code>RegisterMessageHandlers</code> is called</li> <li>Your message handlers now use the container-managed bus</li> </ol> <p>Alternative approach: Extend <code>MessageAwareComponent</code> and override <code>Awake</code>:</p> C#<pre><code>public class ZenjectAwareComponent : MessageAwareComponent\n{\n [Inject]\n private IMessageBus _messageBus;\n\n protected override void Awake()\n {\n Configure(_messageBus, MessageBusRebindMode.RebindActive);\n base.Awake();\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#pattern-3-inject-imessagebus-directly","title":"Pattern 3: Inject IMessageBus Directly","text":"<p>For simple cases, inject <code>IMessageBus</code> and emit messages directly:</p> C#<pre><code>public sealed class GameInitializer : IInitializable\n{\n private readonly IMessageBus _messageBus;\n\n public GameInitializer(IMessageBus messageBus)\n {\n _messageBus = messageBus;\n }\n\n public void Initialize()\n {\n var message = new GameStarted();\n _messageBus.EmitUntargeted(ref message);\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#advanced-bridging-to-zenject-signals","title":"Advanced: Bridging to Zenject Signals","text":"<p>If you're gradually migrating from Zenject Signals to DxMessaging, you can create a bridge:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\nusing System;\nusing Zenject;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneTransition\n{\n public readonly string sceneName;\n}\n\npublic sealed class DxToSignalBridge : IInitializable, IDisposable\n{\n private readonly IMessageBus _messageBus;\n private readonly SignalBus _signalBus;\n private MessageRegistrationToken _token;\n\n public DxToSignalBridge(IMessageBus messageBus, SignalBus signalBus)\n {\n _messageBus = messageBus;\n _signalBus = signalBus;\n }\n\n public void Initialize()\n {\n // Create a handler to listen to DxMessaging events\n var handler = new MessageHandler(new InstanceId(0), _messageBus)\n {\n active = true\n };\n _token = MessageRegistrationToken.Create(handler, _messageBus);\n\n // Bridge DxMessaging \u2192 Zenject Signals\n _ = _token.RegisterUntargeted<SceneTransition>(OnSceneTransition);\n _token.Enable();\n }\n\n public void Dispose()\n {\n _token?.Disable();\n }\n\n private void OnSceneTransition(ref SceneTransition message)\n {\n // Forward to SignalBus for legacy consumers\n _signalBus.Fire(message);\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#register-the-bridge-in-your-installer","title":"Register the bridge in your installer","text":"C#<pre><code>public override void InstallBindings()\n{\n Container.BindInterfacesAndSelfTo<DxToSignalBridge>().AsSingle();\n Container.DeclareSignal<SceneTransition>();\n}\n</code></pre>"},{"location":"integrations/zenject/#testing-with-zenject","title":"Testing with Zenject","text":""},{"location":"integrations/zenject/#unit-tests","title":"Unit Tests","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\nusing Zenject;\nusing NUnit.Framework;\n\n[TestFixture]\npublic class GameInitializerTests : ZenjectUnitTestFixture\n{\n [Test]\n public void Initialize_EmitsGameStarted()\n {\n // Arrange\n var bus = new MessageBus();\n Container.Bind<IMessageBus>().FromInstance(bus).AsSingle();\n Container.BindInterfacesAndSelfTo<GameInitializer>().AsSingle();\n\n bool messageReceived = false;\n var handler = new MessageHandler(new InstanceId(1), bus) { active = true };\n var token = MessageRegistrationToken.Create(handler, bus);\n _ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);\n token.Enable();\n\n // Act\n var initializer = Container.Resolve<GameInitializer>();\n initializer.Initialize();\n\n // Assert\n Assert.IsTrue(messageReceived);\n }\n}\n</code></pre>"},{"location":"integrations/zenject/#checklist","title":"Checklist","text":""},{"location":"integrations/zenject/#initial-setup","title":"Initial Setup","text":"<ul> <li> Install DxMessaging and Zenject/Extenject</li> <li> Create <code>DxMessagingInstaller</code> with <code>Container.BindInterfacesAndSelfTo<MessageBus>()</code></li> <li> Add installer to your <code>ProjectContext</code></li> <li> Add <code>#if ZENJECT_PRESENT</code> check and call <code>Container.RegisterMessageRegistrationBuilder()</code></li> </ul>"},{"location":"integrations/zenject/#integration","title":"Integration","text":"<ul> <li> Use <code>IMessageRegistrationBuilder</code> in plain classes with <code>IInitializable</code>/<code>IDisposable</code></li> <li> Add <code>MessagingComponentConfigurator</code> to prefabs with <code>MessagingComponent</code></li> <li> Replace <code>MessageHandler.MessageBus</code> references with injected <code>IMessageBus</code></li> <li> Consider bridging to SignalBus if migrating from Zenject Signals</li> </ul>"},{"location":"integrations/zenject/#testing","title":"Testing","text":"<ul> <li> Inject <code>IMessageBus</code> in tests using <code>FromInstance(new MessageBus())</code></li> <li> Verify messages flow through the container-provided bus</li> </ul>"},{"location":"integrations/zenject/#next-steps","title":"Next Steps","text":"<ul> <li>VContainer Integration \u2014 Lightweight alternative to Zenject</li> <li>Reflex Integration \u2014 Minimal DI framework</li> <li>Back to Documentation Hub \u2014 Browse all docs</li> </ul>"},{"location":"reference/compatibility/","title":"Compatibility","text":"<p>DxMessaging is render\u2011pipeline agnostic (pure C#) and targets Unity 2021.3+. The matrix below summarizes support by Unity version and Render Pipeline.</p> <p>Unity Version vs Render Pipeline</p> Unity Built\u2011In RP URP HDRP 2021.3 LTS \u2705 Compatible \u2705 Compatible \u2705 Compatible 2022.3 LTS \u2705 Compatible \u2705 Compatible \u2705 Compatible 2023.x \u2705 Compatible \u2705 Compatible \u2705 Compatible 6.x \u2705 Compatible \u2705 Compatible \u2705 Compatible <p>Notes</p> <ul> <li>RP\u2011agnostic: DxMessaging does not depend on rendering APIs; it works equally across Built\u2011In, URP, and HDRP.</li> <li>Minimum version is governed by the package manifest (<code>unity</code>: 2021.3). Newer LTS versions are expected to work.</li> </ul>"},{"location":"reference/compatibility/#architecture-pattern-compatibility","title":"Architecture Pattern Compatibility","text":""},{"location":"reference/compatibility/#scriptable-object-architecture-soa","title":"Scriptable Object Architecture (SOA)","text":"<p>DxMessaging can work alongside Scriptable Object Architecture patterns, though SOA has documented limitations. See Pattern 14: SOA Compatibility for detailed integration strategies, code examples, and migration paths.</p>"},{"location":"reference/compatibility/#quick-summary","title":"Quick summary","text":"<ul> <li>\u2705 Compatible - DxMessaging can bridge with SOA systems</li> <li>\u26a0\ufe0f Not recommended - SOA has scalability and maintainability concerns (detailed critique)</li> <li>\u2705 Best practice - Use ScriptableObjects for immutable design data, DxMessaging for runtime events</li> <li>\u2192 See SOA Integration Patterns for three coexistence strategies with code examples</li> </ul>"},{"location":"reference/compatibility/#dependency-injection-di-frameworks","title":"Dependency Injection (DI) Frameworks","text":"<p>DxMessaging integrates with popular DI frameworks:</p> <ul> <li>Zenject - See Zenject Integration Guide</li> <li>VContainer - See VContainer Integration Guide</li> <li>Reflex - See Reflex Integration Guide</li> </ul> <p>DI and DxMessaging complement each other: DI manages dependencies/services, DxMessaging handles event communication.</p>"},{"location":"reference/compatibility/#other-unity-frameworks","title":"Other Unity Frameworks","text":"<p>For comparisons with other messaging/event frameworks (UniRx, MessagePipe, Zenject Signals, etc.), see Framework Comparisons.</p>"},{"location":"reference/faq/","title":"FAQ \u2014 Frequently Asked Questions","text":"<p>\u2190 Back to Index | Troubleshooting | Getting Started | Glossary</p>"},{"location":"reference/faq/#do-i-need-to-use-attributes-or-source-generators","title":"Do I need to use attributes or source generators","text":"<ul> <li>No. You can implement <code>IUntargetedMessage<T></code>, <code>ITargetedMessage<T></code>, or <code>IBroadcastMessage<T></code> directly (recommended for structs). Attributes are optional and help tooling/source\u2011gen.</li> </ul>"},{"location":"reference/faq/#which-message-type-should-i-use","title":"Which message type should I use?","text":"<ul> <li>Untargeted - global notifications (any listener).</li> <li>Targeted - commands/events for a specific recipient.</li> <li>Broadcast - facts emitted from a source that others may observe.</li> </ul>"},{"location":"reference/faq/#how-do-i-enforce-ordering","title":"How do I enforce ordering?","text":"<ul> <li>Use the <code>priority</code> parameter at registration; lower runs earlier. Interceptors run before handlers; post\u2011processors run after.</li> </ul>"},{"location":"reference/faq/#can-i-observe-all-targetssources-for-a-type","title":"Can I observe all targets/sources for a type?","text":"<ul> <li>Yes. Use <code>RegisterTargetedWithoutTargeting<T></code> or <code>RegisterBroadcastWithoutSource<T></code> (and their post\u2011processor counterparts).</li> </ul>"},{"location":"reference/faq/#how-do-i-diagnose-whats-happening","title":"How do I diagnose what's happening?","text":"<ul> <li>Enable logs and diagnostics: Diagnostics.</li> </ul>"},{"location":"reference/faq/#what-happens-if-i-register-a-listener-inside-a-message-handler","title":"What happens if I register a listener inside a message handler?","text":"<ul> <li>The newly registered listener will not run for the current message emission. It will only become active starting with the next message emission.</li> <li>This is called \"snapshot semantics\" \u2014 when a message is emitted, DxMessaging takes a snapshot of all current listeners and uses that frozen list for the entire emission.</li> <li>This applies to all listener types (handlers, interceptors, post-processors) and all message categories (Untargeted, Targeted, Broadcast).</li> <li>This behavior prevents infinite loops and ensures predictable execution order. See Interceptors & Ordering for details and examples.</li> </ul>"},{"location":"reference/faq/#do-i-need-a-global-bus","title":"Do I need a global bus?","text":"<ul> <li>A global bus is provided (<code>MessageHandler.MessageBus</code>). You can also create and pass your own <code>MessageBus</code> instance to isolate subsystems and tests.</li> </ul>"},{"location":"reference/faq/#is-this-compatible-with-unitys-sendmessageunityevents","title":"Is this compatible with Unity's SendMessage/UnityEvents","text":"<ul> <li>Yes. You can integrate with legacy patterns via <code>ReflexiveMessage</code>. Prefer DxMessaging for new code.</li> </ul>"},{"location":"reference/faq/#related-documentation","title":"Related Documentation","text":"<ul> <li>New to DxMessaging?</li> <li>\u2192 Visual Guide \u2014 Beginner-friendly introduction</li> <li>\u2192 Getting Started \u2014 Complete guide</li> <li>\u2192 Glossary \u2014 All terms explained</li> <li>Common Issues</li> <li>\u2192 Troubleshooting \u2014 Solutions to common problems</li> <li>\u2192 Common Patterns \u2014 See how to use it correctly</li> <li>Reference</li> <li>\u2192 Quick Reference \u2014 API cheat sheet</li> <li>\u2192 Message Types \u2014 Which type to use when</li> </ul>"},{"location":"reference/glossary/","title":"Glossary \u2014 DxMessaging Terms Explained","text":"<p>\u2190 Back to Index | Getting Started | Visual Guide</p> <p>New to messaging systems? This glossary explains key terms in plain English.</p>"},{"location":"reference/glossary/#core-concepts","title":"Core Concepts","text":""},{"location":"reference/glossary/#message","title":"Message","text":"<p>A data structure that represents something that happened or should happen. Think of it like a letter or announcement containing information.</p> <p>Example: <code>Heal { amount: 10 }</code> is a message saying \"heal for 10 points.\"</p>"},{"location":"reference/glossary/#message-type","title":"Message Type","text":"<p>The category of message that determines who receives it:</p> <ul> <li>Untargeted: Everyone hears it (like a megaphone announcement)</li> <li>Targeted: One specific recipient gets it (like a letter to an address)</li> <li>Broadcast: Comes from one source, anyone can listen (like a news broadcast)</li> </ul>"},{"location":"reference/glossary/#handler","title":"Handler","text":"<p>A function that runs when a message is received. Your game logic goes here.</p> <p>Example:</p> C#<pre><code>void OnHeal(ref Heal msg) {\n health += msg.amount; // This is the handler\n}\n</code></pre>"},{"location":"reference/glossary/#token","title":"Token","text":"<p>A registration handle that manages the lifecycle of your message handlers. It automatically enables/disables handlers when your component is active/inactive.</p> <p>Think of it like a subscription card \u2014 when you destroy it, all your subscriptions end automatically.</p>"},{"location":"reference/glossary/#messageawarecomponent","title":"MessageAwareComponent","text":"<p>A Unity MonoBehaviour base class that handles all the lifecycle management for you. Inherit from this and you get automatic setup/cleanup.</p> C#<pre><code>public class MyComponent : MessageAwareComponent {\n // Token is created automatically!\n}\n</code></pre>"},{"location":"reference/glossary/#message-pipeline","title":"Message Pipeline","text":""},{"location":"reference/glossary/#interceptor","title":"Interceptor","text":"<p>Code that runs before handlers. Can validate, modify, or cancel messages before anyone else sees them.</p> <p>Example: \"Only allow damage between 1 and 999\"</p> C#<pre><code>// Use the type-specific interceptor variant based on your message type:\n// - RegisterUntargetedInterceptor<T> for IUntargetedMessage\n// - RegisterTargetedInterceptor<T> for ITargetedMessage\n// - RegisterBroadcastInterceptor<T> for IBroadcastMessage\n_ = token.RegisterBroadcastInterceptor<Damage>((ref InstanceId source, ref Damage msg) => {\n if (msg.amount <= 0) return false; // Cancel\n if (msg.amount > 999) msg = new Damage(999); // Clamp\n return true; // Allow\n});\n</code></pre>"},{"location":"reference/glossary/#post-processor","title":"Post-Processor","text":"<p>Code that runs after all handlers. Perfect for logging, analytics, or metrics that shouldn't affect gameplay.</p> <p>Example: \"Track every damage event for statistics\"</p> C#<pre><code>// Use the type-specific post-processor variant based on your message type:\n// - RegisterUntargetedPostProcessor<T> for IUntargetedMessage\n// - RegisterBroadcastWithoutSourcePostProcessor<T> for IBroadcastMessage from any source\n_ = token.RegisterBroadcastWithoutSourcePostProcessor<Damage>((InstanceId source, Damage msg) => {\n Analytics.LogDamage(source, msg.amount);\n});\n</code></pre>"},{"location":"reference/glossary/#priority","title":"Priority","text":"<p>A number that controls execution order. Lower numbers run first.</p> <p>Example:</p> C#<pre><code>_ = token.RegisterUntargeted<GameExit>(SaveGame, priority: 0); // Runs first\n_ = token.RegisterUntargeted<GameExit>(FadeAudio, priority: 5); // Runs second\n_ = token.RegisterUntargeted<GameExit>(ShowUI, priority: 10); // Runs third\n</code></pre>"},{"location":"reference/glossary/#unity-integration","title":"Unity Integration","text":""},{"location":"reference/glossary/#instanceid","title":"InstanceId","text":"<p>A unique identifier for a GameObject or Component. Used internally to route messages to the right place.</p> <p>You rarely use this directly \u2014 use the GameObject/Component helpers instead:</p> C#<pre><code>msg.EmitGameObjectTargeted(gameObject); // Helper (use this)\n// vs\nmsg.EmitTargeted(gameObject.GetInstanceID()); // Manual InstanceId (avoid)\n</code></pre>"},{"location":"reference/glossary/#messagingcomponent","title":"MessagingComponent","text":"<p>The base Unity component that provides messaging infrastructure. <code>MessageAwareComponent</code> inherits from this.</p>"},{"location":"reference/glossary/#emit","title":"Emit","text":"<p>To send a message. Like hitting \"send\" on an email.</p> C#<pre><code>var heal = new Heal(10);\nheal.Emit(); // Send it!\n</code></pre>"},{"location":"reference/glossary/#register","title":"Register","text":"<p>To sign up to receive messages. Like subscribing to a newsletter.</p> C#<pre><code>_ = Token.RegisterUntargeted<Heal>(OnHeal); // Subscribe\n</code></pre>"},{"location":"reference/glossary/#advanced-terms","title":"Advanced Terms","text":""},{"location":"reference/glossary/#message-bus","title":"Message Bus","text":"<p>The central routing system that delivers messages. Think of it like a post office.</p> <p>There's a global bus (<code>MessageHandler.MessageBus</code>) that most code uses, but you can create local buses for testing or isolation.</p>"},{"location":"reference/glossary/#local-bus--bus-island","title":"Local Bus / Bus Island","text":"<p>A separate message bus used to isolate subsystems or tests. Messages sent to a local bus don't affect the global bus.</p> C#<pre><code>var testBus = new MessageBus(); // Create island\nvar token = MessageRegistrationToken.Create(handler, testBus); // Use it\n</code></pre>"},{"location":"reference/glossary/#global-accept-all","title":"Global Accept-All","text":"<p>A special handler that receives every single message regardless of type. Used for tools, debuggers, and analytics.</p> C#<pre><code>_ = Token.RegisterGlobalAcceptAll(\n (ref IUntargetedMessage m) => Debug.Log(\"Untargeted: \" + m),\n (ref InstanceId t, ref ITargetedMessage m) => Debug.Log(\"Targeted: \" + m),\n (ref InstanceId s, ref IBroadcastMessage m) => Debug.Log(\"Broadcast: \" + m)\n);\n</code></pre>"},{"location":"reference/glossary/#diagnostics-mode","title":"Diagnostics Mode","text":"<p>A debug feature that tracks message history and handler statistics. Enable in Editor, disable in builds for performance.</p> C#<pre><code>IMessageBus.GlobalDiagnosticsMode = true; // See message history\n</code></pre>"},{"location":"reference/glossary/#attributes-source-generation","title":"Attributes (Source Generation)","text":""},{"location":"reference/glossary/#dxuntargetedmessage","title":"[DxUntargetedMessage]","text":"<p>Marks a struct as an Untargeted message (global announcement).</p>"},{"location":"reference/glossary/#dxtargetedmessage","title":"[DxTargetedMessage]","text":"<p>Marks a struct as a Targeted message (command to one recipient).</p>"},{"location":"reference/glossary/#dxbroadcastmessage","title":"[DxBroadcastMessage]","text":"<p>Marks a struct as a Broadcast message (event from a source).</p>"},{"location":"reference/glossary/#dxautoconstructor","title":"[DxAutoConstructor]","text":"<p>Auto-generates a constructor for your message struct so you don't have to write it manually.</p> C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor] // Generates: public Heal(int amount) { this.amount = amount; }\npublic readonly partial struct Heal {\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/glossary/#common-patterns","title":"Common Patterns","text":""},{"location":"reference/glossary/#lifecycle","title":"Lifecycle","text":"<p>The creation and destruction of components and their message registrations.</p> <p>DxMessaging handles this automatically via <code>MessageAwareComponent</code>:</p> <ul> <li><code>Awake()</code>: Token created, handlers registered</li> <li><code>OnEnable()</code>: Token enabled, handlers active</li> <li><code>OnDisable()</code>: Token disabled, handlers inactive</li> <li><code>OnDestroy()</code>: Token destroyed, everything cleaned up</li> </ul>"},{"location":"reference/glossary/#decoupling","title":"Decoupling","text":"<p>Making systems independent so they don't need references to each other.</p>"},{"location":"reference/glossary/#before-dxmessaging","title":"Before DxMessaging","text":"C#<pre><code>public class UI : MonoBehaviour {\n [SerializeField] Player player; // Tight coupling!\n [SerializeField] EnemySpawner spawner;\n [SerializeField] AudioManager audio;\n}\n</code></pre>"},{"location":"reference/glossary/#with-dxmessaging","title":"With DxMessaging","text":"C#<pre><code>public class UI : MessageAwareComponent {\n // No references needed! Just listen for messages.\n protected override void RegisterMessageHandlers() {\n _ = Token.RegisterBroadcastWithoutSource<PlayerDamaged>(OnDamage);\n }\n}\n</code></pre>"},{"location":"reference/glossary/#quick-reference-table","title":"Quick Reference Table","text":"Term One-Line Explanation Message Data saying \"something happened\" or \"do something\" Handler Function that runs when you receive a message Token Subscription manager (auto-cleanup) Interceptor Guard that checks/modifies messages before delivery Post-Processor Code that runs after all handlers (logging/metrics) Priority Number controlling execution order (lower = earlier) Emit Send a message Register Subscribe to receive messages InstanceId Unique ID for a GameObject or Component Bus Central message router (like a post office)"},{"location":"reference/glossary/#related-documentation","title":"Related Documentation","text":""},{"location":"reference/glossary/#learn-more","title":"Learn More","text":"<ul> <li>\u2192 Visual Guide \u2014 See these concepts visualized</li> <li>\u2192 Getting Started \u2014 Full introduction with examples</li> <li>\u2192 Message Types \u2014 When to use each type</li> </ul>"},{"location":"reference/glossary/#reference","title":"Reference","text":"<ul> <li>\u2192 Quick Reference \u2014 API cheat sheet</li> <li> <p>\u2192 API Reference \u2014 Complete API documentation</p> </li> <li> <p>\u2192 Mini Combat sample \u2014 See concepts in action</p> </li> <li>\u2192 Patterns \u2014 Real-world usage patterns</li> </ul>"},{"location":"reference/helpers/","title":"Helpers and Source Generation","text":""},{"location":"reference/helpers/#what-are-source-generators","title":"What Are Source Generators","text":"<p>Source generators are a C# feature (introduced in C# 9.0) that automatically write code for you at compile time. Think of them as smart code wizards that look at your code, see what you need, and generate the boilerplate automatically.</p>"},{"location":"reference/helpers/#in-plain-english","title":"In plain English","text":"<ul> <li>You write: <code>[DxAutoConstructor]</code> on a struct</li> <li>Source generator sees: \"Oh, they want a constructor!\"</li> <li>Source generator creates: A constructor with all the fields as parameters</li> <li>You get: Less typing, fewer bugs, more consistent code</li> </ul>"},{"location":"reference/helpers/#learn-more-about-source-generators","title":"Learn more about source generators","text":"<ul> <li>Microsoft Docs: Source Generators</li> <li>Introduction to C# Source Generators</li> </ul>"},{"location":"reference/helpers/#dxmessaging-attributes-your-code-wizards","title":"DxMessaging Attributes (Your Code Wizards)","text":"<p>DxMessaging provides 3 main attributes that use source generators to eliminate boilerplate:</p>"},{"location":"reference/helpers/#1-message-type-attributes-pick-your-message-category","title":"1. Message Type Attributes (Pick Your Message Category)","text":"<p>These tell the source generator what KIND of message you're making:</p>"},{"location":"reference/helpers/#dxuntargetedmessage---global-messages","title":"<code>[DxUntargetedMessage]</code> - Global Messages","text":"C#<pre><code>[DxUntargetedMessage] // \u2190 Tells generator: \"This is a global message\"\npublic readonly partial struct GamePaused { }\n</code></pre>"},{"location":"reference/helpers/#what-it-generates","title":"What it generates","text":"<ul> <li>Implements <code>IUntargetedMessage<GamePaused></code></li> <li>Adds required plumbing for the message system</li> <li>Makes it work with <code>.Emit()</code> extension methods</li> </ul>"},{"location":"reference/helpers/#dxtargetedmessage---messages-to-specific-targets","title":"<code>[DxTargetedMessage]</code> - Messages to Specific Targets","text":"C#<pre><code>[DxTargetedMessage] // \u2190 Tells generator: \"This goes to one specific target\"\npublic readonly partial struct Heal {\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#what-it-generates_1","title":"What it generates","text":"<ul> <li>Implements <code>ITargetedMessage<Heal></code></li> <li>Adds required plumbing for targeted emissions</li> <li>Makes it work with <code>.EmitGameObjectTargeted()</code> / <code>.EmitComponentTargeted()</code></li> </ul>"},{"location":"reference/helpers/#dxbroadcastmessage---messages-from-a-source","title":"<code>[DxBroadcastMessage]</code> - Messages from a Source","text":"C#<pre><code>[DxBroadcastMessage] // \u2190 Tells generator: \"This broadcasts from a source\"\npublic readonly partial struct TookDamage {\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#what-it-generates_2","title":"What it generates","text":"<ul> <li>Implements <code>IBroadcastMessage<TookDamage></code></li> <li>Adds required plumbing for broadcast emissions</li> <li>Makes it work with <code>.EmitGameObjectBroadcast()</code> / <code>.EmitComponentBroadcast()</code></li> </ul>"},{"location":"reference/helpers/#2-dxautoconstructor---automatic-constructors","title":"2. <code>[DxAutoConstructor]</code> - Automatic Constructors","text":"<p>Problem: Writing constructors for every message is tedious and error-prone.</p> <p>Solution: Let the source generator do it!</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor] // \u2190 Magic happens here!\npublic readonly partial struct VideoSettingsChanged\n{\n public readonly int width;\n public readonly int height;\n}\n</code></pre>"},{"location":"reference/helpers/#what-you-get-auto-generated","title":"What you get (auto-generated)","text":"C#<pre><code>// You don't write this - it's generated for you!\npublic VideoSettingsChanged(int width, int height)\n{\n this.width = width;\n this.height = height;\n}\n</code></pre>"},{"location":"reference/helpers/#rules","title":"Rules","text":"<ul> <li>Creates constructor parameters in field declaration order</li> <li>Only includes <code>public</code> fields</li> <li>Ignores <code>static</code> fields</li> <li>Works with <code>readonly</code> structs (recommended!)</li> <li>Supports nested types (types defined inside other classes/structs)</li> <li>Supports internal types (types with <code>internal</code> visibility)</li> </ul>"},{"location":"reference/helpers/#3-dxoptionalparameter---optional-constructor-parameters","title":"3. <code>[DxOptionalParameter]</code> - Optional Constructor Parameters","text":"<p>The <code>DxOptionalParameterAttribute</code> marks a field as optional when used with <code>[DxAutoConstructor]</code>. The source generator emits a constructor parameter with a default value for annotated fields, making it easy to create messages with sensible defaults.</p>"},{"location":"reference/helpers/#how-it-works-with-dxautoconstructor","title":"How It Works with DxAutoConstructor","text":"<p>When you combine <code>[DxOptionalParameter]</code> with <code>[DxAutoConstructor]</code>, the generator creates a constructor where:</p> <ol> <li>Required fields (without the attribute) become regular constructor parameters</li> <li>Optional fields (with the attribute) become parameters with default values</li> <li>Parameter order follows field declaration order</li> <li>C# rules apply - optional parameters must come after required parameters</li> </ol> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SettingsChanged\n{\n public readonly float volume; // Required (no attribute)\n public readonly int quality; // Required (no attribute)\n [DxOptionalParameter] // Optional, defaults to false\n public readonly bool fullscreen;\n [DxOptionalParameter(100)] // Optional, defaults to 100\n public readonly int brightness;\n}\n</code></pre>"},{"location":"reference/helpers/#generated-constructor","title":"Generated constructor","text":"C#<pre><code>public SettingsChanged(float volume, int quality, bool fullscreen = default, int brightness = 100)\n{\n this.volume = volume;\n this.quality = quality;\n this.fullscreen = fullscreen;\n this.brightness = brightness;\n}\n</code></pre>"},{"location":"reference/helpers/#calling-the-constructor","title":"Calling the Constructor","text":"<p>Optional parameters support all standard C# calling conventions:</p> C#<pre><code>// Provide all arguments\nvar settings1 = new SettingsChanged(0.8f, 2, true, 80);\n\n// Use all defaults\nvar settings2 = new SettingsChanged(0.8f, 2); // fullscreen=false, brightness=100\n\n// Use named arguments to skip some optionals\nvar settings3 = new SettingsChanged(0.8f, 2, fullscreen: true); // brightness=100\nvar settings4 = new SettingsChanged(0.8f, 2, brightness: 50); // fullscreen=false\n</code></pre>"},{"location":"reference/helpers/#supported-default-value-types","title":"Supported Default Value Types","text":"<p>The attribute provides constructor overloads for all common primitive types:</p> <p>\u2139\ufe0f Info: Built-in Type Support</p> <p>Numeric:</p> Category Types Signed Integers <code>sbyte</code>, <code>short</code>, <code>int</code>, <code>long</code> Unsigned Integers <code>byte</code>, <code>ushort</code>, <code>uint</code>, <code>ulong</code> Floating Point <code>float</code>, <code>double</code> <p>Other:</p> Category Types Boolean <code>bool</code> Character <code>char</code> String <code>string</code> <p>For enums, Unity types, and other complex types, use the <code>Expression</code> property.</p>"},{"location":"reference/helpers/#examples-by-type","title":"Examples by type","text":"C#<pre><code>[DxAutoConstructor]\npublic readonly partial struct TypeExamples\n{\n // Boolean\n [DxOptionalParameter(true)]\n public readonly bool isEnabled;\n\n // Character\n [DxOptionalParameter('X')]\n public readonly char grade;\n\n // String\n [DxOptionalParameter(\"unknown\")]\n public readonly string playerName;\n\n // Signed integers\n [DxOptionalParameter((sbyte)-1)]\n public readonly sbyte signedByte;\n\n [DxOptionalParameter((short)100)]\n public readonly short shortValue;\n\n [DxOptionalParameter(42)]\n public readonly int score;\n\n [DxOptionalParameter(9999999999L)]\n public readonly long bigNumber;\n\n // Unsigned integers\n [DxOptionalParameter((byte)255)]\n public readonly byte level;\n\n [DxOptionalParameter((ushort)1000)]\n public readonly ushort maxHealth;\n\n [DxOptionalParameter(50u)]\n public readonly uint count;\n\n [DxOptionalParameter(100UL)]\n public readonly ulong totalPoints;\n\n // Floating point\n [DxOptionalParameter(1.5f)]\n public readonly float speed;\n\n [DxOptionalParameter(3.14159)]\n public readonly double precision;\n}\n</code></pre>"},{"location":"reference/helpers/#using-type-defaults","title":"Using Type Defaults","text":"<p>When you use <code>[DxOptionalParameter]</code> without a value, the field defaults to the type's default value:</p> C#<pre><code>[DxAutoConstructor]\npublic readonly partial struct DefaultExamples\n{\n [DxOptionalParameter] // Defaults to 0\n public readonly int count;\n\n [DxOptionalParameter] // Defaults to false\n public readonly bool isActive;\n\n [DxOptionalParameter] // Defaults to null\n public readonly string name;\n\n [DxOptionalParameter] // Defaults to 0.0f\n public readonly float multiplier;\n}\n</code></pre>"},{"location":"reference/helpers/#advanced-custom-expressions-for-any-type","title":"Advanced: Custom Expressions for Any Type","text":"<p>For types that don't have built-in constructor overloads (enums, nullables, Unity types, custom structs), use the <code>Expression</code> property. The expression is inserted verbatim into the generated constructor, and the C# compiler enforces type safety:</p> C#<pre><code>[DxAutoConstructor]\npublic readonly partial struct AdvancedDefaults\n{\n // Nullable types\n [DxOptionalParameter(Expression = \"null\")]\n public readonly int? optionalId;\n\n // Enum values\n [DxOptionalParameter(Expression = \"DamageType.Physical\")]\n public readonly DamageType damageType;\n\n // Unity types\n [DxOptionalParameter(Expression = \"Vector3.zero\")]\n public readonly Vector3 position;\n\n [DxOptionalParameter(Expression = \"Color.white\")]\n public readonly Color tint;\n\n // Static constants\n [DxOptionalParameter(Expression = \"float.MaxValue\")]\n public readonly float range;\n\n // Named constants from your code\n [DxOptionalParameter(Expression = \"GameConstants.DefaultHealth\")]\n public readonly int health;\n}\n</code></pre> <p>\ud83d\udca1 Tip: Expression rules</p> <ul> <li>The expression is inserted verbatim into the generated code</li> <li>Must be a valid C# constant expression</li> <li>Type safety is enforced by the C# compiler at build time</li> <li>Perfect for enums, nullable types, static constants, or complex defaults</li> </ul>"},{"location":"reference/helpers/#code-examples","title":"Code Examples","text":""},{"location":"reference/helpers/#example-1-basic-optional-parameter-with-default-value","title":"Example 1: Basic Optional Parameter with Default Value","text":"C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n [DxOptionalParameter(false)]\n public readonly bool isCritical;\n}\n\n// Usage:\nvar normalHeal = new Heal(25); // isCritical = false\nvar critHeal = new Heal(50, true); // isCritical = true\n</code></pre>"},{"location":"reference/helpers/#example-2-multiple-optional-parameters","title":"Example 2: Multiple Optional Parameters","text":"C#<pre><code>[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct SpawnEffect\n{\n [DxOptionalParameter(1.0f)]\n public readonly float scale;\n\n [DxOptionalParameter(Expression = \"Color.white\")]\n public readonly Color color;\n\n [DxOptionalParameter(5.0f)]\n public readonly float duration;\n\n [DxOptionalParameter(false)]\n public readonly bool looping;\n}\n\n// Usage:\nvar defaultEffect = new SpawnEffect(); // All defaults\nvar largeEffect = new SpawnEffect(2.5f); // Custom scale\nvar redEffect = new SpawnEffect(1.0f, Color.red); // Custom color\nvar customEffect = new SpawnEffect(1.5f, Color.blue, 10f, true); // All custom\n</code></pre>"},{"location":"reference/helpers/#example-3-mix-of-required-and-optional-fields","title":"Example 3: Mix of Required and Optional Fields","text":"C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct GameEvent\n{\n // Required fields (must be provided)\n public readonly string eventName;\n public readonly int playerId;\n\n // Optional fields (have defaults)\n [DxOptionalParameter(0)]\n public readonly int score;\n\n [DxOptionalParameter(\"\")]\n public readonly string metadata;\n\n [DxOptionalParameter(Expression = \"System.DateTime.UtcNow\")]\n public readonly System.DateTime timestamp;\n}\n\n// Usage:\nvar minimalEvent = new GameEvent(\"LevelStart\", 1);\nvar scoredEvent = new GameEvent(\"EnemyKilled\", 1, 100);\nvar fullEvent = new GameEvent(\"Achievement\", 1, 500, \"first_blood\");\n</code></pre>"},{"location":"reference/helpers/#example-4-game-settings-message","title":"Example 4: Game Settings Message","text":"C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct AudioSettingsChanged\n{\n [DxOptionalParameter(1.0f)]\n public readonly float masterVolume;\n\n [DxOptionalParameter(0.8f)]\n public readonly float musicVolume;\n\n [DxOptionalParameter(1.0f)]\n public readonly float sfxVolume;\n\n [DxOptionalParameter(true)]\n public readonly bool muteWhenUnfocused;\n}\n\n// Apply all defaults\naudioSettings.Emit();\n\n// Only change music volume\nnew AudioSettingsChanged(musicVolume: 0.5f).Emit();\n\n// Change multiple settings\nnew AudioSettingsChanged(0.7f, 0.6f, 0.9f, false).Emit();\n</code></pre>"},{"location":"reference/helpers/#best-practices","title":"Best Practices","text":"<p>\u2705 Success: Recommendations</p> <ol> <li>Order matters \u2014 Place required fields before optional fields in your struct</li> <li>Use meaningful defaults \u2014 Choose defaults that represent the most common use case</li> <li>Prefer explicit values \u2014 Use <code>[DxOptionalParameter(0)]</code> instead of <code>[DxOptionalParameter]</code> when clarity helps</li> <li>Use Expression for complex types \u2014 Don't fight the type system; use <code>Expression</code> for enums and Unity types</li> <li>Document unusual defaults \u2014 If a default isn't obvious, add a comment explaining why</li> </ol>"},{"location":"reference/helpers/#why-use-attributes-instead-of-manual-implementation","title":"Why Use Attributes Instead of Manual Implementation","text":""},{"location":"reference/helpers/#-attribute-definition-clean-automatic","title":"\u2705 Attribute Definition (Clean, Automatic)","text":"C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#what-the-generator-emits-for-reference","title":"What the generator emits (for reference)","text":"C#<pre><code>// Auto-generated by DxMessaging (no need to hand-write this)\npublic readonly partial struct Heal : ITargetedMessage<Heal>\n{\n public readonly int amount;\n\n public Heal(int amount)\n {\n this.amount = amount;\n }\n\n public Type MessageType => typeof(Heal);\n}\n</code></pre>"},{"location":"reference/helpers/#benefits","title":"Benefits","text":"<ul> <li>\u2705 Less code - 50% fewer lines</li> <li>\u2705 Fewer bugs - Can't forget fields in constructor</li> <li>\u2705 Focused - Focus on data, not boilerplate</li> <li>\u2705 Refactor-safe - Add field? Constructor updates automatically!</li> </ul>"},{"location":"reference/helpers/#complete-example-attribute-definition-vs-generated-output","title":"Complete Example: Attribute Definition vs Generated Output","text":""},{"location":"reference/helpers/#attribute-definition-8-lines","title":"Attribute Definition (8 lines)","text":"C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct PlayerDamaged\n{\n public readonly int amount;\n public readonly string damageType;\n public readonly GameObject source;\n}\n</code></pre>"},{"location":"reference/helpers/#generated-output-20-lines-you-never-write","title":"Generated Output (20 lines you never write)","text":"C#<pre><code>// Auto-generated by DxMessaging (for reference only)\npublic readonly partial struct PlayerDamaged : IBroadcastMessage<PlayerDamaged>\n{\n public readonly int amount;\n public readonly string damageType;\n public readonly GameObject source;\n\n public PlayerDamaged(int amount, string damageType, GameObject source)\n {\n this.amount = amount;\n this.damageType = damageType;\n this.source = source;\n }\n\n public Type MessageType => typeof(PlayerDamaged);\n}\n</code></pre>"},{"location":"reference/helpers/#result","title":"Result","text":"<ul> <li>\u2705 Same functionality</li> <li>\u2705 Less code to maintain</li> <li>\u2705 Automatically updates when you add/remove fields</li> <li>\u2705 Works for class messages too</li> <li>\u2705 Zero effort once you mark the struct partial</li> </ul>"},{"location":"reference/helpers/#advanced-manual-implementation-when-attributes-arent-enough","title":"Advanced: Manual Implementation (When Attributes Aren't Enough)","text":"<p>Attributes cover almost every scenario. If you intentionally drop <code>[DxTargetedMessage]</code>, <code>[DxUntargetedMessage]</code>, or <code>[DxBroadcastMessage]</code>, you'll need to hand-write the interface implementations and constructors shown in the \u201cgenerated output\u201d examples. Keep the attributes unless you have a very specific data-backed reason not to.</p>"},{"location":"reference/helpers/#generic-message-interfaces-zero-boxing-for-structs","title":"Generic Message Interfaces (Zero-Boxing for Structs)","text":"<p><code>readonly struct</code> messages marked with the attributes already implement the generic interfaces, so emissions stay allocation-free. You get the same performance characteristics as the manual approach without writing any plumbing.</p> C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#do-i-have-to-use-attributes","title":"\"Do I HAVE to use attributes?\"","text":"<p>Technically no\u2014but without them you must write the constructor, interface implementation, and <code>MessageType</code> property yourself (for speed, you can optionally leave this off, but it might box on certain call paths). Leaving the attributes on keeps everything consistent for the whole team.</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct MyMsg { }\n</code></pre>"},{"location":"reference/helpers/#what-if-i-want-custom-constructor-logic","title":"\"What if I want custom constructor logic?\"","text":"<p>Keep the attributes and add a factory/helper so you still benefit from the generated constructor:</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct ComplexMessage\n{\n public readonly int value;\n\n public static ComplexMessage FromRaw(int rawValue)\n {\n int clamped = Math.Clamp(rawValue, 0, 100);\n return new ComplexMessage(clamped);\n }\n}\n</code></pre> <p>If you truly must write a custom constructor, drop <code>[DxAutoConstructor]</code> for that type but keep the <code>[DxUntargetedMessage]</code>/<code>[DxTargetedMessage]</code> attribute so the interface plumbing stays consistent.</p>"},{"location":"reference/helpers/#can-i-mix-attribute-based-and-manual-messages","title":"\"Can I mix attribute-based and manual messages?\"","text":"<p>Yes. Attribute-driven messages happily coexist with string messages and any manual implementations you already have. You can migrate gradually by converting one message at a time:</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct MessageA\n{\n public readonly int value;\n}\n\n// Existing manual message types keep working alongside attribute-driven ones.\n</code></pre>"},{"location":"reference/helpers/#extension-methods-emit-helpers","title":"Extension Methods (Emit Helpers)","text":"<p>DxMessaging provides extension methods to make emitting messages easy:</p> C#<pre><code>using DxMessaging.Core.Extensions; // \u2190 Don't forget this!\nusing UnityEngine;\n\n// Untargeted (global)\nvar settings = new VideoSettingsChanged(1920, 1080);\nsettings.Emit();\n\n// Targeted - GameObject overload\nvar heal = new Heal(10);\nheal.EmitGameObjectTargeted(playerGameObject);\n\n// Targeted - Component overload\nheal.EmitComponentTargeted(playerComponent);\n\n// Broadcast - GameObject overload\nvar dmg = new TookDamage(25);\ndmg.EmitGameObjectBroadcast(enemyGameObject);\n\n// Broadcast - Component overload\ndmg.EmitComponentBroadcast(enemyComponent);\n\n// String convenience (for quick prototyping)\n\"LevelCompleted\".Emit();\n</code></pre> <p>Located in: <code>DxMessaging.Core.Extensions.MessageExtensions</code></p>"},{"location":"reference/helpers/#automatic-overload-selection","title":"Automatic overload selection","text":"<ul> <li>Extension methods pick the right overload based on type</li> <li>Defaults to global <code>MessageHandler.MessageBus</code></li> <li>Pass custom bus with optional parameter</li> </ul>"},{"location":"reference/helpers/#local-bus-islands-isolated-testing","title":"Local Bus Islands (Isolated Testing)","text":"<p>Create isolated message buses for tests or subsystems:</p> C#<pre><code>using DxMessaging.Core;\nusing DxMessaging.Core.MessageBus;\n\n// Create isolated bus\nvar testBus = new MessageBus();\nvar handler = new MessageHandler(new InstanceId(1), testBus) { active = true };\nvar token = MessageRegistrationToken.Create(handler, testBus);\n\n// Register on isolated bus\n_ = token.RegisterUntargeted<MyMessage>(OnMessage);\n\n// Emit to isolated bus (won't affect global bus!)\nvar msg = new MyMessage();\nmsg.Emit(testBus);\n</code></pre>"},{"location":"reference/helpers/#use-cases","title":"Use cases","text":"<ul> <li>Unit tests (no global side effects!)</li> <li>Subsystem isolation (UI has own bus)</li> <li>Sandboxing (mod systems, untrusted code)</li> </ul>"},{"location":"reference/helpers/#attributes-quick-reference","title":"Attributes Quick Reference","text":""},{"location":"reference/helpers/#message-type-attributes","title":"Message Type Attributes","text":""},{"location":"reference/helpers/#untargeted","title":"Untargeted","text":"<p>Mark as a global message (no target):</p> C#<pre><code>[DxUntargetedMessage]\npublic readonly partial struct GamePaused { }\n</code></pre>"},{"location":"reference/helpers/#targeted","title":"Targeted","text":"<p>Mark as a message sent to a specific target:</p> C#<pre><code>[DxTargetedMessage]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#broadcast","title":"Broadcast","text":"<p>Mark as a message broadcast from a source:</p> C#<pre><code>[DxBroadcastMessage]\npublic readonly partial struct TookDamage\n{\n public readonly int amount;\n}\n</code></pre>"},{"location":"reference/helpers/#constructor-generation-attributes","title":"Constructor Generation Attributes","text":""},{"location":"reference/helpers/#dxautoconstructor","title":"<code>[DxAutoConstructor]</code>","text":"<p>Automatically generates a constructor with all public fields as parameters:</p> C#<pre><code>[DxAutoConstructor]\npublic readonly partial struct Damage\n{\n public readonly int amount;\n public readonly string type;\n}\n// Generates: Damage(int amount, string type)\n</code></pre>"},{"location":"reference/helpers/#dxoptionalparameter","title":"<code>[DxOptionalParameter]</code>","text":"<p>Makes a constructor parameter optional. Three usage patterns:</p> <p>\ud83d\udccb Example: Default Value (type default)</p> <p>Uses the type's default value (<code>0</code>, <code>false</code>, <code>null</code>, etc.):</p> C#<pre><code>[DxOptionalParameter]\npublic readonly bool flag;\n// Generates: bool flag = default\n</code></pre> <p>\ud83d\udccb Example: Custom Value (primitive)</p> <p>Provides a specific default value:</p> C#<pre><code>[DxOptionalParameter(42)]\npublic readonly int count;\n// Generates: int count = 42\n</code></pre> <p>\ud83d\udccb Example: Expression (complex types)</p> <p>Uses a verbatim expression for enums, Unity types, etc.:</p> C#<pre><code>[DxOptionalParameter(Expression = \"DamageType.Physical\")]\npublic readonly DamageType damageType;\n// Generates: DamageType damageType = DamageType.Physical\n</code></pre>"},{"location":"reference/helpers/#supported-types","title":"Supported Types","text":"<p>These attributes work with:</p> <ul> <li>Top-level types \u2014 public structs and classes</li> <li>Nested types \u2014 types inside other classes</li> <li>Internal types \u2014 assembly-private messages</li> </ul>"},{"location":"reference/helpers/#common-patterns-with-attributes","title":"Common Patterns with Attributes","text":""},{"location":"reference/helpers/#pattern-1-simple-message-no-data","title":"Pattern 1: Simple Message (No Data)","text":"C#<pre><code>[DxUntargetedMessage]\npublic readonly partial struct GamePaused { }\n// No constructor needed - empty message\n</code></pre>"},{"location":"reference/helpers/#pattern-2-message-with-data","title":"Pattern 2: Message with Data","text":"C#<pre><code>[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal\n{\n public readonly int amount;\n}\n// Auto-generates: Heal(int amount)\n</code></pre>"},{"location":"reference/helpers/#pattern-3-message-with-optional-fields","title":"Pattern 3: Message with Optional Fields","text":"C#<pre><code>[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct Attack\n{\n public readonly int damage;\n [DxOptionalParameter]\n public readonly string damageType;\n}\n// Auto-generates: Attack(int damage, string damageType = default)\n</code></pre>"},{"location":"reference/helpers/#pattern-4-complex-message","title":"Pattern 4: Complex Message","text":"C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct LevelCompleted\n{\n public readonly int level;\n public readonly float time;\n public readonly int score;\n [DxOptionalParameter]\n public readonly bool perfectRun;\n [DxOptionalParameter]\n public readonly string bonusReason;\n}\n// All fields become constructor parameters, last two are optional\n</code></pre>"},{"location":"reference/helpers/#pattern-5-nested-types","title":"Pattern 5: Nested Types","text":"<p>Define messages inside other classes for better organization:</p> C#<pre><code>public partial class GameEvents\n{\n [DxUntargetedMessage]\n [DxAutoConstructor]\n public readonly partial struct LevelUp\n {\n public readonly int newLevel;\n }\n\n [DxTargetedMessage]\n [DxAutoConstructor]\n public readonly partial struct GainExperience\n {\n public readonly int amount;\n }\n}\n\n// Usage:\nvar levelUp = new GameEvents.LevelUp(5);\nlevelUp.Emit();\n</code></pre>"},{"location":"reference/helpers/#benefits_1","title":"Benefits","text":"<ul> <li>Organizes related messages into namespaces or classes</li> <li>Reduces global namespace pollution</li> <li>Works identically to top-level messages</li> </ul>"},{"location":"reference/helpers/#pattern-6-internal-types-assembly-private-messages","title":"Pattern 6: Internal Types (Assembly-Private Messages)","text":"<p>Keep implementation details private to your assembly:</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\ninternal readonly partial struct InternalDebugMessage\n{\n public readonly string debugInfo;\n}\n\n// Only visible within this assembly\n// Perfect for internal messaging that shouldn't leak to other packages\n</code></pre>"},{"location":"reference/helpers/#use-cases_1","title":"Use cases","text":"<ul> <li>Implementation details that shouldn't be public API</li> <li>Plugin or package-internal messaging</li> <li>Test-only messages</li> </ul>"},{"location":"reference/helpers/#faq-source-generators--attributes","title":"FAQ: Source Generators & Attributes","text":""},{"location":"reference/helpers/#do-i-have-to-use-attributes_1","title":"\"Do I HAVE to use attributes?\"","text":"<p>No! You can implement interfaces manually:</p> C#<pre><code>// Manual (works fine, just more code)\npublic readonly struct MyMsg : IUntargetedMessage<MyMsg>\n{\n public Type MessageType => typeof(MyMsg);\n}\n\n// Attributes (same result, less code)\n[DxUntargetedMessage]\npublic readonly partial struct MyMsg { }\n</code></pre> <p>Recommendation: Use attributes unless you need explicit control.</p>"},{"location":"reference/helpers/#why-partial-when-using-attributes","title":"\"Why <code>partial</code> when using attributes?\"","text":"<p>Source generators need to add code to your type. The <code>partial</code> keyword allows them to extend your struct/class in a separate file.</p> C#<pre><code>// Your file (MyMessage.cs)\n[DxUntargetedMessage]\npublic readonly partial struct MyMessage { }\n\n// Generated file (MyMessage.g.cs - auto-created!)\npublic readonly partial struct MyMessage : IUntargetedMessage<MyMessage>\n{\n public Type MessageType => typeof(MyMessage);\n}\n</code></pre> <p>Forget <code>partial</code>? You'll get a compile error: \"Type must be partial to use source generators\"</p>"},{"location":"reference/helpers/#can-i-see-the-generated-code","title":"\"Can I see the generated code?\"","text":"<p>Yes! In Visual Studio/Rider:</p> <ol> <li>Right-click on the message type</li> <li>Select \"Go to Implementation\" or \"Go to Definition\"</li> <li>You'll see the auto-generated file</li> </ol> <p>Or check your <code>obj/</code> folder for <code>.g.cs</code> files.</p>"},{"location":"reference/helpers/#what-if-i-want-custom-constructor-logic_1","title":"\"What if I want custom constructor logic?\"","text":"<p>Keep the attributes and wrap the generated constructor with a helper so you can inject custom logic without losing the source-generated plumbing:</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct ComplexMessage\n{\n public readonly int value;\n\n public static ComplexMessage FromRaw(int rawValue)\n {\n int clamped = Math.Clamp(rawValue, 0, 100);\n return new ComplexMessage(clamped);\n }\n}\n</code></pre> <p>If you truly need to hand-craft the constructor, drop <code>[DxAutoConstructor]</code> for that specific type but keep the <code>[DxUntargetedMessage]</code>/<code>[DxTargetedMessage]</code> attribute so the interface implementation is still generated.</p>"},{"location":"reference/helpers/#do-attributes-affect-runtime-performance","title":"\"Do attributes affect runtime performance?\"","text":"<p>No! Source generation happens at compile time. Generated code is identical to hand-written code. Zero runtime overhead.</p>"},{"location":"reference/helpers/#can-i-mix-attributes-and-manual-implementation","title":"\"Can I mix attributes and manual implementation?\"","text":"<p>Yes. Attribute-driven messages happily coexist with any legacy manual messages or string messages you already emit. Convert types gradually\u2014one message at a time:</p> C#<pre><code>[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct MessageA\n{\n public readonly int value;\n}\n\n// Existing manual messages keep working alongside attribute-driven ones.\n</code></pre>"},{"location":"reference/helpers/#troubleshooting-source-generators","title":"Troubleshooting Source Generators","text":"<p>\u26a0\ufe0f Warning: Attributes not working / code not generated</p>"},{"location":"reference/helpers/#checklist","title":"Checklist","text":"<ol> <li>\u2705 Is type marked <code>partial</code>?</li> <li>\u2705 Did you rebuild the project?</li> <li>\u2705 Is Unity 2021.3+ (Roslyn source generator support)?</li> <li>\u2705 Check <code>obj/</code> folder for <code>.g.cs</code> files</li> </ol>"},{"location":"reference/helpers/#fix","title":"Fix","text":"C#<pre><code>// \u274c Missing partial, will not compile\n[DxAutoConstructor]\npublic readonly struct MyMsg { }\n\n// \u2705 Correct\n[DxAutoConstructor]\npublic readonly partial struct MyMsg { }\n</code></pre> <p>\u26a0\ufe0f Warning: Constructor not generated</p> <p>Cause: No public fields to generate from</p> C#<pre><code>// \u274c No public fields\n[DxAutoConstructor]\npublic readonly partial struct Empty { }\n\n// \u2705 Has public field\n[DxAutoConstructor]\npublic readonly partial struct WithData {\n public readonly int value;\n}\n</code></pre> <p>\u26a0\ufe0f Warning: Unity can't find generated code</p>"},{"location":"reference/helpers/#solution","title":"Solution","text":"<ol> <li>Close Unity</li> <li>Delete <code>Library/</code> folder</li> <li>Reopen Unity</li> <li>Let it reimport everything</li> </ol>"},{"location":"reference/helpers/#related-documentation","title":"Related Documentation","text":"<ul> <li>API Reference \u2014 Complete API documentation</li> <li>Message Types \u2014 When to use Untargeted/Targeted/Broadcast</li> <li>Quick Reference \u2014 Cheat sheet</li> <li>Design & Architecture \u2014 How source generation works internally</li> </ul>"},{"location":"reference/helpers/#summary","title":"Summary","text":""},{"location":"reference/helpers/#source-generators--code-wizards-that-write-boilerplate-for-you","title":"Source generators = Code wizards that write boilerplate for you","text":""},{"location":"reference/helpers/#use-attributes-for","title":"Use attributes for","text":"<ul> <li>\u2705 Clean, maintainable code</li> <li>\u2705 Automatic constructor generation</li> <li>\u2705 Zero boilerplate</li> <li>\u2705 Refactor safety</li> </ul>"},{"location":"reference/helpers/#use-manual-implementation-for","title":"Use manual implementation for","text":"<ul> <li>\u2705 Custom constructor logic</li> <li>\u2705 Explicit control</li> <li>\u2705 Understanding exactly what happens</li> </ul> <p>Recommendation: Start with attributes (they cover most cases), switch to manual only when needed.</p>"},{"location":"reference/quick-reference/","title":"Quick Reference (Cheat Sheet)","text":"<p>Use this as a rapid guide to define/emit/listen and manage lifecycles.</p> <p>Do\u2019s</p> <ul> <li>Use attributes + <code>DxAutoConstructor</code> for clarity (or interfaces on structs for perf).</li> <li>Bind struct messages to a variable before emitting.</li> <li>Use GameObject/Component emit helpers (no manual <code>InstanceId</code>).</li> <li>Register once; enable/disable with component state.</li> <li>Prefer named handler methods over inline lambdas for reuse and clarity.</li> <li>When using DI, inject <code>IMessageRegistrationBuilder</code> instead of newing <code>MessageHandler</code>s manually.</li> </ul>"},{"location":"reference/quick-reference/#donts","title":"Don'ts","text":"<ul> <li>Don\u2019t emit from temporaries; use a local variable (e.g., <code>var msg = new M(...); msg.Emit();</code>).</li> <li>Don\u2019t mix Component vs GameObject targeting if you expect matches (see targeting notes below).</li> <li>Don\u2019t register in Update; use <code>Awake</code> for staging + <code>OnEnable</code>/<code>OnDisable</code> for lifecycle.</li> <li>Don\u2019t forget base calls when inheriting from <code>MessageAwareComponent</code> \u2014 call <code>base.RegisterMessageHandlers()</code> and <code>base.OnEnable()</code>/<code>base.OnDisable()</code>.</li> <li>Don\u2019t hide Unity methods with <code>new</code> (e.g., <code>new void OnEnable()</code>); prefer <code>override</code> and call <code>base.*</code>.</li> </ul>"},{"location":"reference/quick-reference/#define-messages","title":"Define messages","text":"C#<pre><code>using DxMessaging.Core.Attributes;\n\n[DxUntargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct SceneLoaded { public readonly int buildIndex; }\n\n[DxTargetedMessage]\n[DxAutoConstructor]\npublic readonly partial struct Heal { public readonly int amount; }\n\n[DxBroadcastMessage]\n[DxAutoConstructor]\npublic readonly partial struct TookDamage { public readonly int amount; }\n</code></pre>"},{"location":"reference/quick-reference/#emit-unity-helpers","title":"Emit (Unity helpers)","text":"C#<pre><code>using DxMessaging.Core.Extensions;\n\nvar scene = new SceneLoaded(1); scene.Emit();\nvar heal = new Heal(10); heal.EmitGameObjectTargeted(gameObject);\nvar hit = new TookDamage(5); hit.EmitComponentBroadcast(this);\n\n// String shorthands\n\"Saved\".Emit(); // GlobalStringMessage\n\"Hello\".EmitAt(gameObject); // StringMessage to GO (or .Emit(instanceId))\n\"Hit\".EmitFrom(gameObject); // SourcedStringMessage from GO\n</code></pre>"},{"location":"reference/quick-reference/#register-unity-via-token","title":"Register (Unity, via token)","text":"C#<pre><code>using DxMessaging.Core; // InstanceId\n// Untargeted\n_ = token.RegisterUntargeted<SceneLoaded>(OnSceneLoaded);\nvoid OnSceneLoaded(ref SceneLoaded m) { /* ... */ }\n\n// Targeted: to this component or gameObject\n_ = token.RegisterComponentTargeted<Heal>(this, OnHeal);\n_ = token.RegisterGameObjectTargeted<Heal>(gameObject, OnHeal);\nvoid OnHeal(ref Heal m) { /* ... */ }\n\n// Broadcast: from this component or gameObject\n_ = token.RegisterComponentBroadcast<TookDamage>(this, OnDamageFromMe);\n_ = token.RegisterGameObjectBroadcast<TookDamage>(gameObject, OnDamageFromMe);\nvoid OnDamageFromMe(ref TookDamage m) { /* ... */ }\n\n// Listen to all targets/sources\n_ = token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);\nvoid OnAnyHeal(ref InstanceId target, ref Heal m) { /* ... */ }\n\n_ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);\nvoid OnAnyDamage(ref InstanceId src, ref TookDamage m) { /* ... */ }\n</code></pre>"},{"location":"reference/quick-reference/#register-di--services","title":"Register (DI / services)","text":"C#<pre><code>using DxMessaging.Core.MessageBus;\n\npublic sealed class DamageSystem : IStartable, IDisposable\n{\n private readonly MessageRegistrationLease lease;\n\n public DamageSystem(IMessageRegistrationBuilder registrationBuilder)\n {\n lease = registrationBuilder.Build(new MessageRegistrationBuildOptions\n {\n Configure = token =>\n {\n _ = token.RegisterUntargeted<TookDamage>(OnDamage);\n }\n });\n }\n\n public void Start() => lease.Activate();\n\n public void Dispose() => lease.Dispose();\n\n private static void OnDamage(ref TookDamage message) { /* respond */ }\n}\n</code></pre> <p>Tip: Define <code>ZENJECT_PRESENT</code>, <code>VCONTAINER_PRESENT</code>, or <code>REFLEX_PRESENT</code> to enable the optional shims under Runtime/Unity/Integrations that bind the builder automatically for those containers.</p>"},{"location":"reference/quick-reference/#interceptors-and-postprocessors","title":"Interceptors and post\u2011processors","text":"C#<pre><code>using DxMessaging.Core; // MessageHandler\nusing DxMessaging.Core.MessageBus; // IMessageBus\n\nvar bus = MessageHandler.MessageBus;\n_ = bus.RegisterBroadcastInterceptor<TookDamage>((ref InstanceId src, ref TookDamage m) =>\n{\n if (m.amount <= 0) return false; // cancel\n m = new TookDamage(Math.Min(m.amount, 999));\n return true;\n});\n\n_ = token.RegisterUntargetedPostProcessor<SceneLoaded>((ref SceneLoaded m) => LogScene(m.buildIndex));\n</code></pre>"},{"location":"reference/quick-reference/#lifecycle","title":"Lifecycle","text":"C#<pre><code>void Awake() { /* stage registrations */ }\nvoid OnEnable() { token.Enable(); }\nvoid OnDisable() { token.Disable(); }\n</code></pre>"},{"location":"reference/quick-reference/#inheritance-tip-messageawarecomponent","title":"Inheritance tip (MessageAwareComponent)","text":"<ul> <li>If you override <code>RegisterMessageHandlers</code>, start with <code>base.RegisterMessageHandlers()</code>.</li> <li>If you override Unity lifecycle methods, call <code>base.OnEnable()</code> / <code>base.OnDisable()</code> (and <code>base.Awake()</code>/<code>base.OnDestroy()</code> if overridden).</li> </ul>"},{"location":"reference/quick-reference/#targeting-notes-component-vs-gameobject","title":"Targeting notes (Component vs GameObject)","text":"<ul> <li>A targeted message matches if the emitted <code>InstanceId</code> equals the registered <code>InstanceId</code>.</li> <li>Registering for a Component target listens for messages targeted at that specific Component.</li> <li>Registering for a GameObject target listens for messages targeted at that GameObject.</li> <li>Emitting to a GameObject will not reach Component\u2011targeted listeners (and vice\u2011versa). Use the matching helper.</li> <li>Shorthands exist for strings too; be explicit about using a GameObject vs Component with <code>EmitAt</code>/<code>EmitFrom</code>.</li> </ul>"},{"location":"reference/quick-reference/#see-also","title":"See also","text":"<ul> <li>Emit Shorthands</li> <li>Advanced</li> <li>Targeting & Context</li> <li>Interceptors & Ordering</li> </ul>"},{"location":"reference/quick-reference/#execution-order","title":"Execution Order","text":""},{"location":"reference/quick-reference/#untargeted","title":"Untargeted","text":"Text Only<pre><code>Interceptors \u2192 Global Accept-All \u2192 Handlers<T> \u2192 Post-Processors<T>\n</code></pre>"},{"location":"reference/quick-reference/#targeted","title":"Targeted","text":"Text Only<pre><code>Interceptors \u2192 Global Accept-All \u2192 Handlers<T> @ target\n \u2192 Handlers<T> (All Targets) \u2192 Post-Processors<T> @ target\n \u2192 Post-Processors<T> (All Targets)\n</code></pre>"},{"location":"reference/quick-reference/#broadcast","title":"Broadcast","text":"Text Only<pre><code>Interceptors \u2192 Global Accept-All \u2192 Handlers<T> @ source\n \u2192 Handlers<T> (All Sources) \u2192 Post-Processors<T> @ source\n \u2192 Post-Processors<T> (All Sources)\n</code></pre> <p>\ud83d\udcdd Note: Priority Rules</p> <ul> <li>Lower priority values run earlier</li> <li>Same priority preserves registration order</li> <li>Within a priority, fast (by-ref) handlers run before action handlers</li> </ul>"},{"location":"reference/quick-reference/#api-quick-reference","title":"API Quick Reference","text":""},{"location":"reference/quick-reference/#token-untargeted","title":"Token: Untargeted","text":"C#<pre><code>// Register handler\ntoken.RegisterUntargeted<T>(Action<T> handler, int priority = 0)\ntoken.RegisterUntargeted<T>(FastHandler<T> handler, int priority = 0)\n\n// Post-processor\ntoken.RegisterUntargetedPostProcessor<T>(FastHandler<T> handler, int priority = 0)\n</code></pre>"},{"location":"reference/quick-reference/#token-targeted-specific","title":"Token: Targeted (Specific)","text":"C#<pre><code>// Register for specific target\ntoken.RegisterGameObjectTargeted<T>(GameObject go, handler, int priority = 0)\ntoken.RegisterComponentTargeted<T>(Component c, handler, int priority = 0)\ntoken.RegisterTargeted<T>(InstanceId id, handler, int priority = 0)\n\n// Post-processor\ntoken.RegisterTargetedPostProcessor<T>(InstanceId id, FastHandler<T> handler, int priority = 0)\n</code></pre>"},{"location":"reference/quick-reference/#token-targeted-all-targets","title":"Token: Targeted (All Targets)","text":"C#<pre><code>// Listen to messages for any target\ntoken.RegisterTargetedWithoutTargeting<T>(FastHandlerWithContext<T> handler, int priority = 0)\n\n// Post-processor\ntoken.RegisterTargetedWithoutTargetingPostProcessor<T>(FastHandlerWithContext<T> handler, int priority = 0)\n</code></pre>"},{"location":"reference/quick-reference/#token-broadcast-specific","title":"Token: Broadcast (Specific)","text":"C#<pre><code>// Register for specific source\ntoken.RegisterGameObjectBroadcast<T>(GameObject go, handler, int priority = 0)\ntoken.RegisterComponentBroadcast<T>(Component c, handler, int priority = 0)\ntoken.RegisterBroadcast<T>(InstanceId id, handler, int priority = 0)\n\n// Post-processor\ntoken.RegisterBroadcastPostProcessor<T>(InstanceId id, FastHandler<T> handler, int priority = 0)\n</code></pre>"},{"location":"reference/quick-reference/#token-broadcast-all-sources","title":"Token: Broadcast (All Sources)","text":"C#<pre><code>// Listen to broadcasts from any source\ntoken.RegisterBroadcastWithoutSource<T>(FastHandlerWithContext<T> handler, int priority = 0)\n\n// Post-processor\ntoken.RegisterBroadcastWithoutSourcePostProcessor<T>(FastHandlerWithContext<T> handler, int priority = 0)\n</code></pre>"},{"location":"reference/quick-reference/#token-global-observer","title":"Token: Global Observer","text":"C#<pre><code>// Action-based\ntoken.RegisterGlobalAcceptAll(\n Action<IUntargetedMessage> untargeted,\n Action<InstanceId, ITargetedMessage> targeted,\n Action<InstanceId, IBroadcastMessage> broadcast)\n\n// Fast handler-based\ntoken.RegisterGlobalAcceptAll(\n FastHandler<IUntargetedMessage> untargeted,\n FastHandlerWithContext<ITargetedMessage> targeted,\n FastHandlerWithContext<IBroadcastMessage> broadcast)\n</code></pre>"},{"location":"reference/quick-reference/#bus-interceptors","title":"Bus: Interceptors","text":"C#<pre><code>// Type-specific interceptors (return false to cancel)\nbus.RegisterUntargetedInterceptor<T>(UntargetedInterceptor<T> interceptor, int priority = 0)\nbus.RegisterTargetedInterceptor<T>(TargetedInterceptor<T> interceptor, int priority = 0)\nbus.RegisterBroadcastInterceptor<T>(BroadcastInterceptor<T> interceptor, int priority = 0)\n\n// Bus-level global observer\nbus.RegisterGlobalAcceptAll(MessageHandler handler)\n</code></pre>"},{"location":"reference/reference/","title":"API Reference (Practical)","text":"<p>This reference provides a practical overview of the DxMessaging API for Unity developers.</p>"},{"location":"reference/reference/#message-registration-unity-friendly","title":"Message Registration (Unity-Friendly)","text":"<p>The <code>MessageRegistrationToken</code> is your primary interface for subscribing to messages in a managed, lifecycle-aware manner.</p>"},{"location":"reference/reference/#token-lifecycle-methods","title":"Token Lifecycle Methods","text":"C#<pre><code>// Enable/disable all registrations on this token\ntoken.Enable();\ntoken.Disable();\n\n// Remove all registrations\ntoken.UnregisterAll();\n\n// Remove a specific registration\ntoken.RemoveRegistration(handle);\n</code></pre>"},{"location":"reference/reference/#untargeted-message-registration","title":"Untargeted Message Registration","text":"<p>Register handlers for messages that have no specific target\u2014system-wide events.</p> C#<pre><code>// Standard handler (allocation-friendly for simple cases)\nMessageRegistrationHandle RegisterUntargeted<T>(\n Action<T> handler,\n int priority = 0\n)\n\n// Fast handler (zero-allocation, receives message by ref)\nMessageRegistrationHandle RegisterUntargeted<T>(\n MessageHandler.FastHandler<T> handler,\n int priority = 0\n)\n\n// Post-processor (runs after all handlers)\nMessageRegistrationHandle RegisterUntargetedPostProcessor<T>(\n MessageHandler.FastHandler<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#targeted-message-registration","title":"Targeted Message Registration","text":"<p>Register handlers for messages directed at specific GameObjects, Components, or InstanceIds.</p>"},{"location":"reference/reference/#specific-target","title":"Specific Target","text":"C#<pre><code>// GameObject target\nMessageRegistrationHandle RegisterGameObjectTargeted<T>(\n GameObject target,\n Action<T> handler,\n int priority = 0\n)\n\n// Component target\nMessageRegistrationHandle RegisterComponentTargeted<T>(\n Component target,\n Action<T> handler,\n int priority = 0\n)\n\n// InstanceId target (low-level)\nMessageRegistrationHandle RegisterTargeted<T>(\n InstanceId target,\n Action<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#all-targets","title":"All Targets","text":"C#<pre><code>// Receive all targeted messages regardless of target\nMessageRegistrationHandle RegisterTargetedWithoutTargeting<T>(\n FastHandlerWithContext<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#post-processors","title":"Post-Processors","text":"C#<pre><code>// Post-process for specific target\nMessageRegistrationHandle RegisterTargetedPostProcessor<T>(\n InstanceId target,\n FastHandler<T> handler,\n int priority = 0\n)\n\n// Post-process all targeted messages\nMessageRegistrationHandle RegisterTargetedWithoutTargetingPostProcessor<T>(\n FastHandlerWithContext<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#broadcast-message-registration","title":"Broadcast Message Registration","text":"<p>Register handlers for messages broadcast from specific sources.</p>"},{"location":"reference/reference/#specific-source","title":"Specific Source","text":"C#<pre><code>// From specific GameObject\nMessageRegistrationHandle RegisterGameObjectBroadcast<T>(\n GameObject source,\n Action<T> handler,\n int priority = 0\n)\n\n// From specific Component\nMessageRegistrationHandle RegisterComponentBroadcast<T>(\n Component source,\n Action<T> handler,\n int priority = 0\n)\n\n// From specific InstanceId\nMessageRegistrationHandle RegisterBroadcast<T>(\n InstanceId source,\n Action<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#all-sources","title":"All Sources","text":"C#<pre><code>// Receive broadcasts from any source\nMessageRegistrationHandle RegisterBroadcastWithoutSource<T>(\n FastHandlerWithContext<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#post-processors_1","title":"Post-Processors","text":"C#<pre><code>// Post-process for specific source\nMessageRegistrationHandle RegisterBroadcastPostProcessor<T>(\n InstanceId source,\n FastHandler<T> handler,\n int priority = 0\n)\n\n// Post-process all broadcasts\nMessageRegistrationHandle RegisterBroadcastWithoutSourcePostProcessor<T>(\n FastHandlerWithContext<T> handler,\n int priority = 0\n)\n</code></pre>"},{"location":"reference/reference/#emit-helpers","title":"Emit Helpers","text":"<p>The <code>DxMessaging.Core.Extensions.MessageExtensions</code> class provides convenient extension methods for emitting messages.</p>"},{"location":"reference/reference/#untargeted-emission","title":"Untargeted Emission","text":"C#<pre><code>// Emit any message type as untargeted\nmessage.Emit();\nmessage.EmitUntargeted();\n</code></pre>"},{"location":"reference/reference/#targeted-emission","title":"Targeted Emission","text":"C#<pre><code>// Emit to specific target (by InstanceId)\nmessage.EmitTargeted(InstanceId target);\n\n// Emit to GameObject target\nmessage.EmitGameObjectTargeted(GameObject target);\n\n// Emit to Component target\nmessage.EmitComponentTargeted(Component target);\n</code></pre>"},{"location":"reference/reference/#broadcast-emission","title":"Broadcast Emission","text":"C#<pre><code>// Broadcast from specific source (by InstanceId)\nmessage.EmitBroadcast(InstanceId source);\n\n// Broadcast from GameObject source\nmessage.EmitGameObjectBroadcast(GameObject source);\n\n// Broadcast from Component source\nmessage.EmitComponentBroadcast(Component source);\n</code></pre>"},{"location":"reference/reference/#string-message-conveniences","title":"String Message Conveniences","text":"C#<pre><code>// Quick string message emission\n\"PlayerDied\".Emit();\n\"DamageDealt\".Emit(targetInstanceId);\n</code></pre>"},{"location":"reference/reference/#interceptors-bus-level","title":"Interceptors (Bus-Level)","text":"<p>Interceptors allow you to intercept and potentially modify or cancel messages at the bus level before they reach handlers.</p> C#<pre><code>// Intercept untargeted messages\nAction RegisterUntargetedInterceptor<T>(\n UntargetedInterceptor<T> interceptor,\n int priority = 0\n)\n\n// Intercept targeted messages\nAction RegisterTargetedInterceptor<T>(\n TargetedInterceptor<T> interceptor,\n int priority = 0\n)\n\n// Intercept broadcast messages\nAction RegisterBroadcastInterceptor<T>(\n BroadcastInterceptor<T> interceptor,\n int priority = 0\n)\n\n// Global observer for all messages\nAction RegisterGlobalAcceptAll(MessageHandler handler)\n</code></pre>"},{"location":"reference/reference/#diagnostics","title":"Diagnostics","text":"<p>DxMessaging provides diagnostic tools for debugging and monitoring message flow.</p>"},{"location":"reference/reference/#global-settings","title":"Global Settings","text":"C#<pre><code>// Enable/disable diagnostics globally\nIMessageBus.GlobalDiagnosticsMode = true;\n\n// Configure global message buffer size\nIMessageBus.GlobalMessageBufferSize = 1024;\n</code></pre>"},{"location":"reference/reference/#per-instance-settings","title":"Per-Instance Settings","text":"C#<pre><code>// Per-bus diagnostics\nmessageBus.DiagnosticsMode = true;\n\n// Per-token diagnostics\ntoken.DiagnosticMode = true;\n</code></pre>"},{"location":"reference/reference/#registration-logging","title":"Registration Logging","text":"C#<pre><code>// Enable registration logging\nbus.Log.Enabled = true;\n\n// Get log output\nstring logOutput = bus.Log.ToString();\n</code></pre>"},{"location":"reference/reference/#key-types","title":"Key Types","text":"Type Description <code>DxMessaging.Core.InstanceId</code> Value type identity for GameObjects, Components, or custom owners <code>DxMessaging.Core.MessageHandler</code> Per-owner callback runner that manages message dispatch <code>DxMessaging.Core.MessageBus.MessageBus</code> Instanced bus; global instance at <code>MessageHandler.MessageBus</code> <code>DxMessaging.Core.Messages.*</code> Untargeted/Targeted/Broadcast interfaces and built-in string messages <p>\ud83d\udca1 Tip: String Messages</p> <p>For lightweight string-based messaging, see String Messages.</p>"},{"location":"reference/reference/#unity-bridge-types","title":"Unity Bridge Types","text":""},{"location":"reference/reference/#messagingcomponent","title":"MessagingComponent","text":"<p>Base component for objects that emit messages.</p> C#<pre><code>public class MessagingComponent : MonoBehaviour\n{\n // When true, messages can be emitted even when component is disabled\n public bool emitMessagesWhenDisabled;\n\n // Create a registration token for a listener on this GameObject\n public MessageRegistrationToken Create(MonoBehaviour listener);\n\n // Toggle the message handler on/off\n public void ToggleMessageHandler(bool enabled);\n}\n</code></pre>"},{"location":"reference/reference/#messageawarecomponent","title":"MessageAwareComponent","text":"<p>Base component for objects that both emit and receive messages.</p> C#<pre><code>public abstract class MessageAwareComponent : MessagingComponent\n{\n // The registration token for this component\n public MessageRegistrationToken Token { get; }\n\n // When true, registrations are enabled/disabled with OnEnable/OnDisable\n protected virtual bool MessageRegistrationTiedToEnableStatus { get; }\n\n // When true, registers for string messages automatically\n protected virtual bool RegisterForStringMessages { get; }\n\n // Override to register your message handlers\n protected virtual void RegisterMessageHandlers() { }\n}\n</code></pre> <p>\u26a0\ufe0f Warning: Inheritance Tip</p> <p>If you override any lifecycle hooks (<code>Awake</code>, <code>OnDestroy</code>, <code>OnEnable</code>, <code>OnDisable</code>) or <code>RegisterMessageHandlers</code>, always call the base method:</p> C#<pre><code>protected override void RegisterMessageHandlers()\n{\n base.RegisterMessageHandlers();\n // Your registrations here\n}\n\nprotected override void OnEnable()\n{\n base.OnEnable();\n // Your logic here\n}\n</code></pre> <p>Skipping base calls may prevent token setup and default string-message registrations.</p>"},{"location":"reference/reference/#source-files","title":"Source Files","text":"<p>For deeper exploration, browse the source code:</p> Component Source Message Bus Interface IMessageBus.cs Message Bus MessageBus.cs Message Handler MessageHandler.cs Registration Token MessageRegistrationToken.cs Emit Helpers MessageExtensions.cs Attributes Attributes/"},{"location":"reference/troubleshooting/","title":"Troubleshooting \u2014 Common Issues & Solutions","text":"<p>\u2190 Back to Index | FAQ | Getting Started | Glossary</p>"},{"location":"reference/troubleshooting/#not-receiving-messages","title":"Not receiving messages","text":"<ul> <li>Ensure your <code>MessageRegistrationToken</code> is enabled (Enable in <code>OnEnable</code>, Disable in <code>OnDisable</code>).</li> <li>Verify the category matches the emission (Untargeted vs Targeted vs Broadcast).</li> <li>Targeted/Broadcast require a valid <code>InstanceId</code>; ensure the target/source object exists when you emit.</li> <li>In Unity, confirm your <code>MessagingComponent</code> exists on sender/receiver GameObjects.</li> <li>CRITICAL: If inheriting from <code>MessageAwareComponent</code>, ensure your overrides call base methods:</li> <li><code>base.RegisterMessageHandlers()</code> - Call this FIRST in your override to preserve default setup (including string message demos) and parent class registrations.</li> <li><code>base.Awake()</code> - Call this if you override <code>Awake()</code>, or your token won't be created (this is the #1 cause of handlers not firing).</li> <li><code>base.OnEnable()</code> / <code>base.OnDisable()</code> - Call these so the token actually enables/disables.</li> <li>Never use <code>new</code> to hide Unity methods (e.g., <code>new void OnEnable()</code>); always use <code>override</code> and call <code>base.*</code>.</li> </ul> <p>Registration timing</p> <ul> <li>ALWAYS register message handlers in <code>Awake()</code>, not <code>Start()</code>.</li> <li><code>MessageAwareComponent</code> automatically calls <code>RegisterMessageHandlers()</code> in <code>Awake()</code>.</li> <li>Registering in <code>Awake()</code> ensures handlers are ready before other components' <code>Start()</code> methods run.</li> <li>If you register in <code>Start()</code>, you may miss messages emitted by other components in their <code>Start()</code> methods.</li> </ul> <p>Unexpected ordering</p> <ul> <li>Check <code>priority</code> values on registrations; lower runs earlier. Same priority is registration order.</li> <li>Interceptors always precede handlers and can cancel; confirm interceptors return <code>true</code>.</li> </ul> <p>Double registration or over\u2011deregistration warnings</p> <ul> <li>Avoid calling stage/enable multiple times; pair registrations and lifecycles consistently.</li> <li>Review logs with <code>bus.Log.Enabled = true</code> to see the registration history.</li> </ul> <p>Allocations/boxing</p> <ul> <li>Prefer struct messages implementing the generic interfaces: <code>I*Message<T></code>.</li> <li>Use by\u2011ref handler overloads to avoid copies.</li> </ul> <p>Emitting while disabled</p> <ul> <li>If you need to emit when a component is disabled, use a bus not tied to enable state or set <code>emitMessagesWhenDisabled</code> on <code>MessagingComponent</code>.</li> </ul> <p>Diagnostics overhead</p> <ul> <li>Disable diagnostics in release builds (<code>IMessageBus.GlobalDiagnosticsMode = false</code>).</li> </ul>"},{"location":"reference/troubleshooting/#related-documentation","title":"Related Documentation","text":"<ul> <li>Get Unstuck</li> <li>\u2192 FAQ \u2014 Common questions answered</li> <li>\u2192 Getting Started \u2014 Learn the basics</li> <li>\u2192 Glossary \u2014 Understand the terminology</li> <li>Debug & Inspect</li> <li>\u2192 Diagnostics \u2014 Inspector tools and debugging</li> <li>\u2192 Listening Patterns \u2014 Verify you're listening correctly</li> <li>\u2192 Message Types \u2014 Ensure you're using the right type</li> <li>Examples</li> <li>\u2192 Mini Combat sample \u2014 See working code</li> <li>\u2192 Common Patterns \u2014 Real-world solutions</li> </ul>"}]}
|