com.wallstop-studios.dxmessaging 2.1.5 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.artifacts/SourceGenerators.Tests/obj/Debug/net9.0/WallstopStudios.DxMessaging.SourceGenerators.Tests.AssemblyInfo.cs +1 -1
- package/.cspell.json +4 -1
- package/.github/workflows/actionlint.yml +11 -1
- package/.github/workflows/csharpier-autofix.yml +34 -3
- package/.github/workflows/dotnet-tests.yml +13 -0
- package/.github/workflows/format-on-demand.yml +38 -44
- package/.github/workflows/json-format-check.yml +24 -0
- package/.github/workflows/lint-doc-links.yml +13 -0
- package/.github/workflows/markdown-json.yml +21 -4
- package/.github/workflows/markdown-link-text-check.yml +10 -0
- package/.github/workflows/markdown-link-validity.yml +10 -0
- package/.github/workflows/markdownlint.yml +7 -5
- package/.github/workflows/prettier-autofix.yml +67 -11
- package/.github/workflows/release-drafter.yml +2 -2
- package/.github/workflows/sync-wiki.yml +3 -3
- package/.github/workflows/yaml-format-lint.yml +26 -0
- package/.llm/context.md +113 -3
- package/.llm/skills/documentation/changelog-management.md +38 -0
- package/.llm/skills/documentation/documentation-style-guide.md +18 -0
- package/.llm/skills/documentation/documentation-update-workflow.md +2 -0
- package/.llm/skills/documentation/documentation-updates.md +2 -0
- package/.llm/skills/documentation/markdown-compatibility.md +476 -0
- package/.llm/skills/documentation/mermaid-theming.md +326 -0
- package/.llm/skills/documentation/mkdocs-navigation.md +290 -0
- package/.llm/skills/github-actions/git-renormalize-patterns.md +231 -0
- package/.llm/skills/github-actions/workflow-consistency.md +346 -0
- package/.llm/skills/index.md +53 -27
- package/.llm/skills/scripting/javascript-code-quality.md +417 -0
- package/.llm/skills/scripting/regex-documentation.md +461 -0
- package/.llm/skills/scripting/shell-best-practices.md +55 -4
- package/.llm/skills/scripting/validation-patterns.md +418 -0
- package/.llm/skills/specification.md +4 -1
- package/.llm/skills/testing/test-code-quality.md +243 -0
- package/.llm/skills/testing/test-production-code.md +348 -0
- package/CHANGELOG.md +11 -0
- package/README.md +0 -11
- package/Tests/Runtime/Benchmarks/WallstopStudios.DxMessaging.Tests.Runtime.Benchmarks.asmdef +1 -6
- package/Tests/Runtime/Integrations/Reflex/WallstopStudios.DxMessaging.Tests.Runtime.Reflex.asmdef +1 -1
- package/Tests/Runtime/Integrations/VContainer/WallstopStudios.DxMessaging.Tests.Runtime.VContainer.asmdef +1 -1
- package/Tests/Runtime/Integrations/Zenject/WallstopStudios.DxMessaging.Tests.Runtime.Zenject.asmdef +1 -1
- package/coverage/clover.xml +216 -3
- package/coverage/clover.xml.meta +7 -7
- package/coverage/coverage-final.json +2 -1
- package/coverage/coverage-final.json.meta +7 -7
- package/coverage/lcov-report/base.css.meta +1 -1
- package/coverage/lcov-report/block-navigation.js.meta +1 -1
- package/coverage/lcov-report/favicon.png.meta +1 -1
- package/coverage/lcov-report/index.html +25 -10
- package/coverage/lcov-report/index.html.meta +7 -7
- package/coverage/lcov-report/prettify.css.meta +1 -1
- package/coverage/lcov-report/prettify.js.meta +1 -1
- package/coverage/lcov-report/sort-arrow-sprite.png.meta +1 -1
- package/coverage/lcov-report/sorter.js.meta +1 -1
- package/coverage/lcov-report/transform-docs-to-wiki.js.html +1 -1
- package/coverage/lcov-report/transform-docs-to-wiki.js.html.meta +7 -7
- package/coverage/lcov-report/vendor.meta +1 -1
- package/coverage/lcov-report.meta +8 -8
- package/coverage/lcov.info +365 -0
- package/coverage/lcov.info.meta +7 -7
- package/docs/architecture/design-and-architecture.md +0 -1
- package/docs/concepts/index.md +37 -0
- package/docs/concepts/index.md.meta +7 -0
- package/docs/concepts/interceptors-and-ordering.md +0 -2
- package/docs/concepts/mental-model.md +390 -0
- package/docs/concepts/mental-model.md.meta +7 -0
- package/docs/concepts/message-types.md +0 -1
- package/docs/getting-started/getting-started.md +1 -0
- package/docs/getting-started/index.md +6 -5
- package/docs/getting-started/overview.md +1 -0
- package/docs/getting-started/quick-start.md +2 -1
- package/docs/getting-started/visual-guide.md +4 -10
- package/docs/hooks.py +10 -1
- package/docs/images/DxMessaging-banner.svg +1 -1
- package/docs/index.md +7 -7
- package/docs/javascripts/mermaid-config.js +44 -4
- package/docs/reference/helpers.md +130 -154
- package/docs/reference/quick-reference.md +5 -1
- package/docs/reference/reference.md +124 -130
- package/mkdocs.yml +2 -0
- package/package.json +1 -1
- package/scripts/__tests__/generate-skills-index.test.js +397 -0
- package/scripts/__tests__/generate-skills-index.test.js.meta +7 -0
- package/scripts/__tests__/mermaid-config.test.js +467 -0
- package/scripts/__tests__/mermaid-config.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-optional-fields.test.js +1474 -0
- package/scripts/__tests__/validate-skills-optional-fields.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-required-fields.test.js +188 -0
- package/scripts/__tests__/validate-skills-required-fields.test.js.meta +7 -0
- package/scripts/__tests__/validate-skills-tags.test.js +353 -0
- package/scripts/__tests__/validate-skills-tags.test.js.meta +7 -0
- package/scripts/__tests__/validate-workflows.test.js +188 -0
- package/scripts/__tests__/validate-workflows.test.js.meta +7 -0
- package/scripts/generate-skills-index.js +88 -3
- package/scripts/validate-skills.js +230 -30
- package/scripts/validate-workflows.js +272 -0
- package/scripts/validate-workflows.js.meta +7 -0
- package/site/404.html +1 -1
- package/site/advanced/emit-shorthands/index.html +2 -2
- package/site/advanced/message-bus-providers/index.html +2 -2
- package/site/advanced/registration-builders/index.html +2 -2
- package/site/advanced/runtime-configuration/index.html +2 -2
- package/site/advanced/string-messages/index.html +2 -2
- package/site/advanced.meta +1 -1
- package/site/architecture/comparisons/index.html +2 -2
- package/site/architecture/design-and-architecture/index.html +2 -2
- package/site/architecture/performance/index.html +1 -1
- package/site/architecture.meta +1 -1
- package/site/concepts/index.html +1 -0
- package/site/concepts/index.html.meta +7 -0
- package/site/concepts/interceptors-and-ordering/index.html +4 -4
- package/site/concepts/listening-patterns/index.html +2 -2
- package/site/concepts/mental-model/index.html +146 -0
- package/site/concepts/mental-model/index.html.meta +7 -0
- package/site/concepts/mental-model.meta +8 -0
- package/site/concepts/message-types/index.html +2 -2
- package/site/concepts/targeting-and-context/index.html +2 -2
- package/site/concepts.meta +1 -1
- package/site/examples/end-to-end/index.html +2 -2
- package/site/examples/end-to-end-scene-transitions/index.html +2 -2
- package/site/examples.meta +1 -1
- package/site/getting-started/getting-started/index.html +3 -3
- package/site/getting-started/index.html +4 -4
- package/site/getting-started/install/index.html +3 -3
- package/site/getting-started/overview/index.html +2 -2
- package/site/getting-started/quick-start/index.html +2 -2
- package/site/getting-started/visual-guide/index.html +11 -11
- package/site/getting-started.meta +1 -1
- package/site/guides/advanced/index.html +2 -2
- package/site/guides/diagnostics/index.html +2 -2
- package/site/guides/migration-guide/index.html +2 -2
- package/site/guides/patterns/index.html +2 -2
- package/site/guides/testing/index.html +2 -2
- package/site/guides/unity-integration/index.html +2 -2
- package/site/guides.meta +1 -1
- package/site/hooks.py.meta +1 -1
- package/site/images/DxMessaging-banner.svg +119 -0
- package/site/images/DxMessaging-banner.svg.meta +7 -0
- package/site/images.meta +8 -0
- package/site/index.html +2 -2
- package/site/integrations/index.html +2 -2
- package/site/integrations/reflex/index.html +2 -2
- package/site/integrations/vcontainer/index.html +2 -2
- package/site/integrations/zenject/index.html +2 -2
- package/site/integrations.meta +1 -1
- package/site/javascripts/csharp-highlight.js.meta +7 -7
- package/site/javascripts/mermaid-config.js +4 -1
- package/site/javascripts/mermaid-config.js.meta +1 -1
- package/site/javascripts.meta +1 -1
- package/site/reference/compatibility/index.html +1 -1
- package/site/reference/faq/index.html +1 -1
- package/site/reference/glossary/index.html +2 -2
- package/site/reference/helpers/index.html +15 -15
- package/site/reference/quick-reference/index.html +3 -3
- package/site/reference/reference/index.html +37 -37
- package/site/reference/troubleshooting/index.html +1 -1
- package/site/reference.meta +1 -1
- package/site/search/search_index.json +1 -1
- package/site/sitemap.xml +46 -38
- package/site/sitemap.xml.gz +0 -0
- package/site/stylesheets/extra.css.meta +1 -1
- package/site/stylesheets.meta +1 -1
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Mental Model: How to Think About DxMessaging
|
|
2
|
+
|
|
3
|
+
[← Concepts Index](index.md) | [Message Types](message-types.md) | [Getting Started](../getting-started/getting-started.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This guide explains how to think about DxMessaging from first principles. Understanding the mental model makes everything else easier.
|
|
8
|
+
|
|
9
|
+
## The Core Idea
|
|
10
|
+
|
|
11
|
+
DxMessaging is built around one principle: **it gets out of your way**.
|
|
12
|
+
|
|
13
|
+
You have data. You need to pass it around. That's the problem.
|
|
14
|
+
|
|
15
|
+
DxMessaging provides fast, simple primitives as building blocks. You model changes as message types with optional context, using game primitives (GameObjects, components) as that context. The library handles the plumbing.
|
|
16
|
+
|
|
17
|
+
You don't build your game INTO the messaging system. The messaging system is opt-in and optional - a tool you reach for when it helps.
|
|
18
|
+
|
|
19
|
+
## What Problem Does This Solve?
|
|
20
|
+
|
|
21
|
+
In a typical Unity project, you face a common challenge:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
Player takes damage
|
|
25
|
+
├── Health bar needs updating
|
|
26
|
+
├── Sound system plays damage audio
|
|
27
|
+
├── Camera shakes
|
|
28
|
+
├── Achievement system checks milestones
|
|
29
|
+
├── Analytics logs the event
|
|
30
|
+
└── Tutorial system checks for tips
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Traditional approaches require tight coupling: either the Player knows about all these systems, or they all hold references to the Player. Both paths lead to tangled dependencies.
|
|
34
|
+
|
|
35
|
+
DxMessaging inverts this relationship. The Player broadcasts a fact - "I took 25 damage" - and interested systems subscribe to receive it. Systems come and go without the Player knowing or caring.
|
|
36
|
+
|
|
37
|
+
## The Three Message Categories
|
|
38
|
+
|
|
39
|
+
DxMessaging models three fundamental patterns of communication. Each maps to a real-world analogy.
|
|
40
|
+
|
|
41
|
+
### Untargeted: The PA System
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart LR
|
|
45
|
+
S[Someone] -->|announces| PA[📢 PA System]
|
|
46
|
+
PA --> L1[Listener A]
|
|
47
|
+
PA --> L2[Listener B]
|
|
48
|
+
PA --> L3[Listener C]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Untargeted messages are announcements. No specific recipient. No specific sender that matters. Everyone who cares can hear it.
|
|
52
|
+
|
|
53
|
+
> 📝 **Note: No sender identity**
|
|
54
|
+
>
|
|
55
|
+
> Unlike a real PA system, untargeted messages carry no concept of "who announced it." There is no sender identity attached - only the message content itself.
|
|
56
|
+
|
|
57
|
+
#### Real examples
|
|
58
|
+
|
|
59
|
+
- "The game is paused"
|
|
60
|
+
- "Settings changed"
|
|
61
|
+
- "Scene finished loading"
|
|
62
|
+
- "Day/night cycle switched to night"
|
|
63
|
+
|
|
64
|
+
```csharp
|
|
65
|
+
// Define an untargeted message with attributes
|
|
66
|
+
[DxUntargetedMessage]
|
|
67
|
+
[DxAutoConstructor]
|
|
68
|
+
public readonly partial struct GamePaused
|
|
69
|
+
{
|
|
70
|
+
public readonly bool isPaused;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Multiple fields are supported
|
|
74
|
+
[DxUntargetedMessage]
|
|
75
|
+
[DxAutoConstructor]
|
|
76
|
+
public readonly partial struct SettingsChanged
|
|
77
|
+
{
|
|
78
|
+
public readonly float volume;
|
|
79
|
+
public readonly int quality;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Use when:** You need to announce something to whoever might care. The sender doesn't know or care who's listening.
|
|
84
|
+
|
|
85
|
+
### Targeted: The Addressed Letter
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
flowchart LR
|
|
89
|
+
S[Sender] -->|"To: Player"| Letter[📬 Message Bus]
|
|
90
|
+
Letter --> Player[Player receives]
|
|
91
|
+
Other1[Enemy A] -.->|ignores| Letter
|
|
92
|
+
Other2[Enemy B] -.->|ignores| Letter
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Targeted messages are commands or directed events. They have a specific recipient. Only that recipient (and any interested observers) receives them.
|
|
96
|
+
|
|
97
|
+
#### Real examples
|
|
98
|
+
|
|
99
|
+
- "Player, heal for 10 HP"
|
|
100
|
+
- "Door #7, open"
|
|
101
|
+
- "This specific enemy, take 25 damage"
|
|
102
|
+
- "Inventory slot 3, equip this weapon"
|
|
103
|
+
|
|
104
|
+
```csharp
|
|
105
|
+
// Define a targeted message with attributes
|
|
106
|
+
[DxTargetedMessage]
|
|
107
|
+
[DxAutoConstructor]
|
|
108
|
+
public readonly partial struct Heal
|
|
109
|
+
{
|
|
110
|
+
public readonly int amount;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Emit to a specific target
|
|
114
|
+
Heal heal = new Heal(10);
|
|
115
|
+
heal.EmitGameObjectTargeted(playerGameObject);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Use when:** You're commanding a specific entity to do something, or notifying a specific entity about something that happened to it.
|
|
119
|
+
|
|
120
|
+
### Broadcast: The Radio Station
|
|
121
|
+
|
|
122
|
+
```mermaid
|
|
123
|
+
flowchart LR
|
|
124
|
+
Source[Enemy] -->|"I took damage!"| Radio[📻 Message Bus]
|
|
125
|
+
Radio --> L1[Damage Numbers UI]
|
|
126
|
+
Radio --> L2[Achievement Tracker]
|
|
127
|
+
Radio --> L3[Analytics]
|
|
128
|
+
Radio --> L4[Combat Log]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Broadcast messages are facts emitted by a specific source. Unlike targeted messages, there's no intended recipient - just an origin. Anyone who wants to observe can tune in.
|
|
132
|
+
|
|
133
|
+
#### Real examples
|
|
134
|
+
|
|
135
|
+
- "This enemy took 25 damage" (from the enemy)
|
|
136
|
+
- "The player picked up item X" (from the player)
|
|
137
|
+
- "This chest opened" (from the chest)
|
|
138
|
+
- "Projectile hit something" (from the projectile)
|
|
139
|
+
|
|
140
|
+
```csharp
|
|
141
|
+
// Define a broadcast message with attributes
|
|
142
|
+
[DxBroadcastMessage]
|
|
143
|
+
[DxAutoConstructor]
|
|
144
|
+
public readonly partial struct TookDamage
|
|
145
|
+
{
|
|
146
|
+
public readonly int amount;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Emit from a specific source
|
|
150
|
+
TookDamage damage = new TookDamage(25);
|
|
151
|
+
damage.EmitGameObjectBroadcast(thisEnemy);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Use when:** Something happened to an entity, and other systems might want to know about it. The entity doesn't care who's listening.
|
|
155
|
+
|
|
156
|
+
## Deciding Which Type to Use
|
|
157
|
+
|
|
158
|
+
```mermaid
|
|
159
|
+
flowchart TD
|
|
160
|
+
Start([I need to send a message])
|
|
161
|
+
Start --> Q1{Does it matter<br/>WHO sent it?}
|
|
162
|
+
|
|
163
|
+
Q1 -->|No| Q2{Does it matter<br/>WHO receives it?}
|
|
164
|
+
Q2 -->|No| Untargeted[Use UNTARGETED<br/>Global announcement]
|
|
165
|
+
Q2 -->|Yes| Targeted[Use TARGETED<br/>Directed command]
|
|
166
|
+
|
|
167
|
+
Q1 -->|Yes| Q3{Am I commanding<br/>someone to act?}
|
|
168
|
+
Q3 -->|Yes| Targeted
|
|
169
|
+
Q3 -->|No| Broadcast[Use BROADCAST<br/>Observable fact]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
| Question | Untargeted | Targeted | Broadcast |
|
|
173
|
+
| ----------------------------------- | :--------: | :------: | :-------: |
|
|
174
|
+
| Has a specific sender that matters? | ❌ | ❌ | ✅ |
|
|
175
|
+
| Has a specific recipient? | ❌ | ✅ | ❌ |
|
|
176
|
+
| Is it a command? | ❌ | ✅ | ❌ |
|
|
177
|
+
| Is it an observable fact? | Maybe | ❌ | ✅ |
|
|
178
|
+
| Is it a global announcement? | ✅ | ❌ | ❌ |
|
|
179
|
+
|
|
180
|
+
## Tokens and Lifecycle
|
|
181
|
+
|
|
182
|
+
Registration in DxMessaging happens through tokens. A `MessageRegistrationToken` collects your registrations and ties them to a lifecycle.
|
|
183
|
+
|
|
184
|
+
### The Pattern
|
|
185
|
+
|
|
186
|
+
```csharp
|
|
187
|
+
public class HealthDisplay : MessageAwareComponent
|
|
188
|
+
{
|
|
189
|
+
protected override void RegisterMessageHandlers()
|
|
190
|
+
{
|
|
191
|
+
base.RegisterMessageHandlers();
|
|
192
|
+
|
|
193
|
+
// Register handlers through the Token
|
|
194
|
+
_ = Token.RegisterGameObjectBroadcast<TookDamage>(player, OnPlayerDamaged);
|
|
195
|
+
_ = Token.RegisterUntargeted<GamePaused>(OnGamePaused);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private void OnPlayerDamaged(ref TookDamage msg) { /* update display */ }
|
|
199
|
+
private void OnGamePaused(ref GamePaused msg) { /* pause animations */ }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
> ⚠️ **Warning: Always call `base.RegisterMessageHandlers()`**
|
|
204
|
+
>
|
|
205
|
+
> When overriding `RegisterMessageHandlers()`, always call `base.RegisterMessageHandlers()` first. This ensures that any registrations from parent classes are preserved. Forgetting this call can silently break inherited behavior.
|
|
206
|
+
|
|
207
|
+
### What the Token Does
|
|
208
|
+
|
|
209
|
+
```mermaid
|
|
210
|
+
sequenceDiagram
|
|
211
|
+
participant C as Component
|
|
212
|
+
participant T as Token
|
|
213
|
+
participant B as MessageBus
|
|
214
|
+
|
|
215
|
+
Note over C,B: Awake
|
|
216
|
+
C->>T: Create Token
|
|
217
|
+
C->>T: Register handlers
|
|
218
|
+
T-->>T: Stage (not active yet)
|
|
219
|
+
|
|
220
|
+
Note over C,B: OnEnable
|
|
221
|
+
C->>T: Enable()
|
|
222
|
+
T->>B: Activate registrations
|
|
223
|
+
|
|
224
|
+
Note over C,B: Messages flow
|
|
225
|
+
B->>C: Handler called
|
|
226
|
+
|
|
227
|
+
Note over C,B: OnDisable
|
|
228
|
+
C->>T: Disable()
|
|
229
|
+
T->>B: Deactivate registrations
|
|
230
|
+
|
|
231
|
+
Note over C,B: OnDestroy
|
|
232
|
+
C->>T: Dispose/Release
|
|
233
|
+
T->>B: Remove registrations
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The token:
|
|
237
|
+
|
|
238
|
+
1. **Stages** registrations when you call `Register*` methods
|
|
239
|
+
1. **Activates** them when you call `Enable()`
|
|
240
|
+
1. **Deactivates** them when you call `Disable()`
|
|
241
|
+
1. **Cleans up** when the token is disposed or the component is destroyed
|
|
242
|
+
|
|
243
|
+
This maps directly to Unity's lifecycle: registrations activate in `OnEnable`, deactivate in `OnDisable`, and clean up in `OnDestroy`.
|
|
244
|
+
|
|
245
|
+
### Why Tokens Matter
|
|
246
|
+
|
|
247
|
+
Tokens prevent common event system bugs:
|
|
248
|
+
|
|
249
|
+
- **No forgotten unsubscribes**: The token tracks everything
|
|
250
|
+
- **No null reference handlers**: Disabled tokens don't receive messages
|
|
251
|
+
- **No memory leaks**: Token cleanup is automatic with `MessageAwareComponent`
|
|
252
|
+
- **Predictable timing**: Enable/disable follows Unity's enable state
|
|
253
|
+
|
|
254
|
+
## Context Through Game Primitives
|
|
255
|
+
|
|
256
|
+
DxMessaging uses Unity's primitives (GameObjects, Components) as natural context for messages.
|
|
257
|
+
|
|
258
|
+
### InstanceId
|
|
259
|
+
|
|
260
|
+
Every registration and emission uses an `InstanceId` - a lightweight identifier that wraps a Unity Object's instance ID. You rarely need to create these manually because extension methods handle the conversion:
|
|
261
|
+
|
|
262
|
+
```csharp
|
|
263
|
+
// These are equivalent:
|
|
264
|
+
heal.EmitGameObjectTargeted(playerGameObject);
|
|
265
|
+
heal.EmitTargeted((InstanceId)playerGameObject);
|
|
266
|
+
|
|
267
|
+
// Registering for a specific component
|
|
268
|
+
Token.RegisterComponentTargeted<Damage>(this, OnDamage);
|
|
269
|
+
|
|
270
|
+
// Registering for a whole GameObject
|
|
271
|
+
Token.RegisterGameObjectTargeted<Damage>(gameObject, OnDamage);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### GameObject vs Component Targeting
|
|
275
|
+
|
|
276
|
+
You can target at two levels:
|
|
277
|
+
|
|
278
|
+
**GameObject level**: All components on that GameObject can respond
|
|
279
|
+
|
|
280
|
+
```csharp
|
|
281
|
+
// Registration
|
|
282
|
+
Token.RegisterGameObjectTargeted<Command>(gameObject, HandleCommand);
|
|
283
|
+
|
|
284
|
+
// Emission (any component on playerGO can receive this)
|
|
285
|
+
command.EmitGameObjectTargeted(playerGO);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Component level**: Only that specific component responds
|
|
289
|
+
|
|
290
|
+
```csharp
|
|
291
|
+
// Registration
|
|
292
|
+
Token.RegisterComponentTargeted<Command>(this, HandleCommand);
|
|
293
|
+
|
|
294
|
+
// Emission (only this specific component instance receives this)
|
|
295
|
+
command.EmitComponentTargeted(specificComponent);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Observing Without Targeting
|
|
299
|
+
|
|
300
|
+
Sometimes you want to see all messages of a type, regardless of their target or source.
|
|
301
|
+
|
|
302
|
+
### Observing All Targeted Messages
|
|
303
|
+
|
|
304
|
+
```csharp
|
|
305
|
+
// See every Heal message, no matter who it's for
|
|
306
|
+
Token.RegisterTargetedWithoutTargeting<Heal>(OnAnyHeal);
|
|
307
|
+
|
|
308
|
+
void OnAnyHeal(ref InstanceId target, ref Heal msg)
|
|
309
|
+
{
|
|
310
|
+
// 'target' tells you who was healed
|
|
311
|
+
Debug.Log($"Someone healed {target} for {msg.amount}");
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Observing All Broadcast Messages
|
|
316
|
+
|
|
317
|
+
```csharp
|
|
318
|
+
// See every TookDamage message, no matter who broadcast it
|
|
319
|
+
Token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyDamage);
|
|
320
|
+
|
|
321
|
+
void OnAnyDamage(ref InstanceId source, ref TookDamage msg)
|
|
322
|
+
{
|
|
323
|
+
// 'source' tells you who took damage
|
|
324
|
+
Debug.Log($"{source} took {msg.amount} damage");
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
These patterns are useful for:
|
|
329
|
+
|
|
330
|
+
- Analytics systems
|
|
331
|
+
- Combat logs
|
|
332
|
+
- Debug overlays
|
|
333
|
+
- Achievement tracking
|
|
334
|
+
- Network replication
|
|
335
|
+
|
|
336
|
+
## The "Gets Out of the Way" Philosophy
|
|
337
|
+
|
|
338
|
+
DxMessaging is designed to be invisible when you don't need it:
|
|
339
|
+
|
|
340
|
+
1. **No global state pollution**: Messages are just data. Define them anywhere.
|
|
341
|
+
1. **No inheritance requirements**: Implement an interface or add an attribute. That's it.
|
|
342
|
+
1. **No framework lock-in**: Your message structs are plain C#. They work outside DxMessaging.
|
|
343
|
+
1. **No ceremony**: Define a struct, emit it, handle it. Three steps.
|
|
344
|
+
1. **Optional everywhere**: Don't want to use it for something? Don't. Mix and match.
|
|
345
|
+
|
|
346
|
+
The library provides primitives. You compose them into patterns that fit your game.
|
|
347
|
+
|
|
348
|
+
## Common Patterns at a Glance
|
|
349
|
+
|
|
350
|
+
| Pattern | Message Type | Registration | Emission |
|
|
351
|
+
| --------------------- | ------------ | ------------------------------------- | --------------------------------- |
|
|
352
|
+
| Global setting change | Untargeted | `RegisterUntargeted<T>` | `msg.EmitUntargeted()` |
|
|
353
|
+
| Command to entity | Targeted | `RegisterGameObjectTargeted<T>` | `msg.EmitGameObjectTargeted(go)` |
|
|
354
|
+
| Something happened | Broadcast | `RegisterGameObjectBroadcast<T>` | `msg.EmitGameObjectBroadcast(go)` |
|
|
355
|
+
| Observe all of type | Targeted | `RegisterTargetedWithoutTargeting<T>` | (normal emission) |
|
|
356
|
+
| Observe all sources | Broadcast | `RegisterBroadcastWithoutSource<T>` | (normal emission) |
|
|
357
|
+
|
|
358
|
+
## Common Mistakes
|
|
359
|
+
|
|
360
|
+
When getting started with DxMessaging, watch out for these common pitfalls:
|
|
361
|
+
|
|
362
|
+
### Forgetting to Enable the Token
|
|
363
|
+
|
|
364
|
+
Registrations are **staged** when you call `Register*` methods, but they don't become active until the token is enabled. If you're using `MessageAwareComponent`, this is handled automatically. If managing tokens manually, remember to call `Token.Enable()` after registering.
|
|
365
|
+
|
|
366
|
+
### Targeting Component When You Meant GameObject (or Vice Versa)
|
|
367
|
+
|
|
368
|
+
Component-level targeting (`EmitComponentTargeted`) and GameObject-level targeting (`EmitGameObjectTargeted`) are distinct. A message emitted to a specific component won't be received by handlers registered at the GameObject level for the same object, and vice versa. Be deliberate about which level you're targeting.
|
|
369
|
+
|
|
370
|
+
### Forgetting to Call `base.RegisterMessageHandlers()`
|
|
371
|
+
|
|
372
|
+
See the warning in [The Pattern](#the-pattern) above. Always call the base method first when overriding `RegisterMessageHandlers()`.
|
|
373
|
+
|
|
374
|
+
### Not Understanding Synchronous Handler Execution
|
|
375
|
+
|
|
376
|
+
Message handlers are called **synchronously** and **immediately** when a message is emitted. This means:
|
|
377
|
+
|
|
378
|
+
- The emitting code blocks until all handlers complete
|
|
379
|
+
- Long-running handlers will cause frame hitches
|
|
380
|
+
- Handlers execute in registration order (modified by priority)
|
|
381
|
+
- Exceptions in handlers can affect other handlers if not caught
|
|
382
|
+
|
|
383
|
+
Design your handlers to be fast and non-blocking.
|
|
384
|
+
|
|
385
|
+
## Next Steps
|
|
386
|
+
|
|
387
|
+
- [Message Types](message-types.md): Detailed reference for all three types
|
|
388
|
+
- [Listening Patterns](listening-patterns.md): All the ways to receive messages
|
|
389
|
+
- [Getting Started](../getting-started/getting-started.md): Hands-on walkthrough
|
|
390
|
+
- [Patterns Guide](../guides/patterns.md): Real-world usage patterns
|
|
@@ -182,6 +182,7 @@ Checklist:
|
|
|
182
182
|
|
|
183
183
|
## Next Steps
|
|
184
184
|
|
|
185
|
+
- [Mental Model](../concepts/mental-model.md) - Understand the philosophy behind DxMessaging
|
|
185
186
|
- [Visual Guide](visual-guide.md)
|
|
186
187
|
- [Quick Reference](../reference/quick-reference.md)
|
|
187
188
|
- [Diagnostics](../guides/diagnostics.md)
|
|
@@ -5,8 +5,7 @@ This is the documentation for DxMessaging, a type-safe messaging system for Unit
|
|
|
5
5
|
## Visual Documentation Map
|
|
6
6
|
|
|
7
7
|
```mermaid
|
|
8
|
-
|
|
9
|
-
graph TD
|
|
8
|
+
flowchart TD
|
|
10
9
|
Start[START HERE<br/>Visual Guide<br/>5 minutes]
|
|
11
10
|
Start --> Quick[Quick Start<br/>5 min]
|
|
12
11
|
Start --> Getting[Getting Started<br/>10 min]
|
|
@@ -39,6 +38,7 @@ graph TD
|
|
|
39
38
|
|
|
40
39
|
**Never used a messaging system before?** Start here:
|
|
41
40
|
|
|
41
|
+
1. **[Mental Model](../concepts/mental-model.md)** - How to think about DxMessaging (10 min)
|
|
42
42
|
1. **[Visual Guide](visual-guide.md)** - Beginner-friendly visual introduction (5 min)
|
|
43
43
|
1. **[Getting Started Guide](getting-started.md)** - Comprehensive guide with examples (10 min)
|
|
44
44
|
1. **[Quick Start](quick-start.md)** - Your first working message (5 min)
|
|
@@ -48,6 +48,7 @@ graph TD
|
|
|
48
48
|
|
|
49
49
|
### For Absolute Beginners (Never Used Messaging Before)
|
|
50
50
|
|
|
51
|
+
1. Read [Mental Model](../concepts/mental-model.md) (10 min) - Philosophy first!
|
|
51
52
|
1. Read [Visual Guide](visual-guide.md) (5 min) - Pictures and analogies!
|
|
52
53
|
1. Read [Getting Started](getting-started.md) (10 min) - Complete introduction
|
|
53
54
|
1. Try [Quick Start](quick-start.md) (5 min) - Hands-on tutorial
|
|
@@ -166,7 +167,6 @@ From [Common Patterns](../guides/patterns.md):
|
|
|
166
167
|
Every message flows through 3 stages:
|
|
167
168
|
|
|
168
169
|
```mermaid
|
|
169
|
-
%%{init: {'theme': 'neutral'}}%%
|
|
170
170
|
flowchart LR
|
|
171
171
|
P[Producer] --> I[Interceptors<br/>validate/mutate/cancel]
|
|
172
172
|
I --> H[Handlers<br/>main logic by priority]
|
|
@@ -298,9 +298,10 @@ From [Comparisons](../architecture/comparisons.md):
|
|
|
298
298
|
|
|
299
299
|
## Quick Start Path
|
|
300
300
|
|
|
301
|
-
**Absolute Beginner?** Follow this
|
|
301
|
+
**Absolute Beginner?** Follow this 40-minute path:
|
|
302
302
|
|
|
303
|
-
1.
|
|
303
|
+
1. 10 min: [Mental Model](../concepts/mental-model.md) - Understand the philosophy first!
|
|
304
|
+
1. 5 min: [Visual Guide](visual-guide.md) - Pictures & analogies
|
|
304
305
|
1. 10 min: [Getting Started](getting-started.md) - Deep dive
|
|
305
306
|
1. 5 min: [Quick Start](quick-start.md) - Hands-on code
|
|
306
307
|
1. 10 min: Try a [Sample](https://github.com/wallstop/DxMessaging/blob/master/Samples~/Mini%20Combat/README.md) - See it in action
|
|
@@ -142,6 +142,7 @@ _ = Token.RegisterBroadcastWithoutSource<TookDamage>(
|
|
|
142
142
|
|
|
143
143
|
### Start Here
|
|
144
144
|
|
|
145
|
+
- -> [Mental Model](../concepts/mental-model.md) (10 min) - Philosophy and first principles
|
|
145
146
|
- -> [Visual Guide](visual-guide.md) (5 min) - Beginner-friendly introduction
|
|
146
147
|
- -> [Getting Started](getting-started.md) (10 min) - Complete guide
|
|
147
148
|
- -> [Quick Start](quick-start.md) (5 min) - Working example
|
|
@@ -143,7 +143,8 @@ Registration cleanup is automatic. Messages are type-safe.
|
|
|
143
143
|
## Next Steps
|
|
144
144
|
|
|
145
145
|
- **Understand What You Did**
|
|
146
|
-
- -> [
|
|
146
|
+
- -> [Mental Model](../concepts/mental-model.md) (10 min) - Philosophy and first principles
|
|
147
|
+
- -> [Getting Started Guide](getting-started.md) (10 min) - Full explanation with examples
|
|
147
148
|
- -> [Visual Guide](visual-guide.md) (5 min) - Pictures and analogies
|
|
148
149
|
- **Try Real Examples**
|
|
149
150
|
- -> [Mini Combat sample](https://github.com/wallstop/DxMessaging/blob/master/Samples~/Mini%20Combat/README.md) - Working combat example
|
|
@@ -7,8 +7,7 @@ If you're brand new to messaging systems, this visual guide will help you unders
|
|
|
7
7
|
### The Old Way (Spaghetti Code)
|
|
8
8
|
|
|
9
9
|
```mermaid
|
|
10
|
-
|
|
11
|
-
graph LR
|
|
10
|
+
flowchart LR
|
|
12
11
|
Player[Player]
|
|
13
12
|
Enemy[Enemy]
|
|
14
13
|
Inventory[Inventory]
|
|
@@ -36,8 +35,7 @@ graph LR
|
|
|
36
35
|
### The DxMessaging Way (Clean Separation)
|
|
37
36
|
|
|
38
37
|
```mermaid
|
|
39
|
-
|
|
40
|
-
graph TB
|
|
38
|
+
flowchart TB
|
|
41
39
|
Player[Player]
|
|
42
40
|
Enemy[Enemy]
|
|
43
41
|
Inventory[Inventory]
|
|
@@ -152,7 +150,6 @@ _ = token.RegisterBroadcastWithoutSource<TookDamage>(OnAnyEnemy);
|
|
|
152
150
|
When you send a message, here's what happens:
|
|
153
151
|
|
|
154
152
|
```mermaid
|
|
155
|
-
%%{init: {'theme': 'neutral'}}%%
|
|
156
153
|
sequenceDiagram
|
|
157
154
|
participant You as Your Code
|
|
158
155
|
participant Msg as Message
|
|
@@ -268,7 +265,6 @@ healMsg.EmitComponentTargeted(playerComponent);
|
|
|
268
265
|
### Pattern: Scene Transition
|
|
269
266
|
|
|
270
267
|
```mermaid
|
|
271
|
-
%%{init: {'theme': 'neutral'}}%%
|
|
272
268
|
sequenceDiagram
|
|
273
269
|
participant SM as SceneManager
|
|
274
270
|
participant Bus as Message Bus
|
|
@@ -311,7 +307,6 @@ _ = saveToken.RegisterUntargeted<SceneChanged>(OnScene);
|
|
|
311
307
|
### Pattern: Player Input -> Action
|
|
312
308
|
|
|
313
309
|
```mermaid
|
|
314
|
-
%%{init: {'theme': 'neutral'}}%%
|
|
315
310
|
sequenceDiagram
|
|
316
311
|
participant Input as InputSystem
|
|
317
312
|
participant Bus as Message Bus
|
|
@@ -349,7 +344,6 @@ void OnJump(ref Jump msg) {
|
|
|
349
344
|
### Pattern: Achievement Tracking
|
|
350
345
|
|
|
351
346
|
```mermaid
|
|
352
|
-
%%{init: {'theme': 'neutral'}}%%
|
|
353
347
|
sequenceDiagram
|
|
354
348
|
participant E as Enemy
|
|
355
349
|
participant P as Player
|
|
@@ -472,8 +466,7 @@ DxMessaging has built-in Inspector support!
|
|
|
472
466
|
## Learning Path
|
|
473
467
|
|
|
474
468
|
```mermaid
|
|
475
|
-
|
|
476
|
-
graph TD
|
|
469
|
+
flowchart TD
|
|
477
470
|
Start[START HERE<br/>Read this Visual Guide<br/>5 min]
|
|
478
471
|
Start --> Step2[Try Quick Start example<br/>5 min<br/>Define -> Listen -> Send]
|
|
479
472
|
Step2 --> Step3[Import Mini Combat sample<br/>10 min<br/>See it in action!]
|
|
@@ -601,6 +594,7 @@ If you checked all these, you are following best practices.
|
|
|
601
594
|
|
|
602
595
|
Ready to dive deeper?
|
|
603
596
|
|
|
597
|
+
1. **[Mental Model](../concepts/mental-model.md)** - Understand the philosophy
|
|
604
598
|
1. **[Getting Started Guide](getting-started.md)** - Full guide with more details
|
|
605
599
|
1. **[Common Patterns](../guides/patterns.md)** - Real-world examples
|
|
606
600
|
1. **[Message Types](../concepts/message-types.md)** - Deep dive into when to use what
|
package/docs/hooks.py
CHANGED
|
@@ -28,11 +28,20 @@ SOURCE_PREFIXES = ("Runtime/", "Tests/", "Editor/", "Samples~/")
|
|
|
28
28
|
MARKDOWN_LINK_PATTERN = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
|
|
29
29
|
|
|
30
30
|
# Regex to match fenced code blocks (``` or ~~~ with optional language specifier)
|
|
31
|
-
#
|
|
31
|
+
# Regex flags used:
|
|
32
|
+
# - DOTALL (s): Makes . match newline characters, allowing pattern to span multiple lines
|
|
33
|
+
# The re.sub() method with this pattern finds all occurrences (equivalent to 'g' global flag)
|
|
34
|
+
# Pattern is case-sensitive (no IGNORECASE flag needed - backticks are symbols)
|
|
35
|
+
# No MULTILINE flag needed - we don't use ^ or $ anchors
|
|
32
36
|
FENCED_CODE_BLOCK_PATTERN = re.compile(r"(```|~~~)[^\n]*\n.*?\1", re.DOTALL)
|
|
33
37
|
|
|
34
38
|
# Regex to match inline code (handles multiple backticks like `` or ```)
|
|
35
39
|
# Matches backtick(s), then content that doesn't contain that same sequence, then same backticks
|
|
40
|
+
# Regex flags used:
|
|
41
|
+
# - DOTALL (s): Makes . match newline characters, allowing inline code to span lines
|
|
42
|
+
# The re.sub() method with this pattern finds all occurrences (equivalent to 'g' global flag)
|
|
43
|
+
# Pattern is case-sensitive (no IGNORECASE flag needed - backticks are symbols)
|
|
44
|
+
# No MULTILINE flag needed - we don't use ^ or $ anchors
|
|
36
45
|
INLINE_CODE_PATTERN = re.compile(r"(`+)(?!`)(.*?)(?<!`)\1(?!`)", re.DOTALL)
|
|
37
46
|
|
|
38
47
|
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
<!-- Version badge (top right) - text must contain vX.Y.Z for version sync -->
|
|
108
108
|
<g transform="translate(720, 25)">
|
|
109
109
|
<rect x="0" y="-12" width="60" height="22" rx="11" fill="#e94560" filter="url(#softShadow)"/>
|
|
110
|
-
<text x="30" y="4" text-anchor="middle" font-family="'SF Mono', 'Fira Code', monospace" font-size="12" font-weight="600" fill="#ffffff" letter-spacing="0.5">v2.1.
|
|
110
|
+
<text x="30" y="4" text-anchor="middle" font-family="'SF Mono', 'Fira Code', monospace" font-size="12" font-weight="600" fill="#ffffff" letter-spacing="0.5">v2.1.6</text>
|
|
111
111
|
</g>
|
|
112
112
|
|
|
113
113
|
<!-- Wallstop Studios branding (bottom right) -->
|
package/docs/index.md
CHANGED
|
@@ -7,19 +7,19 @@ description: High-performance type-safe messaging library for Unity
|
|
|
7
7
|
|
|
8
8
|
**DxMessaging** is a high-performance, type-safe messaging library for Unity that provides a clean, decoupled communication pattern between game components.
|
|
9
9
|
|
|
10
|
-
[Get Started](getting-started/index.md)
|
|
11
|
-
[View on GitHub](https://github.com/wallstop/DxMessaging){ .md-button }
|
|
10
|
+
**[Get Started](getting-started/index.md)** | [View on GitHub](https://github.com/wallstop/DxMessaging)
|
|
12
11
|
|
|
13
12
|
## Why DxMessaging?
|
|
14
13
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
14
|
+
- ⚡ **High Performance** — Zero-allocation message dispatch with pooled handlers
|
|
15
|
+
- 🛡️ **Type-Safe** — Compile-time message type checking prevents runtime errors
|
|
16
|
+
- 📦 **Decoupled Architecture** — Components communicate without direct references
|
|
17
|
+
- 🎯 **Flexible Targeting** — Untargeted, Targeted, and Broadcast message patterns
|
|
18
|
+
- 🔧 **Unity-Native** — Built specifically for Unity with MonoBehaviour integration
|
|
20
19
|
|
|
21
20
|
## Quick Links
|
|
22
21
|
|
|
22
|
+
- **[Mental Model](concepts/mental-model.md)** — How to think about DxMessaging
|
|
23
23
|
- **[Visual Guide](getting-started/visual-guide.md)** — Beginner-friendly introduction with diagrams
|
|
24
24
|
- **[Quick Start](getting-started/quick-start.md)** — Your first message in 5 minutes
|
|
25
25
|
- **[Message Types](concepts/message-types.md)** — Untargeted, Targeted, Broadcast patterns
|