com.wallstop-studios.dxmessaging 2.0.0-rc27.3.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/format-on-demand.yml +2 -2
- package/.github/workflows/json-format-check.yml +1 -1
- package/.github/workflows/markdown-json.yml +1 -1
- package/.github/workflows/markdownlint.yml +1 -1
- package/.github/workflows/npm-publish.yml +1 -1
- package/.github/workflows/prettier-autofix.yml +2 -2
- package/.github/workflows/yaml-format-lint.yml +1 -1
- package/Docs/Advanced.md +26 -1
- package/Docs/Comparisons.md +1352 -141
- package/Docs/Compatibility.md +27 -0
- package/Docs/DesignAndArchitecture.md +41 -34
- package/Docs/EmitShorthands.md +34 -0
- package/Docs/GettingStarted.md +84 -17
- package/Docs/Helpers.md +1 -1
- package/Docs/Index.md +28 -25
- package/Docs/Install.md +29 -6
- package/Docs/Integrations/Reflex.md +292 -0
- package/Docs/Integrations/Reflex.md.meta +7 -0
- package/Docs/Integrations/VContainer.md +324 -0
- package/Docs/Integrations/VContainer.md.meta +7 -0
- package/Docs/Integrations/Zenject.md +333 -0
- package/Docs/Integrations/Zenject.md.meta +7 -0
- package/{Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.pdb.meta → Docs/Integrations.meta} +2 -1
- package/Docs/InterceptorsAndOrdering.md +371 -17
- package/Docs/ListeningPatterns.md +206 -0
- package/Docs/MessageBusProviders.md +496 -0
- package/Docs/MessageBusProviders.md.meta +7 -0
- package/Docs/MessageTypes.md +27 -0
- package/Docs/MigrationGuide.md +45 -0
- package/Docs/Overview.md +114 -20
- package/Docs/Patterns.md +327 -2
- package/Docs/Performance.md +9 -9
- package/Docs/QuickReference.md +31 -0
- package/Docs/RuntimeConfiguration.md +407 -0
- package/Docs/RuntimeConfiguration.md.meta +7 -0
- package/Docs/UnityIntegration.md +3 -1
- package/Docs/VisualGuide.md +281 -167
- package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +3 -3
- package/Editor/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
- package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +2 -2
- package/Editor/Analyzers/System.Collections.Immutable.dll +0 -0
- package/Editor/Analyzers/System.Reflection.Metadata.dll +0 -0
- package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
- package/Editor/CustomEditors/MessagingComponentEditor.cs +15 -6
- package/README.md +388 -67
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/Core/Extensions/MessageBusExtensions.cs +253 -0
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -0
- package/Runtime/Core/Extensions/MessageExtensions.cs +137 -89
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs +23 -0
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs +14 -0
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +18 -0
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs +26 -0
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +383 -0
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -0
- package/Runtime/Core/MessageHandler.cs +198 -27
- package/Runtime/Core/MessageRegistrationToken.cs +67 -25
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +31 -0
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +38 -0
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -0
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Reflex.meta +8 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +46 -0
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -0
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/VContainer.meta +8 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs +11 -0
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +3 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -0
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +55 -0
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -0
- package/Runtime/Unity/Integrations/Zenject.meta +8 -0
- package/Runtime/Unity/Integrations.meta +8 -0
- package/Runtime/Unity/MessageAwareComponent.cs +102 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs +97 -0
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -0
- package/Runtime/Unity/MessagingComponent.cs +164 -2
- package/Runtime/Unity/MessagingComponentInstaller.cs +120 -0
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs +14 -0
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -0
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -0
- package/Samples~/DI/Prefabs.meta +8 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -0
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -0
- package/Samples~/DI/Providers.meta +8 -0
- package/Samples~/DI/README.md +51 -0
- package/Samples~/DI/README.md.meta +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs +75 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Reflex.meta +8 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +81 -0
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -0
- package/Samples~/DI/VContainer.meta +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs +67 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -0
- package/Samples~/DI/Zenject.meta +8 -0
- package/Samples~/DI.meta +8 -0
- package/Samples~/Mini Combat/README.md +86 -28
- package/Samples~/Mini Combat/Walkthrough.md +41 -25
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs +12 -2
- package/Samples~/UI Buttons + Inspector/README.md +55 -12
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +444 -0
- package/Tests/Runtime/Benchmarks/BenchmarkSession.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs +94 -0
- package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs +395 -0
- package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Benchmarks/PerformanceTests.cs +77 -429
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs +142 -0
- package/Tests/Runtime/Benchmarks/ProviderResolutionBenchmarks.cs.meta +12 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +50 -0
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef.meta +7 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs +333 -0
- package/Tests/Runtime/Core/DefaultBusFallbackTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs +278 -0
- package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs +289 -0
- package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/Extensions.meta +8 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs +57 -0
- package/Tests/Runtime/Core/IntegrationShimSmokeTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs +219 -0
- package/Tests/Runtime/Core/MessageHandlerGlobalBusTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs +204 -0
- package/Tests/Runtime/Core/MessageRegistrationBuilderTests.cs.meta +11 -0
- package/Tests/Runtime/Core/MessagingTestBase.cs +4 -4
- package/Tests/Runtime/Core/NominalTests.cs +2 -2
- package/Tests/Runtime/Core/SourceGeneratorNestedTests.cs +1 -1
- package/Tests/Runtime/Core/TypedShorthandTests.cs +2 -2
- package/Tests/Runtime/Core/UntargetedEquivalenceTests.cs +1 -1
- package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +2 -4
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs +162 -0
- package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset +16 -0
- package/Tests/Runtime/Integrations/Reflex/Resources/ReflexSettings.asset.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/Resources.meta +8 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +27 -0
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Reflex.meta +8 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +37 -0
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/VContainer.meta +8 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +37 -0
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef.meta +7 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +140 -0
- package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs.meta +11 -0
- package/Tests/Runtime/Integrations/Zenject.meta +8 -0
- package/Tests/Runtime/Integrations.meta +8 -0
- package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +1 -1
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs +64 -0
- package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs.meta +12 -0
- package/Tests/Runtime/TestUtilities.meta +9 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs +57 -0
- package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs.meta +11 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs +107 -0
- package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs.meta +12 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs +210 -0
- package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs.meta +12 -0
- package/Tests/Runtime/Unity.meta +9 -0
- package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# DxMessaging + Reflex
|
|
2
|
+
|
|
3
|
+
[← Back to Integrations Overview](../README.md#-integrations)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**Reflex** is a minimal, lightweight dependency injection framework for Unity with blazing-fast performance. DxMessaging integrates seamlessly with Reflex, allowing you to:
|
|
10
|
+
|
|
11
|
+
- **Inject `IMessageBus`** in any class with minimal overhead
|
|
12
|
+
- **Use DI for construction** + DxMessaging for events (best of both worlds)
|
|
13
|
+
- **Minimal API surface** — easy to learn, easy to use
|
|
14
|
+
- **High performance** — Reflex + DxMessaging = minimal allocations
|
|
15
|
+
|
|
16
|
+
**Why combine DI + Messaging?** Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected). Reflex's minimal API makes integration simple and straightforward.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Prerequisites
|
|
23
|
+
|
|
24
|
+
- DxMessaging installed via UPM
|
|
25
|
+
- Reflex installed (`gustavopsantos/Reflex`) via source or UPM
|
|
26
|
+
|
|
27
|
+
### 1. Create a Reflex Installer
|
|
28
|
+
|
|
29
|
+
```csharp
|
|
30
|
+
using DxMessaging.Core.MessageBus;
|
|
31
|
+
using Reflex.Core;
|
|
32
|
+
|
|
33
|
+
public sealed class DxMessagingInstaller : Installer
|
|
34
|
+
{
|
|
35
|
+
public override void InstallBindings(ContainerDescriptor descriptor)
|
|
36
|
+
{
|
|
37
|
+
// Bind MessageBus as both concrete and interface
|
|
38
|
+
descriptor.AddSingleton(typeof(MessageBus), typeof(MessageBus));
|
|
39
|
+
descriptor.AddSingleton(typeof(IMessageBus), c => c.Resolve<MessageBus>());
|
|
40
|
+
|
|
41
|
+
// Optional: Enable automatic IMessageRegistrationBuilder binding
|
|
42
|
+
// Requires REFLEX_PRESENT define (auto-added by DxMessaging when Reflex detected)
|
|
43
|
+
#if REFLEX_PRESENT
|
|
44
|
+
descriptor.AddMessageRegistrationBuilder();
|
|
45
|
+
#endif
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Add to your scene
|
|
51
|
+
|
|
52
|
+
1. Create a `SceneContext` or `ProjectContext` in your scene
|
|
53
|
+
1. Add `DxMessagingInstaller` to the installers list
|
|
54
|
+
1. Reflex will now inject `IMessageBus` automatically
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Usage Patterns
|
|
59
|
+
|
|
60
|
+
### Pattern 1: Inject into Plain Classes (Recommended for Services)
|
|
61
|
+
|
|
62
|
+
Use `IMessageRegistrationBuilder` to create message handlers in non-MonoBehaviour classes:
|
|
63
|
+
|
|
64
|
+
```csharp
|
|
65
|
+
using DxMessaging.Core.MessageBus;
|
|
66
|
+
using DxMessaging.Core.Attributes;
|
|
67
|
+
|
|
68
|
+
// Define a message
|
|
69
|
+
[DxUntargetedMessage]
|
|
70
|
+
[DxAutoConstructor]
|
|
71
|
+
public readonly partial struct PlayerDamaged
|
|
72
|
+
{
|
|
73
|
+
public readonly int damage;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Service that listens to messages
|
|
77
|
+
public sealed class DamageService
|
|
78
|
+
{
|
|
79
|
+
private readonly MessageRegistrationLease _lease;
|
|
80
|
+
|
|
81
|
+
// Builder is injected automatically when using the installer
|
|
82
|
+
public DamageService(IMessageRegistrationBuilder registrationBuilder)
|
|
83
|
+
{
|
|
84
|
+
var options = new MessageRegistrationBuildOptions
|
|
85
|
+
{
|
|
86
|
+
Configure = token =>
|
|
87
|
+
{
|
|
88
|
+
_ = token.RegisterUntargeted<PlayerDamaged>(OnPlayerDamaged);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
_lease = registrationBuilder.Build(options);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public void Initialize()
|
|
96
|
+
{
|
|
97
|
+
_lease.Activate(); // Start listening
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public void Dispose()
|
|
101
|
+
{
|
|
102
|
+
_lease.Dispose(); // Clean up
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static void OnPlayerDamaged(ref PlayerDamaged message)
|
|
106
|
+
{
|
|
107
|
+
UnityEngine.Debug.Log($"Player took {message.damage} damage!");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Register the service in your installer
|
|
113
|
+
|
|
114
|
+
```csharp
|
|
115
|
+
public override void InstallBindings(ContainerDescriptor descriptor)
|
|
116
|
+
{
|
|
117
|
+
descriptor.AddSingleton<DamageService>();
|
|
118
|
+
// Call Initialize() from a bootstrap MonoBehaviour
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Note:** Reflex doesn't have lifecycle interfaces like `IInitializable`. Call `Initialize()` and `Dispose()` manually from a controlling MonoBehaviour or bootstrap script.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)
|
|
127
|
+
|
|
128
|
+
```csharp
|
|
129
|
+
using DxMessaging.Core.MessageBus;
|
|
130
|
+
using DxMessaging.Unity;
|
|
131
|
+
using Reflex.Attributes;
|
|
132
|
+
using UnityEngine;
|
|
133
|
+
|
|
134
|
+
[DisallowMultipleComponent]
|
|
135
|
+
[RequireComponent(typeof(MessagingComponent))]
|
|
136
|
+
public sealed class MessagingComponentConfigurator : MonoBehaviour
|
|
137
|
+
{
|
|
138
|
+
[Inject]
|
|
139
|
+
private IMessageBus _messageBus;
|
|
140
|
+
|
|
141
|
+
private void Awake()
|
|
142
|
+
{
|
|
143
|
+
GetComponent<MessagingComponent>().Configure(
|
|
144
|
+
_messageBus,
|
|
145
|
+
MessageBusRebindMode.RebindActive
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Usage
|
|
152
|
+
|
|
153
|
+
1. Add `MessagingComponentConfigurator` alongside any `MessagingComponent` in your prefabs
|
|
154
|
+
1. Reflex will inject the bus in `Awake()` before handlers are registered
|
|
155
|
+
1. Your message handlers now use the container-managed bus
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### Pattern 3: Inject IMessageBus Directly
|
|
160
|
+
|
|
161
|
+
For simple emission without listening, inject `IMessageBus` directly:
|
|
162
|
+
|
|
163
|
+
```csharp
|
|
164
|
+
public sealed class GameBootstrap : MonoBehaviour
|
|
165
|
+
{
|
|
166
|
+
[Inject]
|
|
167
|
+
private IMessageBus _messageBus;
|
|
168
|
+
|
|
169
|
+
private void Start()
|
|
170
|
+
{
|
|
171
|
+
var message = new GameStarted();
|
|
172
|
+
_messageBus.EmitUntargeted(ref message);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Advanced: Object Pooling
|
|
180
|
+
|
|
181
|
+
When using object pooling with Reflex:
|
|
182
|
+
|
|
183
|
+
```csharp
|
|
184
|
+
public sealed class EnemyPool
|
|
185
|
+
{
|
|
186
|
+
private readonly Container _container;
|
|
187
|
+
private readonly Queue<Enemy> _pool = new();
|
|
188
|
+
|
|
189
|
+
public EnemyPool(Container container)
|
|
190
|
+
{
|
|
191
|
+
_container = container;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
public Enemy Spawn()
|
|
195
|
+
{
|
|
196
|
+
Enemy enemy;
|
|
197
|
+
if (_pool.Count > 0)
|
|
198
|
+
{
|
|
199
|
+
enemy = _pool.Dequeue();
|
|
200
|
+
}
|
|
201
|
+
else
|
|
202
|
+
{
|
|
203
|
+
enemy = Object.Instantiate(enemyPrefab);
|
|
204
|
+
_container.Inject(enemy); // Inject dependencies
|
|
205
|
+
}
|
|
206
|
+
return enemy;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public void Return(Enemy enemy)
|
|
210
|
+
{
|
|
211
|
+
_pool.Enqueue(enemy);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Testing with Reflex
|
|
219
|
+
|
|
220
|
+
### Unit Tests
|
|
221
|
+
|
|
222
|
+
```csharp
|
|
223
|
+
using DxMessaging.Core.MessageBus;
|
|
224
|
+
using Reflex.Core;
|
|
225
|
+
using NUnit.Framework;
|
|
226
|
+
|
|
227
|
+
[TestFixture]
|
|
228
|
+
public class DamageServiceTests
|
|
229
|
+
{
|
|
230
|
+
[Test]
|
|
231
|
+
public void Initialize_ListensToMessages()
|
|
232
|
+
{
|
|
233
|
+
// Arrange
|
|
234
|
+
var builder = new ContainerBuilder();
|
|
235
|
+
var bus = new MessageBus();
|
|
236
|
+
builder.AddSingleton<IMessageBus>(bus);
|
|
237
|
+
builder.AddSingleton<DamageService>();
|
|
238
|
+
var container = builder.Build();
|
|
239
|
+
|
|
240
|
+
bool messageReceived = false;
|
|
241
|
+
var handler = new MessageHandler(new InstanceId(1), bus) { active = true };
|
|
242
|
+
var token = MessageRegistrationToken.Create(handler, bus);
|
|
243
|
+
_ = token.RegisterUntargeted<PlayerDamaged>(ref msg => messageReceived = true);
|
|
244
|
+
token.Enable();
|
|
245
|
+
|
|
246
|
+
// Act
|
|
247
|
+
var service = container.Resolve<DamageService>();
|
|
248
|
+
service.Initialize();
|
|
249
|
+
var message = new PlayerDamaged(25);
|
|
250
|
+
bus.EmitUntargeted(ref message);
|
|
251
|
+
|
|
252
|
+
// Assert
|
|
253
|
+
Assert.IsTrue(messageReceived);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Checklist
|
|
261
|
+
|
|
262
|
+
### Initial Setup
|
|
263
|
+
|
|
264
|
+
- [ ] Install DxMessaging and Reflex
|
|
265
|
+
- [ ] Create `DxMessagingInstaller` with bus bindings
|
|
266
|
+
- [ ] Add installer to your `SceneContext` or `ProjectContext`
|
|
267
|
+
- [ ] Add `#if REFLEX_PRESENT` check and call `descriptor.AddMessageRegistrationBuilder()`
|
|
268
|
+
|
|
269
|
+
### Integration
|
|
270
|
+
|
|
271
|
+
- [ ] Use `IMessageRegistrationBuilder` in plain classes
|
|
272
|
+
- [ ] Call `Initialize()` and `Dispose()` manually from a bootstrap script
|
|
273
|
+
- [ ] Add `MessagingComponentConfigurator` to prefabs with `MessagingComponent`
|
|
274
|
+
- [ ] Replace `MessageHandler.MessageBus` references with injected `IMessageBus`
|
|
275
|
+
|
|
276
|
+
### Pooling
|
|
277
|
+
|
|
278
|
+
- [ ] Use `container.Inject(instance)` for pooled objects
|
|
279
|
+
- [ ] Ensure injection happens before message handlers are registered
|
|
280
|
+
|
|
281
|
+
### Testing
|
|
282
|
+
|
|
283
|
+
- [ ] Create isolated `ContainerBuilder` instances in tests
|
|
284
|
+
- [ ] Use `builder.AddSingleton<IMessageBus>(new MessageBus())` for test buses
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Next Steps
|
|
289
|
+
|
|
290
|
+
- **[Zenject Integration](Zenject.md)** — Full-featured DI with extensive Unity support
|
|
291
|
+
- **[VContainer Integration](VContainer.md)** — Lightweight alternative with scoped lifetimes
|
|
292
|
+
- **[Back to Documentation Hub](../Index.md)** — Browse all docs
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# DxMessaging + VContainer
|
|
2
|
+
|
|
3
|
+
[← Back to Integrations Overview](../README.md#-integrations)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
**VContainer** is a fast, lightweight dependency injection framework for Unity with minimal overhead. DxMessaging integrates seamlessly with VContainer, allowing you to:
|
|
10
|
+
|
|
11
|
+
- **Inject `IMessageBus`** in any class with deterministic lifetimes
|
|
12
|
+
- **Create per-scope buses** for scene isolation (perfect for additive scenes)
|
|
13
|
+
- **Use DI for construction** + DxMessaging for events (best of both worlds)
|
|
14
|
+
- **Minimal performance overhead** — VContainer + DxMessaging = blazing fast
|
|
15
|
+
|
|
16
|
+
**Why combine DI + Messaging?** Use constructor injection for service dependencies (repositories, managers) and messaging for reactive events (damage taken, item collected). VContainer's scoped lifetimes make it perfect for per-scene message buses.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Prerequisites
|
|
23
|
+
|
|
24
|
+
- DxMessaging installed via UPM
|
|
25
|
+
- VContainer installed (Git URL or OpenUPM)
|
|
26
|
+
|
|
27
|
+
### 1. Create a LifetimeScope with DxMessaging
|
|
28
|
+
|
|
29
|
+
```csharp
|
|
30
|
+
using DxMessaging.Core.MessageBus;
|
|
31
|
+
using UnityEngine;
|
|
32
|
+
using VContainer;
|
|
33
|
+
using VContainer.Unity;
|
|
34
|
+
|
|
35
|
+
public sealed class MessagingLifetimeScope : LifetimeScope
|
|
36
|
+
{
|
|
37
|
+
protected override void Configure(IContainerBuilder builder)
|
|
38
|
+
{
|
|
39
|
+
// Register MessageBus as both concrete and interface
|
|
40
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
41
|
+
|
|
42
|
+
// Optional: Enable automatic IMessageRegistrationBuilder binding
|
|
43
|
+
// Requires VCONTAINER_PRESENT define (auto-added by DxMessaging when VContainer detected)
|
|
44
|
+
#if VCONTAINER_PRESENT
|
|
45
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
46
|
+
#endif
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Add to your scene
|
|
52
|
+
|
|
53
|
+
1. Create an empty GameObject in your scene
|
|
54
|
+
1. Add the `MessagingLifetimeScope` component
|
|
55
|
+
1. This creates a singleton bus for the entire scene
|
|
56
|
+
|
|
57
|
+
**Tip:** Use `Lifetime.Singleton` for project-wide buses, or `Lifetime.Scoped` for isolated scene/feature buses.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Usage Patterns
|
|
62
|
+
|
|
63
|
+
### Pattern 1: Inject into Plain Classes (Recommended for Services)
|
|
64
|
+
|
|
65
|
+
Use `IMessageRegistrationBuilder` to create message handlers in non-MonoBehaviour classes:
|
|
66
|
+
|
|
67
|
+
```csharp
|
|
68
|
+
using DxMessaging.Core.MessageBus;
|
|
69
|
+
using DxMessaging.Core.Attributes;
|
|
70
|
+
using VContainer.Unity;
|
|
71
|
+
|
|
72
|
+
// Define a message
|
|
73
|
+
[DxUntargetedMessage]
|
|
74
|
+
[DxAutoConstructor]
|
|
75
|
+
public readonly partial struct PlayerSpawned
|
|
76
|
+
{
|
|
77
|
+
public readonly int playerId;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Service that listens to messages
|
|
81
|
+
public sealed class PlayerService : IStartable, IDisposable
|
|
82
|
+
{
|
|
83
|
+
private readonly MessageRegistrationLease _lease;
|
|
84
|
+
|
|
85
|
+
// Builder is injected automatically when using the scope
|
|
86
|
+
public PlayerService(IMessageRegistrationBuilder registrationBuilder)
|
|
87
|
+
{
|
|
88
|
+
var options = new MessageRegistrationBuildOptions
|
|
89
|
+
{
|
|
90
|
+
Configure = token =>
|
|
91
|
+
{
|
|
92
|
+
_ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
_lease = registrationBuilder.Build(options);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public void Start()
|
|
100
|
+
{
|
|
101
|
+
_lease.Activate(); // Start listening when container starts
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public void Dispose()
|
|
105
|
+
{
|
|
106
|
+
_lease.Dispose(); // Clean up when container disposes
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private static void OnPlayerSpawned(ref PlayerSpawned message)
|
|
110
|
+
{
|
|
111
|
+
UnityEngine.Debug.Log($"Player {message.playerId} spawned!");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Register the service in your scope
|
|
117
|
+
|
|
118
|
+
```csharp
|
|
119
|
+
protected override void Configure(IContainerBuilder builder)
|
|
120
|
+
{
|
|
121
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
122
|
+
#if VCONTAINER_PRESENT
|
|
123
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
124
|
+
#endif
|
|
125
|
+
|
|
126
|
+
// Register your service
|
|
127
|
+
builder.RegisterEntryPoint<PlayerService>();
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Pattern 2: Configure MessagingComponents (For Existing MonoBehaviours)
|
|
134
|
+
|
|
135
|
+
```csharp
|
|
136
|
+
using DxMessaging.Core.MessageBus;
|
|
137
|
+
using DxMessaging.Unity;
|
|
138
|
+
using UnityEngine;
|
|
139
|
+
using VContainer;
|
|
140
|
+
using VContainer.Unity;
|
|
141
|
+
|
|
142
|
+
[RequireComponent(typeof(MessagingComponent))]
|
|
143
|
+
public sealed class MessagingComponentConfigurator : MonoBehaviour, IStartable
|
|
144
|
+
{
|
|
145
|
+
[Inject]
|
|
146
|
+
private readonly IMessageBus _messageBus;
|
|
147
|
+
|
|
148
|
+
private MessagingComponent _messagingComponent;
|
|
149
|
+
|
|
150
|
+
private void Awake()
|
|
151
|
+
{
|
|
152
|
+
_messagingComponent = GetComponent<MessagingComponent>();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public void Start()
|
|
156
|
+
{
|
|
157
|
+
_messagingComponent.Configure(_messageBus, MessageBusRebindMode.RebindActive);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Usage
|
|
163
|
+
|
|
164
|
+
1. Add `MessagingComponentConfigurator` alongside any `MessagingComponent` in your prefabs
|
|
165
|
+
1. VContainer will inject the bus via `IStartable.Start()` before handlers are registered
|
|
166
|
+
1. Your message handlers now use the container-managed bus
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Pattern 3: Inject IMessageBus Directly
|
|
171
|
+
|
|
172
|
+
For simple emission without listening, inject `IMessageBus` directly:
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
public sealed class GameInitializer : IStartable
|
|
176
|
+
{
|
|
177
|
+
private readonly IMessageBus _messageBus;
|
|
178
|
+
|
|
179
|
+
public GameInitializer(IMessageBus messageBus)
|
|
180
|
+
{
|
|
181
|
+
_messageBus = messageBus;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public void Start()
|
|
185
|
+
{
|
|
186
|
+
var message = new GameStarted();
|
|
187
|
+
_messageBus.EmitUntargeted(ref message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Advanced: Scene Scopes and Isolation
|
|
195
|
+
|
|
196
|
+
VContainer's scoped lifetimes make it perfect for per-scene message buses. This is useful for additive scenes or isolated gameplay features:
|
|
197
|
+
|
|
198
|
+
```csharp
|
|
199
|
+
using DxMessaging.Core.MessageBus;
|
|
200
|
+
using VContainer;
|
|
201
|
+
using VContainer.Unity;
|
|
202
|
+
|
|
203
|
+
public sealed class LevelLoader
|
|
204
|
+
{
|
|
205
|
+
private readonly LifetimeScope _parentScope;
|
|
206
|
+
|
|
207
|
+
public LevelLoader(LifetimeScope parentScope)
|
|
208
|
+
{
|
|
209
|
+
_parentScope = parentScope;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public LifetimeScope LoadLevel(GameObject lifetimeScopePrefab)
|
|
213
|
+
{
|
|
214
|
+
// Create a child scope with its own MessageBus
|
|
215
|
+
return _parentScope.CreateChildFromPrefab(lifetimeScopePrefab, builder =>
|
|
216
|
+
{
|
|
217
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
218
|
+
#if VCONTAINER_PRESENT
|
|
219
|
+
builder.RegisterMessageRegistrationBuilder();
|
|
220
|
+
#endif
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Benefits
|
|
227
|
+
|
|
228
|
+
- Each scene gets its own isolated message bus
|
|
229
|
+
- Messages don't leak between scenes
|
|
230
|
+
- Perfect for multiplayer lobbies, mini-games, or feature-scoped events
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Testing with VContainer
|
|
235
|
+
|
|
236
|
+
### Unit Tests
|
|
237
|
+
|
|
238
|
+
```csharp
|
|
239
|
+
using DxMessaging.Core.MessageBus;
|
|
240
|
+
using VContainer;
|
|
241
|
+
using VContainer.Unity;
|
|
242
|
+
using NUnit.Framework;
|
|
243
|
+
|
|
244
|
+
[TestFixture]
|
|
245
|
+
public class GameInitializerTests
|
|
246
|
+
{
|
|
247
|
+
[Test]
|
|
248
|
+
public void Initialize_EmitsGameStarted()
|
|
249
|
+
{
|
|
250
|
+
// Arrange
|
|
251
|
+
var builder = new ContainerBuilder();
|
|
252
|
+
var bus = new MessageBus();
|
|
253
|
+
builder.RegisterInstance<IMessageBus>(bus);
|
|
254
|
+
builder.RegisterEntryPoint<GameInitializer>();
|
|
255
|
+
var container = builder.Build();
|
|
256
|
+
|
|
257
|
+
bool messageReceived = false;
|
|
258
|
+
var handler = new MessageHandler(new InstanceId(1), bus) { active = true };
|
|
259
|
+
var token = MessageRegistrationToken.Create(handler, bus);
|
|
260
|
+
_ = token.RegisterUntargeted<GameStarted>(ref msg => messageReceived = true);
|
|
261
|
+
token.Enable();
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
container.Resolve<GameInitializer>().Start();
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
Assert.IsTrue(messageReceived);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Play-Mode Tests
|
|
273
|
+
|
|
274
|
+
For play-mode tests, create a temporary `LifetimeScope`:
|
|
275
|
+
|
|
276
|
+
```csharp
|
|
277
|
+
[UnityTest]
|
|
278
|
+
public IEnumerator PlayMode_MessageBusIsolation()
|
|
279
|
+
{
|
|
280
|
+
// Create isolated scope for this test
|
|
281
|
+
var scope = LifetimeScope.Create(builder =>
|
|
282
|
+
{
|
|
283
|
+
builder.Register<MessageBus>(Lifetime.Singleton).As<IMessageBus>();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
var bus = scope.Container.Resolve<IMessageBus>();
|
|
287
|
+
// ... test logic ...
|
|
288
|
+
|
|
289
|
+
scope.Dispose(); // Clean up
|
|
290
|
+
yield return null;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Checklist
|
|
297
|
+
|
|
298
|
+
### Initial Setup
|
|
299
|
+
|
|
300
|
+
- [ ] Install DxMessaging and VContainer
|
|
301
|
+
- [ ] Create `MessagingLifetimeScope` with `builder.Register<MessageBus>().As<IMessageBus>()`
|
|
302
|
+
- [ ] Add scope to your scene as a GameObject component
|
|
303
|
+
- [ ] Add `#if VCONTAINER_PRESENT` check and call `builder.RegisterMessageRegistrationBuilder()`
|
|
304
|
+
|
|
305
|
+
### Integration
|
|
306
|
+
|
|
307
|
+
- [ ] Use `IMessageRegistrationBuilder` in plain classes with `IStartable`/`IDisposable`
|
|
308
|
+
- [ ] Add `MessagingComponentConfigurator` to prefabs with `MessagingComponent`
|
|
309
|
+
- [ ] Replace `MessageHandler.MessageBus` references with injected `IMessageBus`
|
|
310
|
+
- [ ] Consider using scoped buses for scene isolation
|
|
311
|
+
|
|
312
|
+
### Testing
|
|
313
|
+
|
|
314
|
+
- [ ] Create isolated `ContainerBuilder` instances in tests
|
|
315
|
+
- [ ] Use `builder.RegisterInstance<IMessageBus>(new MessageBus())` for test buses
|
|
316
|
+
- [ ] Dispose scopes after tests to ensure clean teardown
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Next Steps
|
|
321
|
+
|
|
322
|
+
- **[Zenject Integration](Zenject.md)** — Full-featured DI with extensive Unity support
|
|
323
|
+
- **[Reflex Integration](Reflex.md)** — Minimal DI framework
|
|
324
|
+
- **[Back to Documentation Hub](../Index.md)** — Browse all docs
|