com.wallstop-studios.dxmessaging 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.github/workflows/dotnet-tests.yml +3 -3
  2. package/.github/workflows/prettier-autofix.yml +0 -7
  3. package/.pre-commit-config.yaml +8 -5
  4. package/AGENTS.md +12 -12
  5. package/CONTRIBUTING.md +8 -2
  6. package/Docs/Comparisons.md +5 -5
  7. package/Docs/InterceptorsAndOrdering.md +1 -1
  8. package/Docs/Performance.md +13 -13
  9. package/Docs/QuickReference.md +1 -1
  10. package/Docs/Reference.md +5 -5
  11. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  12. package/Editor/CustomEditors/MessagingComponentEditor.cs +3 -0
  13. package/Editor/DxMessagingEditorInitializer.cs +58 -1
  14. package/Editor/DxMessagingMenu.cs +38 -0
  15. package/Editor/DxMessagingMenu.cs.meta +11 -0
  16. package/Editor/DxMessagingSceneBuildProcessor.cs +81 -0
  17. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -0
  18. package/Editor/Settings/DxMessagingSettings.cs +37 -6
  19. package/Editor/Settings/DxMessagingSettingsProvider.cs +45 -7
  20. package/README.md +1 -1
  21. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +52 -0
  22. package/Runtime/Core/DataStructure/CyclicBuffer.cs +16 -0
  23. package/Runtime/Core/Diagnostics/MessageEmissionData.cs +1 -1
  24. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs +62 -0
  25. package/Runtime/Core/DxMessagingStaticState.cs +108 -0
  26. package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -0
  27. package/Runtime/Core/Extensions/IListExtensions.cs +24 -0
  28. package/Runtime/Core/Extensions/MessageBusExtensions.cs +142 -0
  29. package/Runtime/Core/Helper/MessageCache.cs +16 -0
  30. package/Runtime/Core/Helper/MessageHelperIndexer.cs +77 -0
  31. package/Runtime/Core/InstanceId.cs +86 -0
  32. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs +31 -0
  33. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -0
  34. package/Runtime/Core/MessageBus/IMessageBus.cs +44 -16
  35. package/Runtime/Core/MessageBus/MessageBus.cs +167 -180
  36. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +44 -0
  37. package/Runtime/Core/MessageBus/MessagingRegistration.cs +60 -2
  38. package/Runtime/Core/MessageBus/RegistrationLog.cs +10 -0
  39. package/Runtime/Core/MessageHandler.cs +107 -6
  40. package/Runtime/Core/MessageRegistrationHandle.cs +59 -0
  41. package/Runtime/Core/MessageRegistrationToken.cs +18 -2
  42. package/Runtime/Core/Messages/ReflexiveMessage.cs +38 -0
  43. package/Runtime/Core/MessagingDebug.cs +16 -1
  44. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +4 -0
  45. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs +19 -0
  46. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -0
  47. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +4 -0
  48. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +17 -0
  49. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +8 -0
  50. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +12 -0
  51. package/Runtime/Unity/MessagingComponent.cs +93 -0
  52. package/Samples~/DI/README.md +13 -13
  53. package/Samples~/Mini Combat/README.md +15 -15
  54. package/Samples~/Mini Combat/Walkthrough.md +12 -12
  55. package/Samples~/UI Buttons + Inspector/README.md +4 -4
  56. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +4 -0
  57. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +4 -0
  58. package/Tests/Runtime/Core/DiagnosticsTests.cs +3 -3
  59. package/Tests/Runtime/Core/DxMessagingStaticStateTests.cs +69 -0
  60. package/Tests/Runtime/Core/DxMessagingStaticStateTests.cs.meta +11 -0
  61. package/Tests/Runtime/Core/Extensions/MessageBusExtensionsTests.cs +12 -31
  62. package/Tests/Runtime/Core/OrderingManyRegistrationsTests.cs +683 -0
  63. package/Tests/Runtime/Core/OrderingManyRegistrationsTests.cs.meta +11 -0
  64. package/package.json +1 -1
  65. package/scripts/fix-eol.js +38 -3
  66. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.csproj +0 -20
  67. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.csproj.meta +0 -7
@@ -17,10 +17,29 @@ namespace DxMessaging.Core.Messages
17
17
  public enum ReflexiveSendMode
18
18
  {
19
19
  [Obsolete("Please use a valid Send Mode")]
20
+ /// <summary>
21
+ /// Legacy sentinel indicating no traversal; not supported.
22
+ /// </summary>
20
23
  None = 0,
24
+
25
+ /// <summary>
26
+ /// Invoke matching methods only on the immediate GameObject.
27
+ /// </summary>
21
28
  Flat = 1 << 0,
29
+
30
+ /// <summary>
31
+ /// Traverse into child GameObjects when invoking methods.
32
+ /// </summary>
22
33
  Downwards = 1 << 1,
34
+
35
+ /// <summary>
36
+ /// Traverse up the parent chain when invoking methods.
37
+ /// </summary>
23
38
  Upwards = 1 << 2,
39
+
40
+ /// <summary>
41
+ /// Skip disabled components during traversal.
42
+ /// </summary>
24
43
  OnlyIncludeActive = 1 << 3,
25
44
  }
26
45
 
@@ -34,6 +53,11 @@ namespace DxMessaging.Core.Messages
34
53
 
35
54
  private readonly int _hashCode;
36
55
 
56
+ /// <summary>
57
+ /// Creates a lookup key for a reflected method signature.
58
+ /// </summary>
59
+ /// <param name="methodName">Name of the method.</param>
60
+ /// <param name="parameterTypes">Ordered parameter list expected by the method.</param>
37
61
  public MethodSignatureKey(string methodName, Type[] parameterTypes)
38
62
  : this()
39
63
  {
@@ -54,16 +78,30 @@ namespace DxMessaging.Core.Messages
54
78
  return hashCode;
55
79
  }
56
80
 
81
+ /// <summary>
82
+ /// Gets a stable hash code for the method signature.
83
+ /// </summary>
84
+ /// <returns>Hash code derived from the name and parameter types.</returns>
57
85
  public override int GetHashCode()
58
86
  {
59
87
  return _hashCode;
60
88
  }
61
89
 
90
+ /// <summary>
91
+ /// Checks equality against an arbitrary object.
92
+ /// </summary>
93
+ /// <param name="obj">Object to compare.</param>
94
+ /// <returns><c>true</c> when <paramref name="obj"/> represents the same signature.</returns>
62
95
  public override bool Equals(object obj)
63
96
  {
64
97
  return obj is MethodSignatureKey other && Equals(other);
65
98
  }
66
99
 
100
+ /// <summary>
101
+ /// Checks equality against another method signature key.
102
+ /// </summary>
103
+ /// <param name="other">Signature key to compare with.</param>
104
+ /// <returns><c>true</c> when both keys describe the same method.</returns>
67
105
  public bool Equals(MethodSignatureKey other)
68
106
  {
69
107
  if (
@@ -3,13 +3,28 @@ namespace DxMessaging.Core
3
3
  using System;
4
4
 
5
5
  /// <summary>
6
- /// Severity of the log message
6
+ /// Severity of the log message.
7
7
  /// </summary>
8
8
  public enum LogLevel
9
9
  {
10
+ /// <summary>
11
+ /// Verbose diagnostic information useful while developing or debugging.
12
+ /// </summary>
10
13
  Debug = 0,
14
+
15
+ /// <summary>
16
+ /// Informational messages that describe normal operation.
17
+ /// </summary>
11
18
  Info = 1,
19
+
20
+ /// <summary>
21
+ /// Non-fatal issues that should be investigated.
22
+ /// </summary>
12
23
  Warn = 2,
24
+
25
+ /// <summary>
26
+ /// Errors indicating messaging failed or data may be lost.
27
+ /// </summary>
13
28
  Error = 3,
14
29
  }
15
30
 
@@ -24,6 +24,10 @@ namespace DxMessaging.Unity
24
24
  )]
25
25
  public sealed class CurrentGlobalMessageBusProvider : ScriptableMessageBusProvider
26
26
  {
27
+ /// <summary>
28
+ /// Resolves the message bus currently set as the global bus via <see cref="MessageHandler.SetGlobalMessageBus(IMessageBus)"/>.
29
+ /// </summary>
30
+ /// <returns>The active global <see cref="IMessageBus"/> instance.</returns>
27
31
  public override IMessageBus Resolve()
28
32
  {
29
33
  return MessageHandler.MessageBus;
@@ -0,0 +1,19 @@
1
+ #if UNITY_2021_3_OR_NEWER
2
+ namespace DxMessaging.Unity
3
+ {
4
+ using Core;
5
+ using UnityEngine;
6
+
7
+ /// <summary>
8
+ /// Unity-specific hook that resets DxMessaging static state when domain reloads are skipped.
9
+ /// </summary>
10
+ internal static class DxMessagingRuntimeInitializer
11
+ {
12
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
13
+ private static void ResetStatics()
14
+ {
15
+ DxMessagingStaticState.Reset();
16
+ }
17
+ }
18
+ }
19
+ #endif
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 6e7f0de58bcf4f64813ef3650910c653
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -31,6 +31,10 @@ namespace DxMessaging.Unity
31
31
  )]
32
32
  public sealed class InitialGlobalMessageBusProvider : ScriptableMessageBusProvider
33
33
  {
34
+ /// <summary>
35
+ /// Resolves the message bus captured during static initialization before any runtime overrides occur.
36
+ /// </summary>
37
+ /// <returns>The initial global <see cref="IMessageBus"/> instance.</returns>
34
38
  public override IMessageBus Resolve()
35
39
  {
36
40
  return MessageHandler.InitialGlobalMessageBus;
@@ -11,6 +11,10 @@ namespace DxMessaging.Unity.Integrations.Reflex
11
11
  /// </summary>
12
12
  public sealed class DxMessagingRegistrationInstaller : IInstaller
13
13
  {
14
+ /// <summary>
15
+ /// Registers the DxMessaging builder services within the Reflex container.
16
+ /// </summary>
17
+ /// <param name="containerBuilder">Container builder receiving the registrations.</param>
14
18
  public void InstallBindings(ContainerBuilder containerBuilder)
15
19
  {
16
20
  containerBuilder.AddSingleton(
@@ -25,6 +29,11 @@ namespace DxMessaging.Unity.Integrations.Reflex
25
29
  [Inject]
26
30
  private Container _container;
27
31
 
32
+ /// <summary>
33
+ /// Builds a leasing wrapper using the container-aware provider resolution logic.
34
+ /// </summary>
35
+ /// <param name="options">Build options provided by the caller.</param>
36
+ /// <returns>Lease produced by the underlying <see cref="MessageRegistrationBuilder"/>.</returns>
28
37
  public MessageRegistrationLease Build(MessageRegistrationBuildOptions options)
29
38
  {
30
39
  MessageRegistrationBuilder innerBuilder = ResolveInnerBuilder();
@@ -59,11 +68,19 @@ namespace DxMessaging.Unity.Integrations.Reflex
59
68
  {
60
69
  private readonly Container _container;
61
70
 
71
+ /// <summary>
72
+ /// Wraps a Reflex container as an <see cref="IMessageBusProvider"/>.
73
+ /// </summary>
74
+ /// <param name="container">Container used to resolve <see cref="IMessageBus"/> instances.</param>
62
75
  public ContainerMessageBusProvider(Container container)
63
76
  {
64
77
  _container = container;
65
78
  }
66
79
 
80
+ /// <summary>
81
+ /// Resolves an <see cref="IMessageBus"/> from the underlying container.
82
+ /// </summary>
83
+ /// <returns>Message bus resolved from Reflex.</returns>
67
84
  public IMessageBus Resolve()
68
85
  {
69
86
  return _container.Resolve<IMessageBus>();
@@ -32,11 +32,19 @@ namespace DxMessaging.Unity.Integrations.VContainer
32
32
  {
33
33
  private readonly IObjectResolver _resolver;
34
34
 
35
+ /// <summary>
36
+ /// Wraps a VContainer resolver as an <see cref="IMessageBusProvider"/>.
37
+ /// </summary>
38
+ /// <param name="resolver">Resolver used to obtain <see cref="IMessageBus"/> instances.</param>
35
39
  public ResolverMessageBusProvider(IObjectResolver resolver)
36
40
  {
37
41
  _resolver = resolver;
38
42
  }
39
43
 
44
+ /// <summary>
45
+ /// Resolves an <see cref="IMessageBus"/> from the current VContainer scope.
46
+ /// </summary>
47
+ /// <returns>Scoped message bus.</returns>
40
48
  public IMessageBus Resolve()
41
49
  {
42
50
  return _resolver.Resolve<IMessageBus>();
@@ -16,6 +16,9 @@ namespace DxMessaging.Unity.Integrations.Zenject
16
16
  InstallBindings();
17
17
  }
18
18
 
19
+ /// <summary>
20
+ /// Registers the DxMessaging builder within the Zenject container.
21
+ /// </summary>
19
22
  public override void InstallBindings()
20
23
  {
21
24
  Container.Bind<IMessageRegistrationBuilder>().FromMethod(CreateBuilder).AsTransient();
@@ -40,12 +43,21 @@ namespace DxMessaging.Unity.Integrations.Zenject
40
43
  private readonly DiContainer _container;
41
44
  private readonly IMessageBus _cachedBus;
42
45
 
46
+ /// <summary>
47
+ /// Creates a provider that uses the container-supplied bus, falling back to resolving on demand.
48
+ /// </summary>
49
+ /// <param name="container">Zenject container used to resolve services.</param>
50
+ /// <param name="cachedBus">Cached bus instance to return if available.</param>
43
51
  public ContainerMessageBusProvider(DiContainer container, IMessageBus cachedBus)
44
52
  {
45
53
  _container = container;
46
54
  _cachedBus = cachedBus;
47
55
  }
48
56
 
57
+ /// <summary>
58
+ /// Resolves the message bus for the current scope.
59
+ /// </summary>
60
+ /// <returns>Cached bus if provided; otherwise resolves from the container.</returns>
49
61
  public IMessageBus Resolve()
50
62
  {
51
63
  return _cachedBus ?? _container.Resolve<IMessageBus>();
@@ -3,6 +3,7 @@ namespace DxMessaging.Unity
3
3
  {
4
4
  using System;
5
5
  using System.Collections.Generic;
6
+ using System.Linq;
6
7
  using Core;
7
8
  using Core.MessageBus;
8
9
  using UnityEngine;
@@ -326,6 +327,98 @@ namespace DxMessaging.Unity
326
327
 
327
328
  return null;
328
329
  }
330
+
331
+ #if UNITY_EDITOR
332
+ [ContextMenu("DxMessaging/Dump Registrations")]
333
+ private void ContextDumpRegistrations()
334
+ {
335
+ DumpRegistrations();
336
+ }
337
+
338
+ internal void DumpRegistrations()
339
+ {
340
+ if (_registeredListeners.Count == 0)
341
+ {
342
+ Debug.Log($"[DxMessaging] {name} has no registered listeners.");
343
+ return;
344
+ }
345
+
346
+ foreach (
347
+ KeyValuePair<MonoBehaviour, MessageRegistrationToken> pair in _registeredListeners
348
+ )
349
+ {
350
+ MonoBehaviour listener = pair.Key;
351
+ MessageRegistrationToken token = pair.Value;
352
+ Debug.Log(
353
+ $"[DxMessaging] Listener '{listener}' (Enabled: {token.Enabled}, Diagnostic: {token.DiagnosticMode}) "
354
+ + $"with {token._metadata.Count} registrations on '{name}'."
355
+ );
356
+ }
357
+ }
358
+
359
+ [ContextMenu("DxMessaging/Reset Runtime State")]
360
+ private void ContextResetRuntimeState()
361
+ {
362
+ if (EditorResetRuntimeState())
363
+ {
364
+ Debug.Log($"[DxMessaging] Cleared runtime state for '{name}'.");
365
+ }
366
+ else
367
+ {
368
+ Debug.Log($"[DxMessaging] No runtime state to clear on '{name}'.");
369
+ }
370
+ }
371
+
372
+ internal bool EditorResetRuntimeState()
373
+ {
374
+ bool cleared = false;
375
+
376
+ if (_registeredListeners.Count > 0)
377
+ {
378
+ foreach (MessageRegistrationToken token in _registeredListeners.Values.ToArray())
379
+ {
380
+ try
381
+ {
382
+ if (token.Enabled)
383
+ {
384
+ token.Disable();
385
+ }
386
+
387
+ token.Dispose();
388
+ }
389
+ catch (Exception exception)
390
+ {
391
+ Debug.LogWarning(
392
+ $"[DxMessaging] Disposing stale token on {name} failed. {exception}"
393
+ );
394
+ }
395
+ }
396
+
397
+ _registeredListeners.Clear();
398
+ cleared = true;
399
+ }
400
+
401
+ if (_messageHandler != null)
402
+ {
403
+ _messageHandler = null;
404
+ cleared = true;
405
+ }
406
+
407
+ if (_messageBusOverride != null)
408
+ {
409
+ _messageBusOverride = null;
410
+ cleared = true;
411
+ }
412
+
413
+ if (_messageBusProvider != null)
414
+ {
415
+ _messageBusProvider = null;
416
+ cleared = true;
417
+ }
418
+
419
+ return cleared;
420
+ }
421
+ #endif
329
422
  }
330
423
  }
331
424
  #endif
@@ -13,18 +13,18 @@ These snippets illustrate how to consume `IMessageRegistrationBuilder` inside co
13
13
 
14
14
  Each sample shows:
15
15
 
16
- - Registering `IMessageRegistrationBuilder` via the provided shim under `Runtime/Unity/Integrations/`.
16
+ - Registering `IMessageRegistrationBuilder` via the provided shim under [Runtime/Unity/Integrations](../../Runtime/Unity/Integrations/).
17
17
  - Constructing a `MessageRegistrationLease` in a container-managed service.
18
18
  - Activating/deactivating the lease using the container lifecycle.
19
19
 
20
20
  ### Structure
21
21
 
22
- - `Zenject/SampleInstaller.cs`
23
- - `VContainer/SampleLifetimeScope.cs`
24
- - `Reflex/SampleInstaller.cs`
25
- - `Providers/CurrentGlobalMessageBusProvider.asset` — ScriptableObject that resolves whichever bus is currently configured as global.
26
- - `Providers/InitialGlobalMessageBusProvider.asset` — ScriptableObject that always returns the original startup global bus, ignoring later overrides.
27
- - `Prefabs/MessagingInstallerSample.prefab` — ready-to-use hierarchy with `MessagingComponentInstaller` configuring a child `MessagingComponent` using the provider asset. Drop it into a scene to see provider-driven wiring without writing setup code.
22
+ - Zenject sample installer: [SampleInstaller.cs](./Zenject/SampleInstaller.cs)
23
+ - VContainer sample lifetime scope: [SampleLifetimeScope.cs](./VContainer/SampleLifetimeScope.cs)
24
+ - Reflex sample installer: [SampleInstaller.cs](./Reflex/SampleInstaller.cs)
25
+ - Current global message bus provider asset: [CurrentGlobalMessageBusProvider.asset](./Providers/CurrentGlobalMessageBusProvider.asset) — ScriptableObject that resolves whichever bus is currently configured as global.
26
+ - Initial global message bus provider asset: [InitialGlobalMessageBusProvider.asset](./Providers/InitialGlobalMessageBusProvider.asset) — ScriptableObject that always returns the original startup global bus, ignoring later overrides.
27
+ - Prefab setup: [MessagingInstallerSample.prefab](./Prefabs/MessagingInstallerSample.prefab) — ready-to-use hierarchy with `MessagingComponentInstaller` configuring a child `MessagingComponent` using the provider asset. Drop it into a scene to see provider-driven wiring without writing setup code.
28
28
 
29
29
  ## Walkthrough
30
30
 
@@ -33,19 +33,19 @@ Each sample shows:
33
33
 
34
34
  2. **Hook up the container**
35
35
  - **Zenject**:
36
- - Add `DxMessagingRegistrationInstaller` (from `Runtime/Unity/Integrations/`) to your ProjectContext or scene installer list.
37
- - Drop `Zenject/SampleInstaller.cs` into your project and register it alongside other installers. When the scene runs, the installer resolves `IMessageRegistrationBuilder`, stages a `PlayerSpawned` listener, and activates via the Zenject lifecycle.
36
+ - Add `DxMessagingRegistrationInstaller` (from [Runtime/Unity/Integrations](../../Runtime/Unity/Integrations/)) to your ProjectContext or scene installer list.
37
+ - Drop [SampleInstaller.cs](./Zenject/SampleInstaller.cs) into your project and register it alongside other installers. When the scene runs, the installer resolves `IMessageRegistrationBuilder`, stages a `PlayerSpawned` listener, and activates via the Zenject lifecycle.
38
38
  - **VContainer**:
39
- - Define `VCONTAINER_PRESENT` and reference the optional extension under `Runtime/Unity/Integrations/VContainerRegistrationExtensions.cs`.
40
- - Add `VContainer/SampleLifetimeScope` to the scene (or derive from it); the sample scope registers the builder and an entry point that emits/consumes `ScoreUpdated` messages each tick.
39
+ - Define `VCONTAINER_PRESENT` and reference the optional extension under [VContainerRegistrationExtensions.cs](../../Runtime/Unity/Integrations/VContainerRegistrationExtensions.cs).
40
+ - Add [SampleLifetimeScope.cs](./VContainer/SampleLifetimeScope.cs) to the scene (or derive from it); the sample scope registers the builder and an entry point that emits/consumes `ScoreUpdated` messages each tick.
41
41
  - **Reflex**:
42
42
  - Enable `REFLEX_PRESENT` and install `DxMessagingRegistrationInstaller` into your container bootstrap.
43
- - Include `Reflex/SampleInstaller` in your installer chain. The sample service resolves `IMessageRegistrationBuilder`, subscribes to `PlayerAlert`, and can emit alerts via `EmitAlertFor`.
43
+ - Include [SampleInstaller.cs](./Reflex/SampleInstaller.cs) in your installer chain. The sample service resolves `IMessageRegistrationBuilder`, subscribes to `PlayerAlert`, and can emit alerts via `EmitAlertFor`.
44
44
 
45
45
  3. **Emit a message**
46
46
  Use the service exposed by the container (e.g., call into `ScoreboardService` or `PlayerAlertService`) to emit a message. Because the prefab already configured `MessagingComponent` instances via the installer, the listeners run immediately.
47
47
 
48
48
  4. **Swap providers** (optional)
49
- Duplicate `Providers/CurrentGlobalMessageBusProvider.asset`, modify it to return a custom bus, assign it on the prefab root, and observe how builder-created leases now resolve that bus instead.
49
+ Duplicate [CurrentGlobalMessageBusProvider.asset](./Providers/CurrentGlobalMessageBusProvider.asset), modify it to return a custom bus, assign it on the prefab root, and observe how builder-created leases now resolve that bus instead.
50
50
 
51
51
  Feel free to duplicate these scripts into your own project and adjust lifecycles or message types as needed.
@@ -83,11 +83,11 @@ Here's what each script does:
83
83
 
84
84
  | File | Purpose | Message Type |
85
85
  |------|---------|--------------|
86
- | **Messages.cs** | Defines all message types | Contains `VideoSettingsChanged`, `Heal`, and `TookDamage` |
87
- | **Player.cs** | Handles receiving healing | Listens for `Heal` (Targeted) |
88
- | **Enemy.cs** | Announces when damaged | Emits `TookDamage` (Broadcast) |
89
- | **UIOverlay.cs** | Updates UI based on events | Listens to settings and `TookDamage` (Broadcast) |
90
- | **Boot.cs** | Starts the demo | Simulates message flow |
86
+ | **[Messages.cs](./Messages.cs)** | Defines all message types | Contains `VideoSettingsChanged`, `Heal`, and `TookDamage` |
87
+ | **[Player.cs](./Player.cs)** | Handles receiving healing | Listens for `Heal` (Targeted) |
88
+ | **[Enemy.cs](./Enemy.cs)** | Announces when damaged | Emits `TookDamage` (Broadcast) |
89
+ | **[UIOverlay.cs](./UIOverlay.cs)** | Updates UI based on events | Listens to settings and `TookDamage` (Broadcast) |
90
+ | **[Boot.cs](./Boot.cs)** | Starts the demo | Simulates message flow |
91
91
 
92
92
  ---
93
93
 
@@ -126,10 +126,10 @@ For **each GameObject**, you need TWO components:
126
126
  - Click GameObject → Add Component → "MessagingComponent"
127
127
 
128
128
  1. **Add the sample script**:
129
- - **Player** GameObject → Add `Player.cs` script
130
- - **Enemy** GameObject → Add `Enemy.cs` script
131
- - **UIOverlay** GameObject → Add `UIOverlay.cs` script
132
- - **Boot** GameObject → Add `Boot.cs` script
129
+ - **Player** GameObject → Add [Player.cs](./Player.cs) script
130
+ - **Enemy** GameObject → Add [Enemy.cs](./Enemy.cs) script
131
+ - **UIOverlay** GameObject → Add [UIOverlay.cs](./UIOverlay.cs) script
132
+ - **Boot** GameObject → Add [Boot.cs](./Boot.cs) script
133
133
 
134
134
  #### Step 3: Run and Observe
135
135
 
@@ -147,11 +147,11 @@ Press Play! The Boot script will automatically:
147
147
 
148
148
  ### The Message Flow
149
149
 
150
- #### Boot.cs sends messages:
150
+ #### [Boot.cs](./Boot.cs) sends messages:
151
151
 
152
- 1. `VideoSettingsChanged` (Untargeted) → UIOverlay.cs receives
153
- 2. `Heal` (Targeted to Player) → Player.cs receives
154
- 3. `TookDamage` (Broadcast from Enemy) → UIOverlay.cs receives
152
+ 1. `VideoSettingsChanged` (Untargeted) → [UIOverlay.cs](./UIOverlay.cs) receives
153
+ 2. `Heal` (Targeted to Player) → [Player.cs](./Player.cs) receives
154
+ 3. `TookDamage` (Broadcast from Enemy) → [UIOverlay.cs](./UIOverlay.cs) receives
155
155
 
156
156
  ### Understanding Message Types
157
157
 
@@ -318,6 +318,6 @@ protected override void OnEnable() {
318
318
  ## Quick Reference
319
319
 
320
320
  **Enable Diagnostics**: Select MessagingComponent in Inspector → Enable Diagnostics
321
- **Message Types**: See `Messages.cs` for all available messages
322
- **Modify Behavior**: Edit handler methods in Player.cs, Enemy.cs, or UIOverlay.cs
321
+ **Message Types**: See [Messages.cs](./Messages.cs) for all available messages
322
+ **Modify Behavior**: Edit handler methods in [Player.cs](./Player.cs), [Enemy.cs](./Enemy.cs), or [UIOverlay.cs](./UIOverlay.cs)
323
323
  **Extend Scripts**: Always call `base.RegisterMessageHandlers()` and other `base.*` methods
@@ -17,7 +17,7 @@
17
17
  After reading this walkthrough, you'll know:
18
18
 
19
19
  1. **Why each message type was chosen** - the reasoning behind Untargeted vs Targeted vs Broadcast
20
- 2. **How the code flows** - step-by-step from Boot.cs through every script
20
+ 2. **How the code flows** - step-by-step from [Boot.cs](./Boot.cs) through every script
21
21
  3. **Common patterns** - Observer, Broadcaster, Orchestrator, and more
22
22
  4. **Debugging strategies** - how to find and fix issues
23
23
  5. **How to extend it** - add your own messages and handlers
@@ -43,7 +43,7 @@ After reading this walkthrough, you'll know:
43
43
 
44
44
  Let's understand what each script does:
45
45
 
46
- ### Messages.cs
46
+ ### [Messages.cs](./Messages.cs)
47
47
 
48
48
  **Purpose**: Defines all message types used in the sample
49
49
 
@@ -56,7 +56,7 @@ Let's understand what each script does:
56
56
 
57
57
  **Why separate file?** Keeping messages in one place makes them easy to find and prevents circular dependencies.
58
58
 
59
- ### Player.cs
59
+ ### [Player.cs](./Player.cs)
60
60
 
61
61
  **What it does**: Listens for healing messages targeted specifically at this player
62
62
 
@@ -68,7 +68,7 @@ Let's understand what each script does:
68
68
 
69
69
  **Real-world analogy**: Like having your name called in a doctor's office—only you respond.
70
70
 
71
- ### Enemy.cs
71
+ ### [Enemy.cs](./Enemy.cs)
72
72
 
73
73
  **What it does**: Broadcasts when it takes damage
74
74
 
@@ -80,7 +80,7 @@ Let's understand what each script does:
80
80
 
81
81
  **Real-world analogy**: Like ringing a bell—anyone nearby can hear it.
82
82
 
83
- ### UIOverlay.cs
83
+ ### [UIOverlay.cs](./UIOverlay.cs)
84
84
 
85
85
  **What it does**: Monitors game events and updates the UI
86
86
 
@@ -92,7 +92,7 @@ Let's understand what each script does:
92
92
 
93
93
  **Real-world analogy**: Like a news reporter watching everything and reporting it.
94
94
 
95
- ### Boot.cs
95
+ ### [Boot.cs](./Boot.cs)
96
96
 
97
97
  **What it does**: Starts the demo by simulating a sequence of events
98
98
 
@@ -141,8 +141,8 @@ Settings changes are **global events**—they don't target anyone specifically.
141
141
 
142
142
  ### The Code Flow
143
143
 
144
- 1. **Boot.cs** calls: `MessageHub.Publish(new VideoSettingsChanged())`
145
- 1. **UIOverlay.cs** receives it through its registered handler
144
+ 1. **[Boot.cs](./Boot.cs)** calls: `MessageHub.Publish(new VideoSettingsChanged())`
145
+ 1. **[UIOverlay.cs](./UIOverlay.cs)** receives it through its registered handler
146
146
  1. **UIOverlay** rebuilds the UI with new settings
147
147
 
148
148
  ### Developer Notes
@@ -178,9 +178,9 @@ We want to heal **one specific player**, not all players in the scene.
178
178
 
179
179
  ### The Code Flow
180
180
 
181
- 1. **Boot.cs** finds the Player Component reference
182
- 1. **Boot.cs** calls: `MessageHub.Publish(new Heal(amount), targetComponent)`
183
- 1. **Only the targeted Player.cs** receives it through its handler
181
+ 1. **[Boot.cs](./Boot.cs)** finds the Player Component reference
182
+ 1. **[Boot.cs](./Boot.cs)** calls: `MessageHub.Publish(new Heal(amount), targetComponent)`
183
+ 1. **Only the targeted [Player.cs](./Player.cs)** receives it through its handler
184
184
  1. **Player** increases its HP
185
185
 
186
186
  ### Developer Notes
@@ -216,7 +216,7 @@ The Enemy doesn't know (or care) who needs to know about the damage. It just ann
216
216
 
217
217
  ### The Code Flow
218
218
 
219
- 1. **Enemy.cs** detects it took damage
219
+ 1. **[Enemy.cs](./Enemy.cs)** detects it took damage
220
220
  1. **Enemy** calls: `this.EmitBroadcast(new TookDamage(amount))`
221
221
  1. **UIOverlay** receives it via `RegisterBroadcastWithoutSource`
222
222
  1. **UIOverlay** displays the damage event
@@ -120,7 +120,7 @@ MessageHandler.MessageBus.DiagnosticsMode = true;
120
120
 
121
121
  - Multiple buttons: Add more UI Buttons, add more `UIButtonEmitter`s, and give each a unique `buttonId`.
122
122
  - Targeted vs. untargeted: Notice the sample also sends a targeted `StringMessage` to the emitter’s `gameObject`.
123
- - Your own message type: Open `Messages.cs` to see how `ButtonClicked` is declared using attributes:
123
+ - Your own message type: Open [Messages.cs](./Messages.cs) to see how `ButtonClicked` is declared using attributes:
124
124
 
125
125
  ```csharp
126
126
  [DxUntargetedMessage]
@@ -202,8 +202,8 @@ public class MyObserver : MessageAwareComponent {
202
202
 
203
203
  ## Next Steps
204
204
 
205
- - Quick tour: `Docs/GettingStarted.md`
206
- - Patterns and recipes: `Docs/Patterns.md`
207
- - Explore another sample: `../Mini%20Combat/README.md`
205
+ - Quick tour: [Getting Started guide](../../Docs/GettingStarted.md)
206
+ - Patterns and recipes: [Common Patterns](../../Docs/Patterns.md)
207
+ - Explore another sample: [Mini Combat sample](../Mini%20Combat/README.md)
208
208
 
209
209
  You now have an easy, inspector-first way to publish and observe messages. Build up from here by swapping in your own message types and listeners.
@@ -56,6 +56,10 @@ namespace WallstopStudios.DxMessaging.SourceGenerators
56
56
  ImmutableArray<IFieldSymbol> FieldsToInject // Public readonly non-static fields
57
57
  );
58
58
 
59
+ /// <summary>
60
+ /// Configures the incremental generator pipeline that discovers annotated types and emits constructors.
61
+ /// </summary>
62
+ /// <param name="context">Initialization context provided by Roslyn.</param>
59
63
  public void Initialize(IncrementalGeneratorInitializationContext context)
60
64
  {
61
65
  // Find all class/struct/record declarations that have attribute lists
@@ -70,6 +70,10 @@ namespace WallstopStudios.DxMessaging.SourceGenerators
70
70
  bool HasConflictingMessageAttributes
71
71
  );
72
72
 
73
+ /// <summary>
74
+ /// Configures the incremental generator pipeline that assigns deterministic message identifiers.
75
+ /// </summary>
76
+ /// <param name="context">Initialization context provided by Roslyn.</param>
73
77
  public void Initialize(IncrementalGeneratorInitializationContext context)
74
78
  {
75
79
  // Find all class/struct/record declarations with attributes
@@ -51,11 +51,11 @@ namespace DxMessaging.Tests.Runtime.Core
51
51
  [UnityTest]
52
52
  public IEnumerator MessageBusDiagnosticsRespectBufferSize()
53
53
  {
54
- bool originalDiagnostics = IMessageBus.GlobalDiagnosticsMode;
54
+ DiagnosticsTarget originalDiagnostics = IMessageBus.GlobalDiagnosticsTargets;
55
55
  int originalBufferSize = IMessageBus.GlobalMessageBufferSize;
56
56
  try
57
57
  {
58
- IMessageBus.GlobalDiagnosticsMode = true;
58
+ IMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.All;
59
59
  IMessageBus.GlobalMessageBufferSize = 2;
60
60
 
61
61
  GameObject host = new(nameof(MessageBusDiagnosticsRespectBufferSize));
@@ -90,7 +90,7 @@ namespace DxMessaging.Tests.Runtime.Core
90
90
  }
91
91
  finally
92
92
  {
93
- IMessageBus.GlobalDiagnosticsMode = originalDiagnostics;
93
+ IMessageBus.GlobalDiagnosticsTargets = originalDiagnostics;
94
94
  IMessageBus.GlobalMessageBufferSize = originalBufferSize;
95
95
  }
96
96