com.wallstop-studios.dxmessaging 2.0.0-rc27.3 → 2.0.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/Docs/Comparisons.md +194 -66
- package/Docs/GettingStarted.md +84 -17
- package/Docs/Overview.md +114 -20
- package/Docs/Patterns.md +41 -2
- package/Docs/Performance.md +9 -9
- package/Docs/VisualGuide.md +75 -10
- 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/README.md +262 -63
- package/Runtime/Core/MessageBus/MessageBus.cs +13 -0
- package/Runtime/Core/MessageHandler.cs +27 -33
- package/Runtime/Core/MessageRegistrationToken.cs +12 -21
- package/Runtime/Unity/MessageAwareComponent.cs +6 -0
- package/Runtime/Unity/MessagingComponent.cs +24 -0
- package/Samples~/Mini Combat/README.md +81 -21
- package/Samples~/Mini Combat/Walkthrough.md +23 -1
- package/Samples~/UI Buttons + Inspector/README.md +55 -12
- package/Tests/Runtime/Core/MessagingComponentLifecycleTests.cs +89 -0
- package/Tests/Runtime/Core/MessagingComponentLifecycleTests.cs.meta +11 -0
- package/Tests/Runtime/Core/ReflexiveMessageWarningTests.cs +86 -0
- package/Tests/Runtime/Core/ReflexiveMessageWarningTests.cs.meta +11 -0
- package/Tests/Runtime/Core/SourceGeneratorNestedTests.cs +1 -1
- package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +116 -0
- package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs.meta +11 -0
- package/Tests/Runtime/Scripts/Components/ReflexiveReceiverComponent.cs +14 -0
- package/Tests/Runtime/Scripts/Components/ReflexiveReceiverComponent.cs.meta +11 -0
- package/package.json +1 -1
package/Docs/Comparisons.md
CHANGED
|
@@ -1,75 +1,127 @@
|
|
|
1
|
-
# Comparisons:
|
|
1
|
+
# Comparisons: DxMessaging vs. Everything Else
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**TL;DR:** If you've used C# events, UnityEvents, or static event buses and thought "there has to be a better way," you're right. DxMessaging fixes the pain points while keeping the benefits.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## This guide shows
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- How DxMessaging addresses each
|
|
12
|
-
- When to use which
|
|
7
|
+
- What's wrong with each approach (with real code examples)
|
|
8
|
+
- How DxMessaging solves it (with real code examples)
|
|
9
|
+
- Honest trade-offs (what you give up, what you gain)
|
|
10
|
+
- When to actually use each approach (no BS)
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
### Table of Contents
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
- [C# Events/Delegates](#standard-c-eventsactions)
|
|
15
|
+
- [UnityEvents](#unityevents-inspector-wiring)
|
|
16
|
+
- [Unity SendMessage](#unity-sendmessage)
|
|
17
|
+
- [Static Event Buses](#global-event-bus-singletons)
|
|
18
|
+
- [Honest Trade-offs](#honest-trade-offs-what-you-give-up-what-you-gain)
|
|
19
|
+
- [Feature Matrix](#feature-by-feature-comparison-matrix)
|
|
20
|
+
- [Decision Guide](#when-each-approach-actually-wins)
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
- Tight coupling: consumers reference producers (or vice‑versa), hurting modularity and tests.
|
|
20
|
-
- Ordering is implicit: hard to coordinate A before B across systems.
|
|
21
|
-
- Hard to observe globally: no built‑in way to inspect “what fired recently”.
|
|
22
|
+
## Standard C# Events/Actions
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
### The Pain Points (You've Felt These)
|
|
25
|
+
|
|
26
|
+
#### 1. Memory Leak Hell
|
|
24
27
|
|
|
25
28
|
```csharp
|
|
26
|
-
public
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
public class UI : MonoBehaviour {
|
|
30
|
+
void OnEnable() {
|
|
31
|
+
GameManager.Instance.OnScoreChanged += UpdateScore;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void OnDisable() {
|
|
35
|
+
// ❌ Forgot this line? MEMORY LEAK!
|
|
36
|
+
GameManager.Instance.OnScoreChanged -= UpdateScore;
|
|
33
37
|
}
|
|
34
38
|
}
|
|
39
|
+
```
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
**Real story:** You forget `OnDisable` once. Six months later: "Why is our mobile game crashing after 30 minutes?"
|
|
42
|
+
|
|
43
|
+
##### 2. Tight Coupling Nightmare
|
|
44
|
+
|
|
45
|
+
```csharp
|
|
46
|
+
public class Spawner {
|
|
47
|
+
public event Action Spawned;
|
|
48
|
+
public void Spawn() => Spawned?.Invoke();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public class UI {
|
|
52
|
+
// ❌ UI now depends on Spawner directly
|
|
53
|
+
[SerializeField] private Spawner spawner;
|
|
54
|
+
|
|
55
|
+
void Awake() {
|
|
56
|
+
spawner.Spawned += OnSpawned; // Tight coupling
|
|
43
57
|
}
|
|
44
|
-
private void OnSpawned() => Refresh();
|
|
45
58
|
}
|
|
46
59
|
```
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
**Problem:** Want to add a second spawner? Refactor Spawner? Hope you like breaking things.
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
- Lifecycle managed by a token; enable/disable tied to a component.
|
|
52
|
-
- Ordering via priority; global inspection via diagnostics and the custom inspector.
|
|
63
|
+
###### 3. Mystery Execution Order
|
|
53
64
|
|
|
54
65
|
```csharp
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
// Which runs first? 🤷
|
|
67
|
+
AudioSystem.OnGameEnd += FadeMusic;
|
|
68
|
+
SaveSystem.OnGameEnd += SaveGame;
|
|
69
|
+
UISystem.OnGameEnd += ShowCredits;
|
|
70
|
+
```
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
[DxAutoConstructor]
|
|
61
|
-
public readonly partial struct Spawned { }
|
|
72
|
+
**Result:** Sometimes SaveGame runs after UISystem shows credits. Flaky bugs that only happen sometimes.
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
###### 4. Debugging Black Hole
|
|
75
|
+
|
|
76
|
+
"Which event fired when? Who's subscribed?" → Set 50 breakpoints and hope.
|
|
77
|
+
|
|
78
|
+
### The DxMessaging Way
|
|
79
|
+
|
|
80
|
+
#### 1. Impossible to Leak
|
|
81
|
+
|
|
82
|
+
```csharp
|
|
83
|
+
public class UI : MessageAwareComponent {
|
|
84
|
+
protected override void RegisterMessageHandlers() {
|
|
85
|
+
base.RegisterMessageHandlers();
|
|
86
|
+
_ = Token.RegisterUntargeted<ScoreChanged>(UpdateScore);
|
|
87
|
+
}
|
|
88
|
+
// ✅ That's it! Automatic cleanup when destroyed.
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
##### 2. Zero Coupling
|
|
93
|
+
|
|
94
|
+
```csharp
|
|
95
|
+
// Spawner doesn't know about UI
|
|
96
|
+
public class Spawner : MonoBehaviour {
|
|
97
|
+
void Spawn() {
|
|
98
|
+
// Just emit, don't care who's listening
|
|
99
|
+
new SpawnedEnemy().Emit();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
66
102
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
void
|
|
103
|
+
// UI doesn't know about Spawner
|
|
104
|
+
public class UI : MessageAwareComponent {
|
|
105
|
+
protected override void RegisterMessageHandlers() {
|
|
106
|
+
_ = Token.RegisterUntargeted<SpawnedEnemy>(OnSpawn);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
70
109
|
```
|
|
71
110
|
|
|
72
|
-
|
|
111
|
+
###### 3. Explicit Execution Order
|
|
112
|
+
|
|
113
|
+
```csharp
|
|
114
|
+
// Clear, documented order
|
|
115
|
+
AudioSystem: priority: 10 // Runs third
|
|
116
|
+
SaveSystem: priority: 0 // Runs first
|
|
117
|
+
UISystem: priority: 5 // Runs second
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
###### 4. Built-in Debugging
|
|
121
|
+
|
|
122
|
+
Open any component in Inspector → See message history with timestamps. Done.
|
|
123
|
+
|
|
124
|
+
## UnityEvents (Inspector Wiring)
|
|
73
125
|
|
|
74
126
|
Problems
|
|
75
127
|
|
|
@@ -99,7 +151,7 @@ DxMessaging
|
|
|
99
151
|
- Strongly‑typed registrations in code; explicit priorities and stages.
|
|
100
152
|
- Inspect and page through emissions/registrations from MessagingComponent inspector.
|
|
101
153
|
|
|
102
|
-
Unity SendMessage
|
|
154
|
+
## Unity SendMessage
|
|
103
155
|
|
|
104
156
|
Problems
|
|
105
157
|
|
|
@@ -120,7 +172,7 @@ var msg = new ReflexiveMessage("OnHit", ReflexiveSendMode.Upwards, 10);
|
|
|
120
172
|
MessageHandler.MessageBus.TargetedBroadcast(ref target, ref msg);
|
|
121
173
|
```
|
|
122
174
|
|
|
123
|
-
Global
|
|
175
|
+
## Global Event Bus Singletons
|
|
124
176
|
|
|
125
177
|
Problems
|
|
126
178
|
|
|
@@ -183,23 +235,36 @@ When to use which
|
|
|
183
235
|
|
|
184
236
|
## Honest Trade-offs: What You Give Up, What You Gain
|
|
185
237
|
|
|
186
|
-
|
|
238
|
+
**Let's be real:** DxMessaging isn't free magic. You trade some things for others. Here's the unfiltered truth about what you gain and what you sacrifice.
|
|
239
|
+
|
|
240
|
+
**Bottom line first:** For game jam prototypes, C# events are faster to write. For anything you'll maintain for months, DxMessaging saves you time and sanity.
|
|
187
241
|
|
|
188
242
|
### Learning Curve
|
|
189
243
|
|
|
190
244
|
#### What You Give Up
|
|
191
245
|
|
|
192
|
-
- ❌ Immediate productivity -
|
|
193
|
-
- ❌ Familiarity -
|
|
194
|
-
- ❌ "Just works" -
|
|
246
|
+
- ❌ **Immediate productivity** - ~1-2 days to feel comfortable (reading docs, trying examples)
|
|
247
|
+
- ❌ **Familiarity** - Your team knows C# events already; DxMessaging is new
|
|
248
|
+
- ❌ **"Just works" intuition** - You need to think: "Which message type? What priority?"
|
|
249
|
+
|
|
250
|
+
**Real talk:** Your first message will take 15 minutes. By the 10th message, you'll be faster than with events.
|
|
195
251
|
|
|
196
252
|
#### What You Gain
|
|
197
253
|
|
|
198
|
-
- ✅ Long-term velocity -
|
|
199
|
-
- ✅
|
|
200
|
-
- ✅ Onboarding
|
|
254
|
+
- ✅ **Long-term velocity** - Adding new features doesn't require touching 5 existing systems
|
|
255
|
+
- ✅ **Debugging is 10x faster** - Inspector shows "what fired when" instantly
|
|
256
|
+
- ✅ **Onboarding is easier** - New devs see explicit message contracts, not hidden event chains
|
|
257
|
+
|
|
258
|
+
**Example:** Junior dev asks "How does damage work?"
|
|
259
|
+
|
|
260
|
+
- **C# events:** "Uh, Player has an OnDamaged event, and HealthBar subscribes in line 47, and..."
|
|
261
|
+
- **DxMessaging:** "Search for `TookDamage` message, see who emits it and who listens."
|
|
201
262
|
|
|
202
|
-
|
|
263
|
+
##### Verdict
|
|
264
|
+
|
|
265
|
+
- Game jam (1 week project): Learning curve not worth it → Stick with C# events
|
|
266
|
+
- Mid-size game (1+ month): Pays off by week 2
|
|
267
|
+
- Large game (6+ months): Essential for sanity
|
|
203
268
|
|
|
204
269
|
### Boilerplate
|
|
205
270
|
|
|
@@ -449,17 +514,80 @@ public void TestAchievementSystem() {
|
|
|
449
514
|
|
|
450
515
|
**Break-even point:** Usually around 10-20 hours into a project, when event management becomes painful.
|
|
451
516
|
|
|
452
|
-
## Making the Decision
|
|
517
|
+
## Making the Decision (Be Honest With Yourself)
|
|
518
|
+
|
|
519
|
+
### Answer these questions honestly
|
|
520
|
+
|
|
521
|
+
### 1. Project Lifespan?
|
|
522
|
+
|
|
523
|
+
- **<1 week (game jam):** Skip DxMessaging → Use C# events or direct calls
|
|
524
|
+
- **1-4 weeks (prototype):** Maybe → If you plan to continue, use DxMessaging
|
|
525
|
+
- **1+ months (real project):** Yes → DxMessaging will save you time
|
|
526
|
+
- **6+ months (production):** Absolutely → You'll thank yourself later
|
|
527
|
+
|
|
528
|
+
### 2. Team Size?
|
|
529
|
+
|
|
530
|
+
- **Solo dev:** Optional → Depends on project complexity
|
|
531
|
+
- **2-3 devs:** Valuable → Reduces communication overhead
|
|
532
|
+
- **4+ devs:** Highly recommended → Clear contracts between systems
|
|
533
|
+
- **Remote/distributed team:** Essential → Explicit message contracts prevent miscommunication
|
|
534
|
+
|
|
535
|
+
### 3. Codebase Size?
|
|
536
|
+
|
|
537
|
+
- **<1k lines:** Skip it → Direct method calls are fine
|
|
538
|
+
- **1k-5k lines:** Consider it → If you're growing fast
|
|
539
|
+
- **5k-20k lines:** Recommended → Coupling becomes painful
|
|
540
|
+
- **20k+ lines:** Absolutely → Refactoring without it is a nightmare
|
|
541
|
+
|
|
542
|
+
### 4. How Many Systems Need to Communicate?
|
|
543
|
+
|
|
544
|
+
- **1-2 systems:** Skip → Just call methods directly
|
|
545
|
+
- **3-5 systems:** Consider → If they don't share references
|
|
546
|
+
- **6-10 systems:** Recommended → Coupling becomes unmanageable
|
|
547
|
+
- **10+ systems:** Essential → You're drowning in SerializeFields
|
|
548
|
+
|
|
549
|
+
### 5. Have You Had Memory Leaks From Forgotten Unsubscribes?
|
|
550
|
+
|
|
551
|
+
- **Never:** Lucky you! Optional
|
|
552
|
+
- **Once or twice:** Consider it → Prevention is cheaper than debugging
|
|
553
|
+
- **Multiple times:** Absolutely → Stop wasting time on this
|
|
554
|
+
- **Currently debugging one:** Drop everything and adopt DxMessaging now
|
|
555
|
+
|
|
556
|
+
### 6. How Often Do You Debug "What Fired When?"
|
|
557
|
+
|
|
558
|
+
- **Never:** You're either lying or working on tiny projects
|
|
559
|
+
- **Rarely:** Optional, but would help
|
|
560
|
+
- **Monthly:** Recommended → Inspector diagnostics will save hours
|
|
561
|
+
- **Weekly:** Absolutely → You're wasting too much time
|
|
562
|
+
|
|
563
|
+
### Quick Decision Matrix
|
|
564
|
+
|
|
565
|
+
```text
|
|
566
|
+
Game Jam → C# Events (speed over safety)
|
|
567
|
+
Prototype → DxMessaging IF continuing, else C# Events
|
|
568
|
+
Production → DxMessaging (unless <1k lines)
|
|
569
|
+
Legacy codebase → Migrate gradually (see Migration Guide)
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### The Real Question
|
|
573
|
+
|
|
574
|
+
#### "Will this project still exist in 3 months?"
|
|
575
|
+
|
|
576
|
+
- **No:** C# events are fine
|
|
577
|
+
- **Yes:** Use DxMessaging
|
|
578
|
+
|
|
579
|
+
##### "Will anyone else work on this code?"
|
|
580
|
+
|
|
581
|
+
- **No:** C# events might be okay
|
|
582
|
+
- **Yes:** Use DxMessaging (future you counts as "someone else")
|
|
453
583
|
|
|
454
|
-
|
|
584
|
+
### Rule of Thumb
|
|
455
585
|
|
|
456
|
-
|
|
457
|
-
1. **Team size?** Solo → Optional | 3+ devs → Highly valuable
|
|
458
|
-
1. **Codebase size?** <5k lines → Optional | >10k lines → Recommended
|
|
459
|
-
1. **Event complexity?** <10 events → Skip | >20 events → Use DxMessaging
|
|
460
|
-
1. **Memory leak history?** None → Optional | Frequent → MUST HAVE
|
|
586
|
+
If you're reading this and thinking:
|
|
461
587
|
|
|
462
|
-
**
|
|
588
|
+
- **"I've experienced these pain points"** → DxMessaging will help
|
|
589
|
+
- **"This seems like overkill"** → You probably don't need it yet
|
|
590
|
+
- **"I need this yesterday"** → Welcome home 🚀
|
|
463
591
|
|
|
464
592
|
See also
|
|
465
593
|
|
package/Docs/GettingStarted.md
CHANGED
|
@@ -27,13 +27,31 @@ By the end of this guide, you will:
|
|
|
27
27
|
|
|
28
28
|
## What is DxMessaging?
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**Picture this:** You're building a Unity game. Your player takes damage. Now you need to:
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
- Update the health bar UI
|
|
33
|
+
- Play a damage sound
|
|
34
|
+
- Show a damage number popup
|
|
35
|
+
- Track damage for analytics
|
|
36
|
+
- Check if an achievement unlocked
|
|
37
|
+
- Maybe trigger a tutorial tip
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
**The old way:** The Player script needs references to all 6 systems. Or they all need references to the Player. It's a tangled mess.
|
|
40
|
+
|
|
41
|
+
**The DxMessaging way:** The Player emits one message: `TookDamage(25)`. Everyone who cares receives it automatically. Zero coupling, zero leaks, zero hassle.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
**Technical summary:** DxMessaging is a high-performance, type-safe messaging system that replaces C# events, UnityEvents, and global event buses with a clean, observable, and predictable communication pattern.
|
|
46
|
+
|
|
47
|
+
**Think of it as:** The event system Unity should have shipped with.
|
|
48
|
+
|
|
49
|
+
### What you get
|
|
50
|
+
|
|
51
|
+
- **Decoupled systems** - no manual subscribe/unsubscribe, impossible to leak
|
|
52
|
+
- **Predictable execution** - priority-based ordering, see exactly what runs when
|
|
53
|
+
- **Actually debuggable** - Inspector shows message history with timestamps
|
|
54
|
+
- **Scales effortlessly** - works for prototypes and 100k+ line codebases
|
|
37
55
|
|
|
38
56
|
## Quick Start
|
|
39
57
|
|
|
@@ -91,27 +109,76 @@ heal.EmitGameObjectTargeted(playerGameObject);
|
|
|
91
109
|
|
|
92
110
|
## Message Types
|
|
93
111
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
### New to messaging? Use this decision tree
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
Is it a global announcement? (pause, settings, scene load)
|
|
116
|
+
→ Use UNTARGETED
|
|
117
|
+
|
|
118
|
+
Are you commanding a specific entity? (heal Player, open Chest #3)
|
|
119
|
+
→ Use TARGETED
|
|
120
|
+
|
|
121
|
+
Is an entity announcing something happened? (Enemy died, Chest opened)
|
|
122
|
+
→ Use BROADCAST
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Examples
|
|
126
|
+
|
|
127
|
+
```csharp
|
|
128
|
+
// Untargeted: "Everyone, the game paused!"
|
|
129
|
+
[DxUntargetedMessage]
|
|
130
|
+
public struct GamePaused { }
|
|
131
|
+
|
|
132
|
+
// Targeted: "Player, heal yourself by 50!"
|
|
133
|
+
[DxTargetedMessage]
|
|
134
|
+
public struct Heal { public int amount; }
|
|
97
135
|
|
|
98
|
-
|
|
136
|
+
// Broadcast: "I (Enemy #3) just died!"
|
|
137
|
+
[DxBroadcastMessage]
|
|
138
|
+
public struct EnemyDied { public string enemyName; }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Still confused?** See the [Visual Guide](VisualGuide.md) for beginner-friendly explanations, or [MessageTypes](MessageTypes.md) for technical details.
|
|
99
142
|
|
|
100
143
|
## Common Patterns
|
|
101
144
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
145
|
+
### Want to see real examples? Here's what DxMessaging excels at
|
|
146
|
+
|
|
147
|
+
- **UI reacting to gameplay** - Health bar updates when player takes damage (without UI knowing about Player)
|
|
148
|
+
- **Achievement systems** - Track ALL kills across ALL enemies with ONE listener
|
|
149
|
+
- **Cross-system coordination** - Scene transitions coordinate audio, save system, and UI automatically
|
|
150
|
+
- **Input handling** - Decouple input system from player controller
|
|
151
|
+
- **Analytics** - Track all events without polluting gameplay code
|
|
105
152
|
|
|
106
|
-
See [
|
|
153
|
+
**Want code examples?** See [Patterns](Patterns.md) for production-ready patterns and [ListeningPatterns](ListeningPatterns.md) for advanced techniques.
|
|
107
154
|
|
|
108
155
|
## Troubleshooting
|
|
109
156
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
157
|
+
### "My handler isn't being called!"
|
|
158
|
+
|
|
159
|
+
✅ Checklist:
|
|
160
|
+
|
|
161
|
+
1. Did you call `base.RegisterMessageHandlers()` first?
|
|
162
|
+
1. Is your component enabled in the scene?
|
|
163
|
+
1. Are you emitting to the right target? (Check GameObject vs Component)
|
|
164
|
+
1. Check the Inspector - does the registration show up?
|
|
165
|
+
|
|
166
|
+
#### "My message is firing twice!"
|
|
167
|
+
|
|
168
|
+
- Check if you accidentally registered the same handler multiple times
|
|
169
|
+
- Make sure you're calling `base.RegisterMessageHandlers()` only once
|
|
170
|
+
|
|
171
|
+
##### "I get a compile error on `[DxAutoConstructor]`"
|
|
172
|
+
|
|
173
|
+
- Did you mark the struct as `partial`? (required for code generation)
|
|
174
|
+
- Example: `public readonly partial struct MyMessage`
|
|
175
|
+
|
|
176
|
+
###### "Which message type should I use?"
|
|
177
|
+
|
|
178
|
+
- See the decision tree in [Message Types](#message-types) above
|
|
179
|
+
- When in doubt, start with `Broadcast` - it's the most flexible
|
|
113
180
|
|
|
114
|
-
See [Troubleshooting](Troubleshooting.md) for
|
|
181
|
+
**Still stuck?** See [Troubleshooting](Troubleshooting.md) for the complete guide or [FAQ](FAQ.md) for common questions.
|
|
115
182
|
|
|
116
183
|
## Next Steps
|
|
117
184
|
|
package/Docs/Overview.md
CHANGED
|
@@ -4,22 +4,89 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
DxMessaging is a high
|
|
7
|
+
DxMessaging is a high-performance, type-safe messaging system for Unity that **eliminates the three biggest pain points** of traditional event systems:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
1. **Memory leaks** from forgotten unsubscribes → Automatic lifecycle management
|
|
10
|
+
1. **Tight coupling** creating refactoring nightmares → Full decoupling with no direct references
|
|
11
|
+
1. **Debugging black holes** ("what fired when?") → Built-in Inspector diagnostics
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
- Predictable lifecycle: explicit registration tokens that you enable/disable with Unity components.
|
|
13
|
-
- Ergonomics and performance: struct‑friendly APIs, by‑ref handlers, zero‑boxing patterns.
|
|
14
|
-
- Observability: interceptors, post‑processors, diagnostics buffers, and registration logs.
|
|
15
|
-
- Scalable message taxonomy: Untargeted, Targeted, and Broadcast messages fit most gameplay/UI flows.
|
|
13
|
+
## What Problems Does It Solve?
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
### For Beginners: "I'm calling methods manually everywhere"
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
#### Your code probably looks like this
|
|
18
|
+
|
|
19
|
+
```csharp
|
|
20
|
+
public class Player : MonoBehaviour {
|
|
21
|
+
public HealthBar healthBar;
|
|
22
|
+
public AudioManager audio;
|
|
23
|
+
public AchievementSystem achievements;
|
|
24
|
+
|
|
25
|
+
void TakeDamage(int amount) {
|
|
26
|
+
health -= amount;
|
|
27
|
+
healthBar.UpdateHealth(health); // Manual call
|
|
28
|
+
audio.PlayDamageSound(); // Manual call
|
|
29
|
+
achievements.CheckDamage(amount); // Manual call
|
|
30
|
+
// Add analytics? More manual calls...
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Every new system = another SerializeField + another manual call.** It's exhausting and brittle.
|
|
36
|
+
|
|
37
|
+
##### DxMessaging fixes this
|
|
38
|
+
|
|
39
|
+
```csharp
|
|
40
|
+
void TakeDamage(int amount) {
|
|
41
|
+
health -= amount;
|
|
42
|
+
new TookDamage(amount).EmitComponentBroadcast(this);
|
|
43
|
+
// Done! Everything else reacts automatically.
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### For Intermediate Devs: "I use C# events but they leak"
|
|
48
|
+
|
|
49
|
+
#### You know this pain
|
|
50
|
+
|
|
51
|
+
```csharp
|
|
52
|
+
void OnEnable() { GameManager.OnScoreChanged += UpdateUI; }
|
|
53
|
+
void OnDisable() { /* Forgot this? 💀 LEAK! */ }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**DxMessaging makes leaks impossible** - automatic cleanup when components die.
|
|
57
|
+
|
|
58
|
+
### For Advanced Devs: "I need observability and control"
|
|
59
|
+
|
|
60
|
+
- ✅ See message history in Inspector (timestamps, payloads, call counts)
|
|
61
|
+
- ✅ Priority-based execution (no more race conditions)
|
|
62
|
+
- ✅ Interceptors (validate/normalize before handlers)
|
|
63
|
+
- ✅ Global observers (track ALL instances of a message type)
|
|
64
|
+
- ✅ Local bus islands (isolated testing, zero global state)
|
|
65
|
+
|
|
66
|
+
## What It Solves (Technical)
|
|
67
|
+
|
|
68
|
+
- **Decoupling without references** - producers/consumers never know about each other
|
|
69
|
+
- **Predictable lifecycle** - explicit tokens tied to Unity component lifecycles
|
|
70
|
+
- **Performance** - struct messages passed by-ref, zero allocations, zero boxing
|
|
71
|
+
- **Observability** - interceptors, post-processors, diagnostics, registration logs
|
|
72
|
+
- **Scalable taxonomy** - three message types (Untargeted/Targeted/Broadcast) cover 99% of use cases
|
|
73
|
+
|
|
74
|
+
## When to Consider DxMessaging
|
|
75
|
+
|
|
76
|
+
### Use it when
|
|
77
|
+
|
|
78
|
+
- You have 3+ systems that need to communicate
|
|
79
|
+
- You've debugged memory leaks from forgotten unsubscribes
|
|
80
|
+
- You're tired of UI depending on 15 different gameplay systems
|
|
81
|
+
- You want to see "what fired when" without setting 50 breakpoints
|
|
82
|
+
- You're building for the long term (months/years, not days)
|
|
83
|
+
|
|
84
|
+
#### Skip it when
|
|
85
|
+
|
|
86
|
+
- Game jam prototype (<1 week)
|
|
87
|
+
- Tiny project (<1000 lines)
|
|
88
|
+
- Single-system communication (just call the method directly)
|
|
89
|
+
- You need synchronous return values (DxMessaging is fire-and-forget)
|
|
23
90
|
|
|
24
91
|
Core ideas
|
|
25
92
|
|
|
@@ -31,15 +98,42 @@ Core ideas
|
|
|
31
98
|
- Pipeline: Interceptors → Handlers → Post‑Processors, with diagnostics optionally enabled.
|
|
32
99
|
- Unity integration: `MessagingComponent` and `MessageAwareComponent` manage lifecycles cleanly.
|
|
33
100
|
|
|
34
|
-
Killer
|
|
101
|
+
## Killer Features (What Makes It Special)
|
|
102
|
+
|
|
103
|
+
### 🚀 Global Observers: The Unique Advantage
|
|
104
|
+
|
|
105
|
+
#### Traditional event systems force you to do this
|
|
106
|
+
|
|
107
|
+
```csharp
|
|
108
|
+
// Subscribe to each entity type separately
|
|
109
|
+
PlayerEvents.OnDamaged += TrackPlayerDamage;
|
|
110
|
+
EnemyEvents.OnDamaged += TrackEnemyDamage;
|
|
111
|
+
NPCEvents.OnDamaged += TrackNPCDamage;
|
|
112
|
+
// Add a new entity type? More subscriptions...
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
##### DxMessaging lets you do this
|
|
116
|
+
|
|
117
|
+
```csharp
|
|
118
|
+
// Subscribe ONCE to ALL damage, regardless of source
|
|
119
|
+
_ = Token.RegisterBroadcastWithoutSource<TookDamage>(
|
|
120
|
+
(InstanceId source, TookDamage msg) => {
|
|
121
|
+
Analytics.Log($"{source} took {msg.amount} damage");
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Real-world impact:** Build achievement systems, combat logs, and analytics that work with ANY entity - even ones that don't exist yet.
|
|
127
|
+
|
|
128
|
+
### Other Killer Features
|
|
35
129
|
|
|
36
|
-
-
|
|
37
|
-
- **
|
|
38
|
-
- **Local bus islands**
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
41
|
-
- **Unity
|
|
42
|
-
- **
|
|
130
|
+
- **Priority-based ordering** - eliminate race conditions, control execution flow explicitly
|
|
131
|
+
- **Interceptor pipeline** - validate/normalize messages BEFORE handlers run (one validation, all handlers protected)
|
|
132
|
+
- **Local bus islands** - isolated testing with zero global state contamination
|
|
133
|
+
- **Zero-allocation design** - struct messages passed by-ref, no boxing, no GC spikes
|
|
134
|
+
- **Auto-constructor generation** - `[DxAutoConstructor]` eliminates boilerplate while keeping type safety
|
|
135
|
+
- **Unity-first helpers** - `EmitGameObjectTargeted()`, `EmitComponentBroadcast()` feel natural
|
|
136
|
+
- **Inspector diagnostics** - see message history, registrations, call counts in real-time
|
|
43
137
|
|
|
44
138
|
---
|
|
45
139
|
|
package/Docs/Patterns.md
CHANGED
|
@@ -1,10 +1,49 @@
|
|
|
1
|
-
# DxMessaging Patterns
|
|
1
|
+
# DxMessaging Patterns: Real-World Solutions
|
|
2
2
|
|
|
3
3
|
[← Back to Index](Index.md) | [Getting Started](GettingStarted.md) | [Message Types](MessageTypes.md) | [Samples](../Samples~/)
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**You're here because:** You understand DxMessaging basics, now you want to see "How do I actually build X?"
|
|
8
|
+
|
|
9
|
+
## What you'll find
|
|
10
|
+
|
|
11
|
+
- **Basic Patterns** - Fundamental building blocks (scene transitions, commands, observability)
|
|
12
|
+
- **Advanced Patterns** - Power user techniques (diagnostics, testing, legacy integration)
|
|
13
|
+
- **Scale Patterns** - Production-ready examples (100+ entities, cross-scene systems, large UI)
|
|
14
|
+
|
|
15
|
+
### Reading guide
|
|
16
|
+
|
|
17
|
+
- **New to DxMessaging?** Start with Basic Patterns 1-8
|
|
18
|
+
- **Intermediate user?** Jump to Advanced Patterns 9-12
|
|
19
|
+
- **Building at scale?** Go straight to Real-World Scale Patterns
|
|
20
|
+
- **Specific problem?** Use Ctrl+F / Cmd+F to search
|
|
21
|
+
|
|
22
|
+
**Philosophy:** These aren't toy examples. They're patterns from real games with real problems.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Links: "I Want To..."
|
|
27
|
+
|
|
28
|
+
### Find your use case, jump to the pattern
|
|
29
|
+
|
|
30
|
+
| I want to... | Go to |
|
|
31
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
32
|
+
| Make UI react to gameplay | [Pattern 2: Directed Commands](#2-directed-commands-targeted) |
|
|
33
|
+
| Coordinate scene transitions | [Pattern 1: Scene-wide Events](#1-scene-wide-events-untargeted) |
|
|
34
|
+
| Build an achievement system | [Pattern 3: Observability](#3-observability-broadcast) + [Global Accept-All](#10-global-accept-all-handlers) |
|
|
35
|
+
| Validate input/damage before it happens | [Pattern 4: Interceptors](#4-validation-and-normalization-interceptors) |
|
|
36
|
+
| Add analytics without touching gameplay | [Pattern 5: Post-Processors](#5-analyticslogging-post-processors) |
|
|
37
|
+
| Track ALL damage from ANY entity | [Pattern: Managing 100+ Entities](#pattern-managing-100-combat-entities) |
|
|
38
|
+
| Build a large UI system (20+ panels) | [Pattern: Large-Scale UI](#pattern-large-scale-ui-system-20-panels) |
|
|
39
|
+
| Make systems run in a specific order | [Pattern: Priority Ordering](#pattern-priority-ordered-execution-for-complex-systems) |
|
|
40
|
+
| Test in isolation | [Pattern 6: Local Bus Islands](#6-local-bus-islands) |
|
|
41
|
+
| Migrate from C# events | [Pattern 9: Bridging Legacy](#9-bridging-legacy-unity-messaging) |
|
|
42
|
+
| Handle persistent systems across scene loads | [Pattern: Cross-Scene Persistent](#pattern-cross-scene-persistent-systems) |
|
|
43
|
+
| See what's happening (debug message flow) | [Pattern 11: Diagnostics](#11-diagnostics-and-tuning) |
|
|
44
|
+
| Build a battle royale / large multiplayer | [Pattern: Battle Royale Example](#real-world-production-example-battle-royale-game) |
|
|
45
|
+
|
|
46
|
+
---
|
|
8
47
|
|
|
9
48
|
## Table of Contents
|
|
10
49
|
|