com.wallstop-studios.dxmessaging 1.0.0-rc8

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 (96) hide show
  1. package/.gitattributes +63 -0
  2. package/CHANGELOG.md +0 -0
  3. package/CHANGELOG.md.meta +7 -0
  4. package/Editor.meta +8 -0
  5. package/LICENSE.md +7 -0
  6. package/LICENSE.md.meta +7 -0
  7. package/README.md +76 -0
  8. package/README.md.meta +7 -0
  9. package/Runtime/Core/Extensions/MessageExtensions.cs +275 -0
  10. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -0
  11. package/Runtime/Core/Extensions.meta +8 -0
  12. package/Runtime/Core/IMessage.cs +12 -0
  13. package/Runtime/Core/IMessage.cs.meta +11 -0
  14. package/Runtime/Core/InstanceId.cs +117 -0
  15. package/Runtime/Core/InstanceId.cs.meta +11 -0
  16. package/Runtime/Core/MessageBus/IMessageBus.cs +249 -0
  17. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -0
  18. package/Runtime/Core/MessageBus/MessageBus.cs +961 -0
  19. package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -0
  20. package/Runtime/Core/MessageBus/MessagingRegistration.cs +100 -0
  21. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -0
  22. package/Runtime/Core/MessageBus/RegistrationLog.cs +111 -0
  23. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -0
  24. package/Runtime/Core/MessageBus.meta +8 -0
  25. package/Runtime/Core/MessageHandler.cs +1589 -0
  26. package/Runtime/Core/MessageHandler.cs.meta +11 -0
  27. package/Runtime/Core/MessageRegistrationHandle.cs +55 -0
  28. package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -0
  29. package/Runtime/Core/MessageRegistrationToken.cs +885 -0
  30. package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -0
  31. package/Runtime/Core/Messages/IBroadcastMessage.cs +27 -0
  32. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -0
  33. package/Runtime/Core/Messages/ITargetedMessage.cs +26 -0
  34. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -0
  35. package/Runtime/Core/Messages/IUntargetedMessage.cs +26 -0
  36. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -0
  37. package/Runtime/Core/Messages.meta +8 -0
  38. package/Runtime/Core/MessagingDebug.cs +71 -0
  39. package/Runtime/Core/MessagingDebug.cs.meta +11 -0
  40. package/Runtime/Core.meta +8 -0
  41. package/Runtime/Unity/MessageAwareComponent.cs +77 -0
  42. package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -0
  43. package/Runtime/Unity/MessagingComponent.cs +73 -0
  44. package/Runtime/Unity/MessagingComponent.cs.meta +11 -0
  45. package/Runtime/Unity.meta +8 -0
  46. package/Runtime/WallstopStudios.DxMessaging.asmdef +18 -0
  47. package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -0
  48. package/Runtime.meta +8 -0
  49. package/Tests/Editor/WallstopStudios.DxMessaging.Tests.Editor.asmdef +23 -0
  50. package/Tests/Editor/WallstopStudios.DxMessaging.Tests.Editor.asmdef.meta +7 -0
  51. package/Tests/Editor.meta +8 -0
  52. package/Tests/Runtime/Benchmarks/PerformanceTests.cs +196 -0
  53. package/Tests/Runtime/Benchmarks/PerformanceTests.cs.meta +11 -0
  54. package/Tests/Runtime/Benchmarks.meta +8 -0
  55. package/Tests/Runtime/Core/BroadcastTests.cs +600 -0
  56. package/Tests/Runtime/Core/BroadcastTests.cs.meta +11 -0
  57. package/Tests/Runtime/Core/GlobalAcceptAllTests.cs +169 -0
  58. package/Tests/Runtime/Core/GlobalAcceptAllTests.cs.meta +11 -0
  59. package/Tests/Runtime/Core/MessagingTestBase.cs +164 -0
  60. package/Tests/Runtime/Core/MessagingTestBase.cs.meta +11 -0
  61. package/Tests/Runtime/Core/NominalTests.cs +1468 -0
  62. package/Tests/Runtime/Core/NominalTests.cs.meta +11 -0
  63. package/Tests/Runtime/Core/PostProcessorTests.cs +849 -0
  64. package/Tests/Runtime/Core/PostProcessorTests.cs.meta +11 -0
  65. package/Tests/Runtime/Core/RegistrationTests.cs +1045 -0
  66. package/Tests/Runtime/Core/RegistrationTests.cs.meta +11 -0
  67. package/Tests/Runtime/Core/TargetedTests.cs +596 -0
  68. package/Tests/Runtime/Core/TargetedTests.cs.meta +11 -0
  69. package/Tests/Runtime/Core/UntargetedTests.cs +149 -0
  70. package/Tests/Runtime/Core/UntargetedTests.cs.meta +11 -0
  71. package/Tests/Runtime/Core.meta +8 -0
  72. package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs +9 -0
  73. package/Tests/Runtime/Scripts/Components/EmptyMessageAwareComponent.cs.meta +11 -0
  74. package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +140 -0
  75. package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs.meta +11 -0
  76. package/Tests/Runtime/Scripts/Components.meta +8 -0
  77. package/Tests/Runtime/Scripts/Messages/ComplexTargetedMessage.cs +28 -0
  78. package/Tests/Runtime/Scripts/Messages/ComplexTargetedMessage.cs.meta +11 -0
  79. package/Tests/Runtime/Scripts/Messages/SimpleBroadcastMessage.cs +8 -0
  80. package/Tests/Runtime/Scripts/Messages/SimpleBroadcastMessage.cs.meta +11 -0
  81. package/Tests/Runtime/Scripts/Messages/SimpleTargetedMessage.cs +8 -0
  82. package/Tests/Runtime/Scripts/Messages/SimpleTargetedMessage.cs.meta +11 -0
  83. package/Tests/Runtime/Scripts/Messages/SimpleUntargetedMessage.cs +8 -0
  84. package/Tests/Runtime/Scripts/Messages/SimpleUntargetedMessage.cs.meta +11 -0
  85. package/Tests/Runtime/Scripts/Messages.meta +8 -0
  86. package/Tests/Runtime/Scripts.meta +8 -0
  87. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef +22 -0
  88. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.asmdef.meta +7 -0
  89. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.csproj +1 -0
  90. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.csproj.meta +7 -0
  91. package/Tests/Runtime.meta +8 -0
  92. package/Tests.meta +8 -0
  93. package/Third Party Notices.md +1 -0
  94. package/Third Party Notices.md.meta +7 -0
  95. package/package.json +33 -0
  96. package/package.json.meta +7 -0
@@ -0,0 +1,961 @@
1
+ namespace DxMessaging.Core.MessageBus
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.Linq;
6
+ using System.Reflection;
7
+ using Messages;
8
+ using static IMessageBus;
9
+
10
+ /// <summary>
11
+ /// Instanced MessageBus for use cases where you want distinct islands of MessageBuses.
12
+ /// </summary>
13
+ public sealed class MessageBus : IMessageBus
14
+ {
15
+ public int RegisteredTargeted => _targetedSinks.Select(kvp => kvp.Value.Count).Sum();
16
+ public int RegisteredBroadcast => _broadcastSinks.Select(kvp => kvp.Value.Count).Sum();
17
+ public int RegisteredUntargeted => _sinks.Select(kvp => kvp.Value.Count).Sum();
18
+
19
+ private static readonly Type MessageBusType = typeof(MessageBus);
20
+ // For use with re-broadcasting to generic methods
21
+ private static readonly object[] ReflectionMethodArgumentsCache = new object[2];
22
+
23
+ private const BindingFlags ReflectionHelperBindingFlags = BindingFlags.Static | BindingFlags.NonPublic;
24
+
25
+ private delegate void FastUntargetedBroadcast<T>(ref T message) where T : IUntargetedMessage;
26
+ private delegate void FastTargetedBroadcast<T>(ref InstanceId target, ref T message) where T : ITargetedMessage;
27
+ private delegate void FastSourcedBroadcast<T>(ref InstanceId target, ref T message) where T : IBroadcastMessage;
28
+
29
+ public RegistrationLog Log => _log;
30
+
31
+ private readonly Dictionary<Type, Dictionary<MessageHandler, int>> _sinks = new();
32
+ private readonly Dictionary<Type, Dictionary<InstanceId, Dictionary<MessageHandler, int>>> _targetedSinks = new();
33
+ private readonly Dictionary<Type, Dictionary<InstanceId, Dictionary<MessageHandler, int>>> _broadcastSinks = new();
34
+ private readonly Dictionary<Type, Dictionary<MessageHandler, int>> _postProcessingSinks = new();
35
+ private readonly Dictionary<Type, Dictionary<InstanceId, Dictionary<MessageHandler, int>>> _postProcessingTargetedSinks = new();
36
+ private readonly Dictionary<Type, Dictionary<InstanceId, Dictionary<MessageHandler, int>>> _postProcessingBroadcastSinks = new();
37
+ private readonly Dictionary<Type, Dictionary<MessageHandler, int>> _postProcessingTargetedWithoutTargetingSinks = new();
38
+ private readonly Dictionary<Type, Dictionary<MessageHandler, int>> _postProcessingBroadcastWithoutSourceSinks = new();
39
+ private readonly Dictionary<MessageHandler, int> _globalSinks = new();
40
+ private readonly Dictionary<Type, SortedDictionary<int, List<object>>> _interceptsByType = new();
41
+ private readonly Dictionary<object, Dictionary<int, int>> _uniqueInterceptorsAndPriorities = new();
42
+
43
+ private readonly Dictionary<Type, object> _broadcastMethodsByType = new();
44
+
45
+ private readonly RegistrationLog _log = new();
46
+
47
+ // These are used so we aren't allocating as much every time we send messages
48
+ private readonly Stack<List<MessageHandler>> _messageHandlers = new();
49
+ private readonly Stack<List<object>> _interceptors = new();
50
+ private readonly Stack<List<int>> _interceptorKeys = new();
51
+
52
+ public Action RegisterUntargeted<T>(MessageHandler messageHandler) where T : IUntargetedMessage
53
+ {
54
+ return InternalRegisterUntargeted<T>(messageHandler, _sinks, RegistrationMethod.Untargeted);
55
+ }
56
+
57
+ public Action RegisterTargeted<T>(InstanceId target, MessageHandler messageHandler) where T : ITargetedMessage
58
+ {
59
+ return InternalRegisterWithContext<T>(target, messageHandler, _targetedSinks, RegistrationMethod.Targeted);
60
+ }
61
+
62
+ public Action RegisterSourcedBroadcast<T>(InstanceId source, MessageHandler messageHandler) where T : IBroadcastMessage
63
+ {
64
+ return InternalRegisterWithContext<T>(source, messageHandler, _broadcastSinks, RegistrationMethod.Broadcast);
65
+ }
66
+
67
+ public Action RegisterSourcedBroadcastWithoutSource<T>(MessageHandler messageHandler) where T : IBroadcastMessage
68
+ {
69
+ return InternalRegisterUntargeted<T>(messageHandler, _sinks, RegistrationMethod.BroadcastWithoutSource);
70
+ }
71
+
72
+ public Action RegisterTargetedWithoutTargeting<T>(MessageHandler messageHandler) where T : ITargetedMessage
73
+ {
74
+ return InternalRegisterUntargeted<T>(messageHandler, _sinks, RegistrationMethod.TargetedWithoutTargeting);
75
+ }
76
+
77
+ public Action RegisterGlobalAcceptAll(MessageHandler messageHandler)
78
+ {
79
+ if (!_globalSinks.TryGetValue(messageHandler, out int count))
80
+ {
81
+ count = 0;
82
+ }
83
+
84
+ Type type = typeof(IMessage);
85
+ _globalSinks[messageHandler] = count + 1;
86
+ _log.Log(new MessagingRegistration(messageHandler.owner, type, RegistrationType.Register, RegistrationMethod.GlobalAcceptAll));
87
+
88
+ return () =>
89
+ {
90
+ _log.Log(new MessagingRegistration(messageHandler.owner, type, RegistrationType.Deregister, RegistrationMethod.GlobalAcceptAll));
91
+ if (!_globalSinks.TryGetValue(messageHandler, out count))
92
+ {
93
+ MessagingDebug.Log(LogLevel.Error,
94
+ "Received over-deregistration of GlobalAcceptAll for MessageHandler {0}. Check to make sure you're not calling (de)registration multiple times.",
95
+ messageHandler);
96
+ return;
97
+ }
98
+
99
+ if (count <= 1)
100
+ {
101
+ _ = _globalSinks.Remove(messageHandler);
102
+ }
103
+ else
104
+ {
105
+ _globalSinks[messageHandler] = count - 1;
106
+ }
107
+ };
108
+ }
109
+
110
+ public Action RegisterUntargetedInterceptor<T>(UntargetedInterceptor<T> interceptor, int priority = 0) where T : IUntargetedMessage
111
+ {
112
+ return RegisterInterceptor<T>(interceptor, priority);
113
+ }
114
+
115
+ public Action RegisterTargetedInterceptor<T>(TargetedInterceptor<T> interceptor, int priority = 0) where T : ITargetedMessage
116
+ {
117
+ return RegisterInterceptor<T>(interceptor, priority);
118
+ }
119
+
120
+ public Action RegisterBroadcastInterceptor<T>(BroadcastInterceptor<T> interceptor, int priority = 0) where T : IBroadcastMessage
121
+ {
122
+ return RegisterInterceptor<T>(interceptor, priority);
123
+ }
124
+
125
+ public Action RegisterUntargetedPostProcessor<T>(MessageHandler messageHandler) where T : IUntargetedMessage
126
+ {
127
+ return InternalRegisterUntargeted<T>(messageHandler, _postProcessingSinks, RegistrationMethod.UntargetedPostProcessor);
128
+ }
129
+
130
+ public Action RegisterTargetedPostProcessor<T>(InstanceId target, MessageHandler messageHandler) where T : ITargetedMessage
131
+ {
132
+ return InternalRegisterWithContext<T>(target, messageHandler, _postProcessingTargetedSinks, RegistrationMethod.TargetedPostProcessor);
133
+ }
134
+
135
+ public Action RegisterTargetedWithoutTargetingPostProcessor<T>(MessageHandler messageHandler) where T : ITargetedMessage
136
+ {
137
+ return InternalRegisterUntargeted<T>(messageHandler, _postProcessingTargetedWithoutTargetingSinks, RegistrationMethod.TargetedWithoutTargetingPostProcessor);
138
+ }
139
+
140
+ public Action RegisterBroadcastPostProcessor<T>(InstanceId source, MessageHandler messageHandler) where T : IBroadcastMessage
141
+ {
142
+ return InternalRegisterWithContext<T>(source, messageHandler, _postProcessingBroadcastSinks, RegistrationMethod.BroadcastPostProcessor);
143
+ }
144
+
145
+ public Action RegisterBroadcastWithoutSourcePostProcessor<T>(MessageHandler messageHandler) where T : IBroadcastMessage
146
+ {
147
+ return InternalRegisterUntargeted<T>(messageHandler, _postProcessingBroadcastWithoutSourceSinks, RegistrationMethod.BroadcastWithoutSourcePostProcessor);
148
+ }
149
+
150
+ private Action RegisterInterceptor<T>(object interceptor, int priority) where T : IMessage
151
+ {
152
+ Type type = typeof(T);
153
+ if (!_interceptsByType.TryGetValue(type, out SortedDictionary<int, List<object>> prioritizedInterceptors))
154
+ {
155
+ prioritizedInterceptors = new SortedDictionary<int, List<object>>();
156
+ _interceptsByType[type] = prioritizedInterceptors;
157
+ }
158
+
159
+ if (!prioritizedInterceptors.TryGetValue(priority, out List<object> interceptors))
160
+ {
161
+ interceptors = new List<object>();
162
+ prioritizedInterceptors[priority] = interceptors;
163
+ }
164
+
165
+ if (!_uniqueInterceptorsAndPriorities.TryGetValue(interceptor, out Dictionary<int, int> priorityCount))
166
+ {
167
+ priorityCount = new Dictionary<int, int>();
168
+ _uniqueInterceptorsAndPriorities[interceptor] = priorityCount;
169
+ }
170
+
171
+ if (!priorityCount.TryGetValue(priority, out int count))
172
+ {
173
+ count = 0;
174
+ interceptors.Add(interceptor);
175
+ }
176
+
177
+ priorityCount[priority] = count + 1;
178
+
179
+ _log.Log(new MessagingRegistration(InstanceId.EmptyId, type, RegistrationType.Register, RegistrationMethod.Interceptor));
180
+
181
+ return () =>
182
+ {
183
+ _log.Log(new MessagingRegistration(InstanceId.EmptyId, type, RegistrationType.Deregister, RegistrationMethod.Interceptor));
184
+ bool removed = false;
185
+ if (_uniqueInterceptorsAndPriorities.TryGetValue(interceptor, out priorityCount))
186
+ {
187
+ if (priorityCount.TryGetValue(priority, out count))
188
+ {
189
+ if (1 < count)
190
+ {
191
+ priorityCount[priority] = count - 1;
192
+ }
193
+ else
194
+ {
195
+ removed = true;
196
+ _ = priorityCount.Remove(priority);
197
+ }
198
+ }
199
+
200
+ if (priorityCount.Count <= 0)
201
+ {
202
+ _uniqueInterceptorsAndPriorities.Remove(interceptor);
203
+ }
204
+ }
205
+ else
206
+ {
207
+ MessagingDebug.Log(LogLevel.Error,
208
+ "Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
209
+ interceptor);
210
+ }
211
+
212
+ bool complete = false;
213
+ if (removed)
214
+ {
215
+ if (_interceptsByType.TryGetValue(type, out prioritizedInterceptors))
216
+ {
217
+ if (prioritizedInterceptors.TryGetValue(priority, out interceptors))
218
+ {
219
+ complete = interceptors.Remove(interceptor);
220
+ }
221
+ }
222
+
223
+ if (!complete)
224
+ {
225
+ MessagingDebug.Log(LogLevel.Error,
226
+ "Received over-deregistration of Interceptor {0}. Check to make sure you're not calling (de)registration multiple times.",
227
+ interceptor);
228
+ }
229
+ }
230
+ };
231
+ }
232
+
233
+
234
+ public void UntypedUntargetedBroadcast(IUntargetedMessage typedMessage)
235
+ {
236
+ Type messageType = typedMessage.MessageType;
237
+ if (!_broadcastMethodsByType.TryGetValue(messageType, out object untargetedMethod))
238
+ {
239
+ // ReSharper disable once PossibleNullReferenceException
240
+ MethodInfo broadcastMethod = MessageBusType.GetMethod(nameof(UntargetedBroadcast)).MakeGenericMethod(messageType);
241
+ // ReSharper disable once PossibleNullReferenceException
242
+ MethodInfo helperMethod = MessageBusType.GetMethod(nameof(UntargetedBroadcastReflectionHelper), ReflectionHelperBindingFlags).MakeGenericMethod(messageType);
243
+
244
+ ReflectionMethodArgumentsCache[0] = this;
245
+ ReflectionMethodArgumentsCache[1] = broadcastMethod;
246
+ untargetedMethod = helperMethod.Invoke(null, ReflectionMethodArgumentsCache);
247
+ _broadcastMethodsByType[messageType] = untargetedMethod;
248
+ }
249
+
250
+ Action<IUntargetedMessage> broadcast = (Action<IUntargetedMessage>)untargetedMethod;
251
+ broadcast.Invoke(typedMessage);
252
+ }
253
+
254
+
255
+ public void UntargetedBroadcast<TMessage>(ref TMessage typedMessage) where TMessage : IUntargetedMessage
256
+ {
257
+ Type type = typedMessage.MessageType;
258
+ if (!RunUntargetedInterceptors(type, ref typedMessage))
259
+ {
260
+ return;
261
+ }
262
+
263
+ if (0 < _globalSinks.Count)
264
+ {
265
+ IUntargetedMessage untargetedMessage = typedMessage;
266
+ BroadcastGlobalUntargeted(ref untargetedMessage);
267
+ }
268
+
269
+ bool foundAnyHandlers = InternalUntargetedBroadcast(ref typedMessage, type);
270
+
271
+ if (_postProcessingSinks.TryGetValue(type, out Dictionary<MessageHandler, int> handlers) && 0 < handlers.Count)
272
+ {
273
+ foundAnyHandlers = true;
274
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
275
+ try
276
+ {
277
+ foreach (MessageHandler handler in messageHandlers)
278
+ {
279
+ handler.HandleUntargetedPostProcessing(ref typedMessage, this);
280
+ }
281
+ }
282
+ finally
283
+ {
284
+ _messageHandlers.Push(messageHandlers);
285
+ }
286
+ }
287
+
288
+ if (!foundAnyHandlers)
289
+ {
290
+ MessagingDebug.Log(LogLevel.Info, "Could not find a matching untargeted broadcast handler for Message: {0}.", typedMessage);
291
+ }
292
+ }
293
+
294
+ public void UntypedTargetedBroadcast(InstanceId target, ITargetedMessage typedMessage)
295
+ {
296
+ Type messageType = typedMessage.MessageType;
297
+ if (!_broadcastMethodsByType.TryGetValue(messageType, out object targetedMethod))
298
+ {
299
+ // ReSharper disable once PossibleNullReferenceException
300
+ MethodInfo broadcastMethod = MessageBusType.GetMethod(nameof(TargetedBroadcast)).MakeGenericMethod(messageType);
301
+ // ReSharper disable once PossibleNullReferenceException
302
+ MethodInfo helperMethod = MessageBusType.GetMethod(nameof(TargetedBroadcastReflectionHelper), ReflectionHelperBindingFlags).MakeGenericMethod(messageType);
303
+
304
+ ReflectionMethodArgumentsCache[0] = this;
305
+ ReflectionMethodArgumentsCache[1] = broadcastMethod;
306
+ targetedMethod = helperMethod.Invoke(null, ReflectionMethodArgumentsCache);
307
+ _broadcastMethodsByType[messageType] = targetedMethod;
308
+ }
309
+
310
+ Action<InstanceId, ITargetedMessage> broadcast = (Action<InstanceId, ITargetedMessage>)targetedMethod;
311
+ broadcast.Invoke(target, typedMessage);
312
+ }
313
+
314
+
315
+ public void TargetedBroadcast<TMessage>(ref InstanceId target, ref TMessage typedMessage) where TMessage : ITargetedMessage
316
+ {
317
+ Type type = typedMessage.MessageType;
318
+ if (!RunTargetedInterceptors(type, ref typedMessage, ref target))
319
+ {
320
+ return;
321
+ }
322
+
323
+ if (0 < _globalSinks.Count)
324
+ {
325
+ ITargetedMessage targetedMessage = typedMessage;
326
+ BroadcastGlobalTargeted(ref target, ref targetedMessage);
327
+ }
328
+
329
+ bool foundAnyHandlers = false;
330
+ if (_targetedSinks.TryGetValue(type, out Dictionary<InstanceId, Dictionary<MessageHandler, int>> targetedHandlers)
331
+ && targetedHandlers.TryGetValue(target, out Dictionary<MessageHandler, int> handlers)
332
+ && 0 < handlers.Count)
333
+ {
334
+ foundAnyHandlers = true;
335
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
336
+ try
337
+ {
338
+ foreach (MessageHandler handler in messageHandlers)
339
+ {
340
+ handler.HandleTargeted(ref target, ref typedMessage, this);
341
+ }
342
+ }
343
+ finally
344
+ {
345
+ _messageHandlers.Push(messageHandlers);
346
+ }
347
+ }
348
+
349
+ _ = InternalTargetedWithoutTargetingBroadcast(ref target, ref typedMessage, type);
350
+
351
+ if (_postProcessingTargetedSinks.TryGetValue(type, out targetedHandlers) && targetedHandlers.TryGetValue(target, out handlers) && 0 < handlers.Count)
352
+ {
353
+ foundAnyHandlers = true;
354
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
355
+ try
356
+ {
357
+ foreach (MessageHandler handler in messageHandlers)
358
+ {
359
+ handler.HandleTargetedPostProcessing(ref target, ref typedMessage, this);
360
+ }
361
+ }
362
+ finally
363
+ {
364
+ _messageHandlers.Push(messageHandlers);
365
+ }
366
+ }
367
+
368
+ if (_postProcessingTargetedWithoutTargetingSinks.TryGetValue(type, out handlers) && 0 < handlers.Count)
369
+ {
370
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
371
+ try
372
+ {
373
+ foreach (MessageHandler handler in messageHandlers)
374
+ {
375
+ handler.HandleTargetedWithoutTargetingPostProcessing(ref target, ref typedMessage, this);
376
+ }
377
+ }
378
+ finally
379
+ {
380
+ _messageHandlers.Push(messageHandlers);
381
+ }
382
+ }
383
+
384
+ if (!foundAnyHandlers)
385
+ {
386
+ MessagingDebug.Log(LogLevel.Info, "Could not find a matching targeted broadcast handler for Id: {0}, Message: {1}.", target,
387
+ typedMessage);
388
+ }
389
+ }
390
+
391
+ public void UntypedSourcedBroadcast(InstanceId source, IBroadcastMessage typedMessage)
392
+ {
393
+ Type messageType = typedMessage.MessageType;
394
+ if (!_broadcastMethodsByType.TryGetValue(messageType, out object sourcedBroadcastMethod))
395
+ {
396
+ // ReSharper disable once PossibleNullReferenceException
397
+ MethodInfo broadcastMethod = MessageBusType.GetMethod(nameof(SourcedBroadcast)).MakeGenericMethod(messageType);
398
+ // ReSharper disable once PossibleNullReferenceException
399
+ MethodInfo helperMethod = MessageBusType.GetMethod(nameof(SourcedBroadcastReflectionHelper), ReflectionHelperBindingFlags).MakeGenericMethod(messageType);
400
+
401
+ ReflectionMethodArgumentsCache[0] = this;
402
+ ReflectionMethodArgumentsCache[1] = broadcastMethod;
403
+ sourcedBroadcastMethod = helperMethod.Invoke(null, ReflectionMethodArgumentsCache);
404
+
405
+ _broadcastMethodsByType[messageType] = sourcedBroadcastMethod;
406
+ }
407
+
408
+ Action<InstanceId, IBroadcastMessage> broadcast = (Action<InstanceId, IBroadcastMessage>)sourcedBroadcastMethod;
409
+ broadcast.Invoke(source, typedMessage);
410
+ }
411
+
412
+ public void SourcedBroadcast<TMessage>(ref InstanceId source, ref TMessage typedMessage) where TMessage : IBroadcastMessage
413
+ {
414
+ Type type = typedMessage.MessageType;
415
+ if (!RunBroadcastInterceptors(type, ref typedMessage, ref source))
416
+ {
417
+ return;
418
+ }
419
+
420
+ if (0 < _globalSinks.Count)
421
+ {
422
+ IBroadcastMessage broadcastMessage = typedMessage;
423
+ BroadcastGlobalSourcedBroadcast(ref source, ref broadcastMessage);
424
+ }
425
+
426
+ bool foundAnyHandlers = false;
427
+ if (_broadcastSinks.TryGetValue(type, out Dictionary<InstanceId, Dictionary<MessageHandler, int>> broadcastHandlers)
428
+ && broadcastHandlers.TryGetValue(source, out Dictionary<MessageHandler, int> handlers)
429
+ && 0 < handlers.Count)
430
+ {
431
+ foundAnyHandlers = true;
432
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
433
+ try
434
+ {
435
+ foreach (MessageHandler handler in messageHandlers)
436
+ {
437
+ handler.HandleSourcedBroadcast(ref source, ref typedMessage, this);
438
+ }
439
+ }
440
+ finally
441
+ {
442
+ _messageHandlers.Push(messageHandlers);
443
+ }
444
+ }
445
+
446
+ _ = InternalBroadcastWithoutSource(ref source, ref typedMessage, type);
447
+
448
+ if (_postProcessingBroadcastSinks.TryGetValue(type, out broadcastHandlers) && broadcastHandlers.TryGetValue(source, out handlers) && 0 < handlers.Count)
449
+ {
450
+ foundAnyHandlers = true;
451
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
452
+ try
453
+ {
454
+ foreach (MessageHandler handler in messageHandlers)
455
+ {
456
+ handler.HandleSourcedBroadcastPostProcessing(ref source, ref typedMessage, this);
457
+ }
458
+ }
459
+ finally
460
+ {
461
+ _messageHandlers.Push(messageHandlers);
462
+ }
463
+ }
464
+
465
+ if (_postProcessingBroadcastWithoutSourceSinks.TryGetValue(type, out handlers) && 0 < handlers.Count)
466
+ {
467
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
468
+ try
469
+ {
470
+ foreach (MessageHandler handler in messageHandlers)
471
+ {
472
+ handler.HandleSourcedBroadcastWithoutSourcePostProcessing(ref source, ref typedMessage, this);
473
+ }
474
+ }
475
+ finally
476
+ {
477
+ _messageHandlers.Push(messageHandlers);
478
+ }
479
+ }
480
+
481
+ if (!foundAnyHandlers)
482
+ {
483
+ MessagingDebug.Log(LogLevel.Info, "Could not find a matching sourced broadcast handler for Id: {0}, Message: {1}.",
484
+ source, typedMessage);
485
+ }
486
+ }
487
+
488
+ private void BroadcastGlobalUntargeted(ref IUntargetedMessage message)
489
+ {
490
+ if (_globalSinks.Count <= 0)
491
+ {
492
+ return;
493
+ }
494
+
495
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(_globalSinks.Keys);
496
+ try
497
+ {
498
+ foreach (MessageHandler handler in messageHandlers)
499
+ {
500
+ handler.HandleGlobalUntargetedMessage(ref message, this);
501
+ }
502
+ }
503
+ finally
504
+ {
505
+ _messageHandlers.Push(messageHandlers);
506
+ }
507
+ }
508
+
509
+ private void BroadcastGlobalTargeted(ref InstanceId target, ref ITargetedMessage message)
510
+ {
511
+ if (_globalSinks.Count <= 0)
512
+ {
513
+ return;
514
+ }
515
+
516
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(_globalSinks.Keys);
517
+ try
518
+ {
519
+ foreach (MessageHandler handler in messageHandlers)
520
+ {
521
+ handler.HandleGlobalTargetedMessage(ref target, ref message, this);
522
+ }
523
+ }
524
+ finally
525
+ {
526
+ _messageHandlers.Push(messageHandlers);
527
+ }
528
+ }
529
+
530
+ private void BroadcastGlobalSourcedBroadcast(ref InstanceId source, ref IBroadcastMessage message)
531
+ {
532
+ if (_globalSinks.Count <= 0)
533
+ {
534
+ return;
535
+ }
536
+
537
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(_globalSinks.Keys);
538
+ try
539
+ {
540
+ foreach (MessageHandler handler in messageHandlers)
541
+ {
542
+ handler.HandleGlobalSourcedBroadcastMessage(ref source, ref message, this);
543
+ }
544
+ }
545
+ finally
546
+ {
547
+ _messageHandlers.Push(messageHandlers);
548
+ }
549
+ }
550
+
551
+ private bool TryGetInterceptorCaches(Type type, out SortedDictionary<int, List<object>> interceptors, out List<int> interceptorKeys, out List<object> interceptorStack)
552
+ {
553
+ if (!_interceptsByType.TryGetValue(type, out interceptors) || interceptors.Count <= 0)
554
+ {
555
+ interceptorKeys = default;
556
+ interceptorStack = default;
557
+ return false;
558
+ }
559
+
560
+ if (!_interceptors.TryPop(out interceptorStack))
561
+ {
562
+ interceptorStack = new List<object>();
563
+ }
564
+
565
+ if (!_interceptorKeys.TryPop(out interceptorKeys))
566
+ {
567
+ interceptorKeys = new List<int>();
568
+ }
569
+
570
+ return true;
571
+ }
572
+
573
+ private bool RunUntargetedInterceptors<T>(Type type, ref T message) where T : IUntargetedMessage
574
+ {
575
+ if (!TryGetInterceptorCaches(
576
+ type,
577
+ out SortedDictionary<int, List<object>> interceptors,
578
+ out List<int> interceptorKeys,
579
+ out List<object> interceptorStack))
580
+ {
581
+ return true;
582
+ }
583
+
584
+ try
585
+ {
586
+ interceptorKeys.Clear();
587
+ interceptorKeys.AddRange(interceptors.Keys);
588
+ foreach (int priority in interceptorKeys)
589
+ {
590
+ if (!interceptors.TryGetValue(priority, out List<object> untypedInterceptors) || untypedInterceptors.Count <= 0)
591
+ {
592
+ continue;
593
+ }
594
+
595
+ interceptorStack.Clear();
596
+ interceptorStack.AddRange(untypedInterceptors);
597
+
598
+ foreach (object transformer in interceptorStack)
599
+ {
600
+ if (transformer is not UntargetedInterceptor<T> typedTransformer)
601
+ {
602
+ continue;
603
+ }
604
+
605
+ if (!typedTransformer(ref message))
606
+ {
607
+ return false;
608
+ }
609
+ }
610
+ }
611
+ }
612
+ finally
613
+ {
614
+ _interceptors.Push(interceptorStack);
615
+ _interceptorKeys.Push(interceptorKeys);
616
+ }
617
+
618
+ return true;
619
+ }
620
+
621
+ private bool RunTargetedInterceptors<T>(Type type, ref T message, ref InstanceId target) where T : ITargetedMessage
622
+ {
623
+ if (!TryGetInterceptorCaches(
624
+ type,
625
+ out SortedDictionary<int, List<object>> interceptors,
626
+ out List<int> interceptorKeys,
627
+ out List<object> interceptorStack))
628
+ {
629
+ return true;
630
+ }
631
+
632
+ try
633
+ {
634
+ interceptorKeys.Clear();
635
+ interceptorKeys.AddRange(interceptors.Keys);
636
+ foreach (int priority in interceptorKeys)
637
+ {
638
+ if (!interceptors.TryGetValue(priority, out List<object> untypedInterceptors) || untypedInterceptors.Count <= 0)
639
+ {
640
+ continue;
641
+ }
642
+
643
+ interceptorStack.Clear();
644
+ interceptorStack.AddRange(untypedInterceptors);
645
+
646
+ foreach (object transformer in interceptorStack)
647
+ {
648
+ if (transformer is not TargetedInterceptor<T> typedTransformer)
649
+ {
650
+ continue;
651
+ }
652
+
653
+ if (!typedTransformer(ref target, ref message))
654
+ {
655
+ return false;
656
+ }
657
+ }
658
+ }
659
+ }
660
+ finally
661
+ {
662
+ _interceptors.Push(interceptorStack);
663
+ _interceptorKeys.Push(interceptorKeys);
664
+ }
665
+
666
+ return true;
667
+ }
668
+
669
+ private bool RunBroadcastInterceptors<T>(Type type, ref T message, ref InstanceId source) where T : IBroadcastMessage
670
+ {
671
+ if (!TryGetInterceptorCaches(
672
+ type,
673
+ out SortedDictionary<int, List<object>> interceptors,
674
+ out List<int> interceptorKeys,
675
+ out List<object> interceptorStack))
676
+ {
677
+ return true;
678
+ }
679
+
680
+ try
681
+ {
682
+ interceptorKeys.Clear();
683
+ interceptorKeys.AddRange(interceptors.Keys);
684
+ foreach (int priority in interceptorKeys)
685
+ {
686
+ if (!interceptors.TryGetValue(priority, out List<object> untypedInterceptors) || untypedInterceptors.Count <= 0)
687
+ {
688
+ continue;
689
+ }
690
+
691
+ interceptorStack.Clear();
692
+ interceptorStack.AddRange(untypedInterceptors);
693
+
694
+ foreach (object transformer in interceptorStack)
695
+ {
696
+ if (transformer is not BroadcastInterceptor<T> typedTransformer)
697
+ {
698
+ continue;
699
+ }
700
+
701
+ if (!typedTransformer(ref source, ref message))
702
+ {
703
+ return false;
704
+ }
705
+ }
706
+ }
707
+ }
708
+ finally
709
+ {
710
+ _interceptors.Push(interceptorStack);
711
+ _interceptorKeys.Push(interceptorKeys);
712
+ }
713
+
714
+ return true;
715
+ }
716
+
717
+ private bool InternalUntargetedBroadcast<TMessage>(ref TMessage message, Type type) where TMessage : IMessage
718
+ {
719
+ if (!_sinks.TryGetValue(type, out Dictionary<MessageHandler, int> handlers) || handlers.Count <= 0)
720
+ {
721
+ return false;
722
+ }
723
+
724
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
725
+ try
726
+ {
727
+ foreach (MessageHandler handler in messageHandlers)
728
+ {
729
+ handler.HandleUntargetedMessage(ref message, this);
730
+ }
731
+ }
732
+ finally
733
+ {
734
+ _messageHandlers.Push(messageHandlers);
735
+ }
736
+
737
+ return true;
738
+ }
739
+
740
+ private bool InternalTargetedWithoutTargetingBroadcast<TMessage>(
741
+ ref InstanceId target, ref TMessage message, Type type) where TMessage : ITargetedMessage
742
+ {
743
+ if (!_sinks.TryGetValue(type, out Dictionary<MessageHandler, int> handlers) || handlers.Count <= 0)
744
+ {
745
+ return false;
746
+ }
747
+
748
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
749
+ try
750
+ {
751
+ foreach (MessageHandler handler in messageHandlers)
752
+ {
753
+ handler.HandleTargetedWithoutTargeting(ref target, ref message, this);
754
+ }
755
+ }
756
+ finally
757
+ {
758
+ _messageHandlers.Push(messageHandlers);
759
+ }
760
+
761
+ return true;
762
+ }
763
+
764
+ private bool InternalBroadcastWithoutSource<TMessage>(
765
+ ref InstanceId target, ref TMessage message, Type type) where TMessage : IBroadcastMessage
766
+ {
767
+ if (!_sinks.TryGetValue(type, out Dictionary<MessageHandler, int> handlers) || handlers.Count <= 0)
768
+ {
769
+ return false;
770
+ }
771
+
772
+ List<MessageHandler> messageHandlers = GetOrAddMessageHandlerStack(handlers.Keys);
773
+ try
774
+ {
775
+ foreach (MessageHandler handler in messageHandlers)
776
+ {
777
+ handler.HandleSourcedBroadcastWithoutSource(ref target, ref message, this);
778
+ }
779
+ }
780
+ finally
781
+ {
782
+ _messageHandlers.Push(messageHandlers);
783
+ }
784
+
785
+ return true;
786
+ }
787
+
788
+ private Action InternalRegisterUntargeted<T>(MessageHandler messageHandler, Dictionary<Type, Dictionary<MessageHandler, int>> sinks, RegistrationMethod registrationMethod) where T : IMessage
789
+ {
790
+ if (messageHandler == null)
791
+ {
792
+ throw new ArgumentNullException(nameof(messageHandler));
793
+ }
794
+
795
+ InstanceId handlerOwnerId = messageHandler.owner;
796
+ Type type = typeof(T);
797
+
798
+ if (!sinks.TryGetValue(type, out Dictionary<MessageHandler, int> handlers))
799
+ {
800
+ handlers = new Dictionary<MessageHandler, int>();
801
+ sinks[type] = handlers;
802
+ }
803
+
804
+ if (!handlers.TryGetValue(messageHandler, out int count))
805
+ {
806
+ count = 0;
807
+ }
808
+
809
+ handlers[messageHandler] = count + 1;
810
+ _log.Log(new MessagingRegistration(handlerOwnerId, type, RegistrationType.Register, registrationMethod));
811
+
812
+ return () =>
813
+ {
814
+ _log.Log(new MessagingRegistration(handlerOwnerId, type, RegistrationType.Deregister, registrationMethod));
815
+ if (!sinks.TryGetValue(type, out handlers) || !handlers.TryGetValue(messageHandler, out count))
816
+ {
817
+ MessagingDebug.Log(LogLevel.Error,
818
+ "Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
819
+ type, messageHandler);
820
+ return;
821
+ }
822
+
823
+ if (count <= 1)
824
+ {
825
+ bool complete = handlers.Remove(messageHandler);
826
+ bool trulyComplete = true;
827
+ if (handlers.Count <= 0)
828
+ {
829
+ trulyComplete = sinks.Remove(type);
830
+ }
831
+
832
+ if (!complete || !trulyComplete)
833
+ {
834
+ MessagingDebug.Log(LogLevel.Error,
835
+ "Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
836
+ type, messageHandler);
837
+ }
838
+ }
839
+ else
840
+ {
841
+ handlers[messageHandler] = count - 1;
842
+ }
843
+ };
844
+ }
845
+
846
+ private Action InternalRegisterWithContext<T>(InstanceId context, MessageHandler messageHandler, Dictionary<Type, Dictionary<InstanceId, Dictionary<MessageHandler, int>>> sinks, RegistrationMethod registrationMethod)
847
+ {
848
+ if (messageHandler == null)
849
+ {
850
+ throw new ArgumentNullException(nameof(messageHandler));
851
+ }
852
+
853
+ Type type = typeof(T);
854
+ if (!sinks.TryGetValue(type, out Dictionary<InstanceId, Dictionary<MessageHandler, int>> broadcastHandlers))
855
+ {
856
+ broadcastHandlers = new Dictionary<InstanceId, Dictionary<MessageHandler, int>>();
857
+ sinks[type] = broadcastHandlers;
858
+ }
859
+
860
+ if (!broadcastHandlers.TryGetValue(context, out Dictionary<MessageHandler, int> handlers))
861
+ {
862
+ handlers = new Dictionary<MessageHandler, int>();
863
+ broadcastHandlers[context] = handlers;
864
+ }
865
+
866
+ if (!handlers.TryGetValue(messageHandler, out int count))
867
+ {
868
+ count = 0;
869
+ }
870
+
871
+ handlers[messageHandler] = count + 1;
872
+ _log.Log(new MessagingRegistration(context, type, RegistrationType.Register, registrationMethod));
873
+
874
+ return () =>
875
+ {
876
+ _log.Log(new MessagingRegistration(context, type, RegistrationType.Deregister, registrationMethod));
877
+ if (!sinks.TryGetValue(type, out broadcastHandlers) || !broadcastHandlers.TryGetValue(context, out handlers) || !handlers.TryGetValue(messageHandler, out count))
878
+ {
879
+ MessagingDebug.Log(LogLevel.Error,
880
+ "Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
881
+ type, messageHandler);
882
+ return;
883
+ }
884
+
885
+ if (count <= 1)
886
+ {
887
+ bool complete = handlers.Remove(messageHandler);
888
+ if (handlers.Count <= 0)
889
+ {
890
+ _ = broadcastHandlers.Remove(context);
891
+ }
892
+
893
+ if (broadcastHandlers.Count <= 0)
894
+ {
895
+ _ = sinks.Remove(type);
896
+ }
897
+
898
+ if (!complete)
899
+ {
900
+ MessagingDebug.Log(LogLevel.Error,
901
+ "Received over-deregistration of {0} for {1}. Check to make sure you're not calling (de)registration multiple times.",
902
+ type, messageHandler);
903
+ }
904
+ }
905
+ else
906
+ {
907
+ handlers[messageHandler] = count - 1;
908
+ }
909
+ };
910
+ }
911
+
912
+ private List<MessageHandler> GetOrAddMessageHandlerStack(IEnumerable<MessageHandler> handlers)
913
+ {
914
+ if (!_messageHandlers.TryPop(out List<MessageHandler> messageHandlers))
915
+ {
916
+ return new List<MessageHandler>(handlers);
917
+ }
918
+
919
+ messageHandlers.Clear();
920
+ messageHandlers.AddRange(handlers);
921
+ return messageHandlers;
922
+ }
923
+
924
+ // https://blogs.msmvps.com/jonskeet/2008/08/09/making-reflection-fly-and-exploring-delegates/
925
+ private static Action<IUntargetedMessage> UntargetedBroadcastReflectionHelper<T>(IMessageBus messageBus, MethodInfo methodInfo) where T : IUntargetedMessage
926
+ {
927
+ FastUntargetedBroadcast<T> untargetedBroadcast = (FastUntargetedBroadcast<T>) Delegate.CreateDelegate(typeof(FastUntargetedBroadcast<T>), messageBus, methodInfo);
928
+ void UntypedBroadcast(IUntargetedMessage message)
929
+ {
930
+ T typedMessage = (T) message;
931
+ untargetedBroadcast(ref typedMessage);
932
+ }
933
+
934
+ return UntypedBroadcast;
935
+ }
936
+
937
+ private static Action<InstanceId, ITargetedMessage> TargetedBroadcastReflectionHelper<T>(IMessageBus messageBus, MethodInfo methodInfo) where T : ITargetedMessage
938
+ {
939
+ FastTargetedBroadcast<T> targetedBroadcast = (FastTargetedBroadcast<T>) Delegate.CreateDelegate(typeof(FastTargetedBroadcast<T>), messageBus, methodInfo);
940
+ void UntypedBroadcast(InstanceId target, ITargetedMessage message)
941
+ {
942
+ T typedMessage = (T) message;
943
+ targetedBroadcast(ref target, ref typedMessage);
944
+ }
945
+
946
+ return UntypedBroadcast;
947
+ }
948
+
949
+ private static Action<InstanceId, IBroadcastMessage> SourcedBroadcastReflectionHelper<T>(IMessageBus messageBus, MethodInfo methodInfo) where T : IBroadcastMessage
950
+ {
951
+ FastSourcedBroadcast<T> sourcedBroadcast = (FastSourcedBroadcast<T>)Delegate.CreateDelegate(typeof(FastSourcedBroadcast<T>), messageBus, methodInfo);
952
+ void UntypedBroadcast(InstanceId target, IBroadcastMessage message)
953
+ {
954
+ T typedMessage = (T)message;
955
+ sourcedBroadcast(ref target, ref typedMessage);
956
+ }
957
+
958
+ return UntypedBroadcast;
959
+ }
960
+ }
961
+ }