com.wallstop-studios.dxmessaging 2.0.0-rc21 → 2.0.0-rc23

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 (32) hide show
  1. package/.github/workflows/npm-publish.yml +9 -0
  2. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  3. package/README.md +12 -7
  4. package/Runtime/Core/Extensions/EnumExtensions.cs +37 -0
  5. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +3 -0
  6. package/Runtime/Core/Helper/DxMessagingRuntime.cs +201 -0
  7. package/Runtime/Core/Helper/DxMessagingRuntime.cs.meta +3 -0
  8. package/Runtime/Core/Helper/MessageCache.cs +105 -0
  9. package/Runtime/Core/Helper/MessageCache.cs.meta +3 -0
  10. package/Runtime/Core/Helper/MessageHelperIndexer.cs +9 -0
  11. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +3 -0
  12. package/Runtime/Core/Helper.meta +3 -0
  13. package/Runtime/Core/IMessage.cs +0 -13
  14. package/Runtime/Core/InstanceId.cs +0 -2
  15. package/Runtime/Core/MessageBus/IMessageBus.cs +10 -3
  16. package/Runtime/Core/MessageBus/MessageBus.cs +400 -98
  17. package/Runtime/Core/MessageHandler.cs +51 -119
  18. package/Runtime/Core/Messages/DxReflexiveMessage.cs +132 -0
  19. package/Runtime/Core/Messages/DxReflexiveMessage.cs.meta +3 -0
  20. package/Runtime/Core/MessagingDebug.cs +1 -1
  21. package/Runtime/Unity/MessageAwareComponent.cs +34 -13
  22. package/Runtime/Unity/Messages/GlobalStringMessage.cs +15 -0
  23. package/Runtime/Unity/Messages/GlobalStringMessage.cs.meta +3 -0
  24. package/Runtime/Unity/Messages/StringMessage.cs +15 -0
  25. package/Runtime/Unity/Messages/StringMessage.cs.meta +3 -0
  26. package/Runtime/Unity/Messages.meta +3 -0
  27. package/Runtime/WallstopStudios.DxMessaging.asmdef +1 -1
  28. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +255 -394
  29. package/Tests/Runtime/Benchmarks/PerformanceTests.cs +141 -1
  30. package/Tests/Runtime/Core/MessagingTestBase.cs +3 -11
  31. package/Tests/Runtime/Scripts/Components/SimpleMessageAwareComponent.cs +12 -0
  32. package/package.json +1 -1
@@ -2,9 +2,16 @@
2
2
  {
3
3
  using System;
4
4
  using System.Collections.Generic;
5
+ using System.Linq.Expressions;
5
6
  using System.Reflection;
7
+ using System.Runtime.CompilerServices;
8
+ using Extensions;
9
+ using Helper;
6
10
  using Messages;
7
11
  using static IMessageBus;
12
+ #if UNITY_2017_1_OR_NEWER
13
+ using UnityEngine;
14
+ #endif
8
15
 
9
16
  /// <summary>
10
17
  /// Instanced MessageBus for use cases where you want distinct islands of MessageBuses.
@@ -34,13 +41,15 @@
34
41
  int count = 0;
35
42
  foreach (var entry in _targetedSinks)
36
43
  {
37
- count += entry.Value.Count;
44
+ count += entry.Count;
38
45
  }
39
46
 
40
47
  return count;
41
48
  }
42
49
  }
43
50
 
51
+ public int RegisteredGlobalSequentialIndex { get; } = GenerateNewGlobalSequentialIndex();
52
+
44
53
  public int RegisteredBroadcast
45
54
  {
46
55
  get
@@ -48,7 +57,7 @@
48
57
  int count = 0;
49
58
  foreach (var entry in _broadcastSinks)
50
59
  {
51
- count += entry.Value.Count;
60
+ count += entry.Count;
52
61
  }
53
62
 
54
63
  return count;
@@ -62,7 +71,7 @@
62
71
  int count = 0;
63
72
  foreach (var entry in _sinks)
64
73
  {
65
- count += entry.Value.handlers.Count;
74
+ count += entry.handlers.Count;
66
75
  }
67
76
 
68
77
  return count;
@@ -73,9 +82,12 @@
73
82
 
74
83
  // For use with re-broadcasting to generic methods
75
84
  private static readonly object[] ReflectionMethodArgumentsCache = new object[2];
85
+ private static readonly List<Expression> ArgumentExpressionsCache = new();
76
86
 
77
87
  private const BindingFlags ReflectionHelperBindingFlags =
78
88
  BindingFlags.Static | BindingFlags.NonPublic;
89
+ private const BindingFlags ReflexiveMethodBindingFlags =
90
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
79
91
 
80
92
  private delegate void FastUntargetedBroadcast<T>(ref T message)
81
93
  where T : IUntargetedMessage;
@@ -86,44 +98,54 @@
86
98
 
87
99
  public RegistrationLog Log => _log;
88
100
 
89
- private readonly Dictionary<Type, HandlerCache<int, HandlerCache>> _sinks = new();
90
- private readonly Dictionary<
91
- Type,
101
+ private readonly MessageCache<HandlerCache<int, HandlerCache>> _sinks = new();
102
+ private readonly MessageCache<
92
103
  Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
93
104
  > _targetedSinks = new();
94
- private readonly Dictionary<
95
- Type,
105
+ private readonly MessageCache<
96
106
  Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
97
107
  > _broadcastSinks = new();
98
- private readonly Dictionary<Type, HandlerCache<int, HandlerCache>> _postProcessingSinks =
99
- new();
100
- private readonly Dictionary<
101
- Type,
108
+ private readonly MessageCache<HandlerCache<int, HandlerCache>> _postProcessingSinks = new();
109
+ private readonly MessageCache<
102
110
  Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
103
111
  > _postProcessingTargetedSinks = new();
104
- private readonly Dictionary<
105
- Type,
112
+ private readonly MessageCache<
106
113
  Dictionary<InstanceId, HandlerCache<int, HandlerCache>>
107
114
  > _postProcessingBroadcastSinks = new();
108
- private readonly Dictionary<
109
- Type,
115
+ private readonly MessageCache<
110
116
  HandlerCache<int, HandlerCache>
111
117
  > _postProcessingTargetedWithoutTargetingSinks = new();
112
- private readonly Dictionary<
113
- Type,
118
+ private readonly MessageCache<
114
119
  HandlerCache<int, HandlerCache>
115
120
  > _postProcessingBroadcastWithoutSourceSinks = new();
116
121
  private readonly HandlerCache _globalSinks = new();
117
- private readonly Dictionary<Type, HandlerCache<int, List<object>>> _interceptsByType =
118
- new();
122
+ private readonly MessageCache<HandlerCache<int, List<object>>> _interceptsByType = new();
119
123
  private readonly Dictionary<object, Dictionary<int, int>> _uniqueInterceptorsAndPriorities =
120
124
  new();
121
125
 
122
126
  private readonly Dictionary<Type, object> _broadcastMethodsByType = new();
123
127
  private readonly Stack<List<object>> _innerInterceptorsStack = new();
124
128
 
129
+ private readonly Dictionary<
130
+ Type,
131
+ Dictionary<MethodSignatureKey, Action<MonoBehaviour, object[]>>
132
+ > _methodCache = new();
133
+
134
+ #if UNITY_2017_1_OR_NEWER
135
+ private readonly HashSet<MonoBehaviour> _recipientCache = new();
136
+ private readonly List<MonoBehaviour> _componentCache = new();
137
+ #endif
138
+
125
139
  private readonly RegistrationLog _log = new();
126
140
 
141
+ static MessageBus()
142
+ {
143
+ if (!DxMessagingRuntime.Initialized)
144
+ {
145
+ DxMessagingRuntime.Initialize();
146
+ }
147
+ }
148
+
127
149
  public Action RegisterUntargeted<T>(MessageHandler messageHandler, int priority = 0)
128
150
  where T : IUntargetedMessage
129
151
  {
@@ -352,16 +374,8 @@
352
374
  where T : IMessage
353
375
  {
354
376
  Type type = typeof(T);
355
- if (
356
- !_interceptsByType.TryGetValue(
357
- type,
358
- out HandlerCache<int, List<object>> prioritizedInterceptors
359
- )
360
- )
361
- {
362
- prioritizedInterceptors = new HandlerCache<int, List<object>>();
363
- _interceptsByType[type] = prioritizedInterceptors;
364
- }
377
+ HandlerCache<int, List<object>> prioritizedInterceptors =
378
+ _interceptsByType.GetOrAdd<T>();
365
379
 
366
380
  if (
367
381
  !prioritizedInterceptors.handlers.TryGetValue(
@@ -446,7 +460,7 @@
446
460
  bool complete = false;
447
461
  if (removed)
448
462
  {
449
- if (_interceptsByType.TryGetValue(type, out prioritizedInterceptors))
463
+ if (_interceptsByType.TryGetValue<T>(out prioritizedInterceptors))
450
464
  {
451
465
  prioritizedInterceptors.version++;
452
466
  if (
@@ -499,8 +513,7 @@
499
513
  public void UntargetedBroadcast<TMessage>(ref TMessage typedMessage)
500
514
  where TMessage : IUntargetedMessage
501
515
  {
502
- Type type = typeof(TMessage);
503
- if (!RunUntargetedInterceptors(type, ref typedMessage))
516
+ if (!RunUntargetedInterceptors(ref typedMessage))
504
517
  {
505
518
  return;
506
519
  }
@@ -511,11 +524,10 @@
511
524
  BroadcastGlobalUntargeted(ref untargetedMessage);
512
525
  }
513
526
 
514
- bool foundAnyHandlers = InternalUntargetedBroadcast(ref typedMessage, type);
527
+ bool foundAnyHandlers = InternalUntargetedBroadcast(ref typedMessage);
515
528
 
516
529
  if (
517
- _postProcessingSinks.TryGetValue(
518
- type,
530
+ _postProcessingSinks.TryGetValue<TMessage>(
519
531
  out HandlerCache<int, HandlerCache> sortedHandlers
520
532
  )
521
533
  && 0 < sortedHandlers.handlers.Count
@@ -697,8 +709,7 @@
697
709
  public void TargetedBroadcast<TMessage>(ref InstanceId target, ref TMessage typedMessage)
698
710
  where TMessage : ITargetedMessage
699
711
  {
700
- Type type = typeof(TMessage);
701
- if (!RunTargetedInterceptors(type, ref typedMessage, ref target))
712
+ if (!RunTargetedInterceptors(ref typedMessage, ref target))
702
713
  {
703
714
  return;
704
715
  }
@@ -710,15 +721,191 @@
710
721
  }
711
722
 
712
723
  bool foundAnyHandlers = false;
724
+ Dictionary<InstanceId, HandlerCache<int, HandlerCache>> targetedHandlers;
725
+ HandlerCache<int, HandlerCache> sortedHandlers;
726
+
727
+ if (typeof(TMessage) == typeof(DxReflexiveMessage))
728
+ {
729
+ #if UNITY_2017_1_OR_NEWER
730
+ ref DxReflexiveMessage reflexiveMessage = ref Unsafe.As<
731
+ TMessage,
732
+ DxReflexiveMessage
733
+ >(ref typedMessage);
734
+
735
+ GameObject go;
736
+ bool found;
737
+ UnityEngine.Object targetObject = target.Object;
738
+ switch (targetObject)
739
+ {
740
+ case GameObject gameObject:
741
+ {
742
+ found = true;
743
+ go = gameObject;
744
+ break;
745
+ }
746
+ case Component component:
747
+ {
748
+ found = true;
749
+ go = component.gameObject;
750
+ break;
751
+ }
752
+ default:
753
+ {
754
+ go = null;
755
+ found = false;
756
+ break;
757
+ }
758
+ }
759
+
760
+ if (found)
761
+ {
762
+ _recipientCache.Clear();
763
+ bool sentInADirection = false;
764
+ if (reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Upwards))
765
+ {
766
+ sentInADirection = true;
767
+ if (
768
+ !reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Downwards)
769
+ && !reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Flat)
770
+ )
771
+ {
772
+ switch (reflexiveMessage.parameters.Length)
773
+ {
774
+ case 0:
775
+ {
776
+ go.SendMessageUpwards(reflexiveMessage.method);
777
+ break;
778
+ }
779
+ case 1:
780
+ {
781
+ go.SendMessageUpwards(
782
+ reflexiveMessage.method,
783
+ reflexiveMessage.parameters[0]
784
+ );
785
+ break;
786
+ }
787
+ default:
788
+ {
789
+ Component[] parentComponents = go.GetComponentsInParent(
790
+ typeof(MonoBehaviour),
791
+ true
792
+ );
793
+ foreach (Component component in parentComponents)
794
+ {
795
+ if (component is MonoBehaviour script)
796
+ {
797
+ SendMessage(script, ref reflexiveMessage);
798
+ }
799
+ }
800
+
801
+ break;
802
+ }
803
+ }
804
+ }
805
+ else
806
+ {
807
+ Component[] parentComponents = go.GetComponentsInParent(
808
+ typeof(MonoBehaviour)
809
+ );
810
+ foreach (Component component in parentComponents)
811
+ {
812
+ if (component is MonoBehaviour script)
813
+ {
814
+ SendMessage(script, ref reflexiveMessage);
815
+ }
816
+ }
817
+ }
818
+ }
819
+ if (reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Downwards))
820
+ {
821
+ sentInADirection = true;
822
+ if (
823
+ !reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Upwards)
824
+ && !reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Flat)
825
+ )
826
+ {
827
+ switch (reflexiveMessage.parameters.Length)
828
+ {
829
+ case 0:
830
+ {
831
+ go.BroadcastMessage(reflexiveMessage.method);
832
+ break;
833
+ }
834
+ case 1:
835
+ {
836
+ go.BroadcastMessage(
837
+ reflexiveMessage.method,
838
+ reflexiveMessage.parameters[0]
839
+ );
840
+ break;
841
+ }
842
+ default:
843
+ {
844
+ _componentCache.Clear();
845
+ go.GetComponentsInChildren(true, _componentCache);
846
+ foreach (MonoBehaviour parentComponent in _componentCache)
847
+ {
848
+ SendMessage(parentComponent, ref reflexiveMessage);
849
+ }
850
+
851
+ break;
852
+ }
853
+ }
854
+ }
855
+ else
856
+ {
857
+ _componentCache.Clear();
858
+ go.GetComponentsInChildren(_componentCache);
859
+ foreach (MonoBehaviour parentComponent in _componentCache)
860
+ {
861
+ SendMessage(parentComponent, ref reflexiveMessage);
862
+ }
863
+ }
864
+ }
865
+ else if (
866
+ !sentInADirection
867
+ && reflexiveMessage.sendMode.HasFlagNoAlloc(ReflexiveSendMode.Flat)
868
+ )
869
+ {
870
+ switch (reflexiveMessage.parameters.Length)
871
+ {
872
+ case 0:
873
+ {
874
+ go.SendMessage(reflexiveMessage.method);
875
+ break;
876
+ }
877
+ case 1:
878
+ {
879
+ go.SendMessage(
880
+ reflexiveMessage.method,
881
+ reflexiveMessage.parameters[0]
882
+ );
883
+ break;
884
+ }
885
+ default:
886
+ {
887
+ _componentCache.Clear();
888
+ go.GetComponents(_componentCache);
889
+ foreach (MonoBehaviour component in _componentCache)
890
+ {
891
+ SendMessage(component, ref reflexiveMessage);
892
+ }
893
+ break;
894
+ }
895
+ }
896
+ }
897
+ }
898
+ #else
899
+ MessagingDebug.Log(
900
+ LogLevel.Error,
901
+ "Reflexive messages are not supported in this build."
902
+ );
903
+ #endif
904
+ }
905
+
713
906
  if (
714
- _targetedSinks.TryGetValue(
715
- type,
716
- out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> targetedHandlers
717
- )
718
- && targetedHandlers.TryGetValue(
719
- target,
720
- out HandlerCache<int, HandlerCache> sortedHandlers
721
- )
907
+ _targetedSinks.TryGetValue<TMessage>(out targetedHandlers)
908
+ && targetedHandlers.TryGetValue(target, out sortedHandlers)
722
909
  && 0 < sortedHandlers.handlers.Count
723
910
  )
724
911
  {
@@ -795,10 +982,10 @@
795
982
  }
796
983
  }
797
984
 
798
- _ = InternalTargetedWithoutTargetingBroadcast(ref target, ref typedMessage, type);
985
+ _ = InternalTargetedWithoutTargetingBroadcast(ref target, ref typedMessage);
799
986
 
800
987
  if (
801
- _postProcessingTargetedSinks.TryGetValue(type, out targetedHandlers)
988
+ _postProcessingTargetedSinks.TryGetValue<TMessage>(out targetedHandlers)
802
989
  && targetedHandlers.TryGetValue(target, out sortedHandlers)
803
990
  && 0 < sortedHandlers.handlers.Count
804
991
  )
@@ -952,7 +1139,9 @@
952
1139
  }
953
1140
 
954
1141
  if (
955
- _postProcessingTargetedWithoutTargetingSinks.TryGetValue(type, out sortedHandlers)
1142
+ _postProcessingTargetedWithoutTargetingSinks.TryGetValue<TMessage>(
1143
+ out sortedHandlers
1144
+ )
956
1145
  && 0 < sortedHandlers.handlers.Count
957
1146
  )
958
1147
  {
@@ -1435,8 +1624,7 @@
1435
1624
  public void SourcedBroadcast<TMessage>(ref InstanceId source, ref TMessage typedMessage)
1436
1625
  where TMessage : IBroadcastMessage
1437
1626
  {
1438
- Type type = typeof(TMessage);
1439
- if (!RunBroadcastInterceptors(type, ref typedMessage, ref source))
1627
+ if (!RunBroadcastInterceptors(ref typedMessage, ref source))
1440
1628
  {
1441
1629
  return;
1442
1630
  }
@@ -1449,8 +1637,7 @@
1449
1637
 
1450
1638
  bool foundAnyHandlers = false;
1451
1639
  if (
1452
- _broadcastSinks.TryGetValue(
1453
- type,
1640
+ _broadcastSinks.TryGetValue<TMessage>(
1454
1641
  out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> broadcastHandlers
1455
1642
  )
1456
1643
  && broadcastHandlers.TryGetValue(
@@ -1528,10 +1715,10 @@
1528
1715
  }
1529
1716
  }
1530
1717
 
1531
- _ = InternalBroadcastWithoutSource(ref source, ref typedMessage, type);
1718
+ _ = InternalBroadcastWithoutSource(ref source, ref typedMessage);
1532
1719
 
1533
1720
  if (
1534
- _postProcessingBroadcastSinks.TryGetValue(type, out broadcastHandlers)
1721
+ _postProcessingBroadcastSinks.TryGetValue<TMessage>(out broadcastHandlers)
1535
1722
  && broadcastHandlers.TryGetValue(source, out sortedHandlers)
1536
1723
  && 0 < sortedHandlers.handlers.Count
1537
1724
  )
@@ -1685,7 +1872,7 @@
1685
1872
  }
1686
1873
 
1687
1874
  if (
1688
- _postProcessingBroadcastWithoutSourceSinks.TryGetValue(type, out sortedHandlers)
1875
+ _postProcessingBroadcastWithoutSourceSinks.TryGetValue<TMessage>(out sortedHandlers)
1689
1876
  && 0 < sortedHandlers.handlers.Count
1690
1877
  )
1691
1878
  {
@@ -2138,15 +2325,14 @@
2138
2325
  }
2139
2326
  }
2140
2327
 
2141
- private bool TryGetInterceptorCaches(
2142
- Type type,
2328
+ private bool TryGetInterceptorCaches<TMessage>(
2143
2329
  out List<KeyValuePair<int, List<object>>> interceptorStack,
2144
2330
  out List<object> interceptorObjects
2145
2331
  )
2332
+ where TMessage : IMessage
2146
2333
  {
2147
2334
  if (
2148
- !_interceptsByType.TryGetValue(
2149
- type,
2335
+ !_interceptsByType.TryGetValue<TMessage>(
2150
2336
  out HandlerCache<int, List<object>> interceptors
2151
2337
  )
2152
2338
  || interceptors.handlers.Count == 0
@@ -2179,12 +2365,11 @@
2179
2365
  return true;
2180
2366
  }
2181
2367
 
2182
- private bool RunUntargetedInterceptors<T>(Type type, ref T message)
2368
+ private bool RunUntargetedInterceptors<T>(ref T message)
2183
2369
  where T : IUntargetedMessage
2184
2370
  {
2185
2371
  if (
2186
- !TryGetInterceptorCaches(
2187
- type,
2372
+ !TryGetInterceptorCaches<T>(
2188
2373
  out List<KeyValuePair<int, List<object>>> interceptorStack,
2189
2374
  out List<object> interceptorObjects
2190
2375
  )
@@ -2225,12 +2410,11 @@
2225
2410
  return true;
2226
2411
  }
2227
2412
 
2228
- private bool RunTargetedInterceptors<T>(Type type, ref T message, ref InstanceId target)
2413
+ private bool RunTargetedInterceptors<T>(ref T message, ref InstanceId target)
2229
2414
  where T : ITargetedMessage
2230
2415
  {
2231
2416
  if (
2232
- !TryGetInterceptorCaches(
2233
- type,
2417
+ !TryGetInterceptorCaches<T>(
2234
2418
  out List<KeyValuePair<int, List<object>>> interceptorStack,
2235
2419
  out List<object> interceptorObjects
2236
2420
  )
@@ -2271,12 +2455,11 @@
2271
2455
  return true;
2272
2456
  }
2273
2457
 
2274
- private bool RunBroadcastInterceptors<T>(Type type, ref T message, ref InstanceId source)
2458
+ private bool RunBroadcastInterceptors<T>(ref T message, ref InstanceId source)
2275
2459
  where T : IBroadcastMessage
2276
2460
  {
2277
2461
  if (
2278
- !TryGetInterceptorCaches(
2279
- type,
2462
+ !TryGetInterceptorCaches<T>(
2280
2463
  out List<KeyValuePair<int, List<object>>> interceptorStack,
2281
2464
  out List<object> interceptorObjects
2282
2465
  )
@@ -2317,11 +2500,11 @@
2317
2500
  return true;
2318
2501
  }
2319
2502
 
2320
- private bool InternalUntargetedBroadcast<TMessage>(ref TMessage message, Type type)
2503
+ private bool InternalUntargetedBroadcast<TMessage>(ref TMessage message)
2321
2504
  where TMessage : IMessage
2322
2505
  {
2323
2506
  if (
2324
- !_sinks.TryGetValue(type, out HandlerCache<int, HandlerCache> sortedHandlers)
2507
+ !_sinks.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
2325
2508
  || sortedHandlers.handlers.Count == 0
2326
2509
  )
2327
2510
  {
@@ -2452,13 +2635,12 @@
2452
2635
 
2453
2636
  private bool InternalTargetedWithoutTargetingBroadcast<TMessage>(
2454
2637
  ref InstanceId target,
2455
- ref TMessage message,
2456
- Type type
2638
+ ref TMessage message
2457
2639
  )
2458
2640
  where TMessage : ITargetedMessage
2459
2641
  {
2460
2642
  if (
2461
- !_sinks.TryGetValue(type, out HandlerCache<int, HandlerCache> sortedHandlers)
2643
+ !_sinks.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
2462
2644
  || sortedHandlers.handlers.Count == 0
2463
2645
  )
2464
2646
  {
@@ -2607,13 +2789,12 @@
2607
2789
 
2608
2790
  private bool InternalBroadcastWithoutSource<TMessage>(
2609
2791
  ref InstanceId source,
2610
- ref TMessage message,
2611
- Type type
2792
+ ref TMessage message
2612
2793
  )
2613
2794
  where TMessage : IBroadcastMessage
2614
2795
  {
2615
2796
  if (
2616
- !_sinks.TryGetValue(type, out HandlerCache<int, HandlerCache> sortedHandlers)
2797
+ !_sinks.TryGetValue<TMessage>(out HandlerCache<int, HandlerCache> sortedHandlers)
2617
2798
  || sortedHandlers.handlers.Count == 0
2618
2799
  )
2619
2800
  {
@@ -2841,7 +3022,7 @@
2841
3022
 
2842
3023
  private Action InternalRegisterUntargeted<T>(
2843
3024
  MessageHandler messageHandler,
2844
- Dictionary<Type, HandlerCache<int, HandlerCache>> sinks,
3025
+ MessageCache<HandlerCache<int, HandlerCache>> sinks,
2845
3026
  RegistrationMethod registrationMethod,
2846
3027
  int priority
2847
3028
  )
@@ -2855,11 +3036,7 @@
2855
3036
  InstanceId handlerOwnerId = messageHandler.owner;
2856
3037
  Type type = typeof(T);
2857
3038
 
2858
- if (!sinks.TryGetValue(type, out HandlerCache<int, HandlerCache> handlers))
2859
- {
2860
- handlers = new HandlerCache<int, HandlerCache>();
2861
- sinks[type] = handlers;
2862
- }
3039
+ HandlerCache<int, HandlerCache> handlers = sinks.GetOrAdd<T>();
2863
3040
 
2864
3041
  if (!handlers.handlers.TryGetValue(priority, out HandlerCache cache))
2865
3042
  {
@@ -2894,7 +3071,7 @@
2894
3071
  )
2895
3072
  );
2896
3073
  if (
2897
- !sinks.TryGetValue(type, out handlers)
3074
+ !sinks.TryGetValue<T>(out handlers)
2898
3075
  || !handlers.handlers.TryGetValue(priority, out cache)
2899
3076
  || !cache.handlers.TryGetValue(messageHandler, out count)
2900
3077
  )
@@ -2925,7 +3102,7 @@
2925
3102
 
2926
3103
  if (handlers.handlers.Count == 0)
2927
3104
  {
2928
- _ = sinks.Remove(type);
3105
+ sinks.Remove<T>();
2929
3106
  }
2930
3107
 
2931
3108
  if (!complete && MessagingDebug.enabled)
@@ -2948,10 +3125,11 @@
2948
3125
  private Action InternalRegisterWithContext<T>(
2949
3126
  InstanceId context,
2950
3127
  MessageHandler messageHandler,
2951
- Dictionary<Type, Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks,
3128
+ MessageCache<Dictionary<InstanceId, HandlerCache<int, HandlerCache>>> sinks,
2952
3129
  RegistrationMethod registrationMethod,
2953
3130
  int priority
2954
3131
  )
3132
+ where T : IMessage
2955
3133
  {
2956
3134
  if (messageHandler == null)
2957
3135
  {
@@ -2959,16 +3137,8 @@
2959
3137
  }
2960
3138
 
2961
3139
  Type type = typeof(T);
2962
- if (
2963
- !sinks.TryGetValue(
2964
- type,
2965
- out Dictionary<InstanceId, HandlerCache<int, HandlerCache>> broadcastHandlers
2966
- )
2967
- )
2968
- {
2969
- broadcastHandlers = new Dictionary<InstanceId, HandlerCache<int, HandlerCache>>();
2970
- sinks[type] = broadcastHandlers;
2971
- }
3140
+ Dictionary<InstanceId, HandlerCache<int, HandlerCache>> broadcastHandlers =
3141
+ sinks.GetOrAdd<T>();
2972
3142
 
2973
3143
  if (
2974
3144
  !broadcastHandlers.TryGetValue(
@@ -3014,7 +3184,7 @@
3014
3184
  )
3015
3185
  );
3016
3186
  if (
3017
- !sinks.TryGetValue(type, out broadcastHandlers)
3187
+ !sinks.TryGetValue<T>(out broadcastHandlers)
3018
3188
  || !broadcastHandlers.TryGetValue(context, out handlers)
3019
3189
  || !handlers.handlers.TryGetValue(priority, out cache)
3020
3190
  || !cache.handlers.TryGetValue(messageHandler, out count)
@@ -3050,7 +3220,7 @@
3050
3220
 
3051
3221
  if (broadcastHandlers.Count == 0)
3052
3222
  {
3053
- _ = sinks.Remove(type);
3223
+ sinks.Remove<T>();
3054
3224
  }
3055
3225
 
3056
3226
  if (!complete && MessagingDebug.enabled)
@@ -3180,5 +3350,137 @@
3180
3350
  sourcedBroadcast(ref target, ref typedMessage);
3181
3351
  }
3182
3352
  }
3353
+
3354
+ #if UNITY_2017_1_OR_NEWER
3355
+ private static Action<MonoBehaviour, object[]> CompileMethodAction(MethodInfo methodInfo)
3356
+ {
3357
+ ParameterExpression componentParameter = Expression.Parameter(
3358
+ typeof(MonoBehaviour),
3359
+ "targetComponent"
3360
+ );
3361
+ ParameterExpression argsParameter = Expression.Parameter(typeof(object[]), "args");
3362
+ ParameterInfo[] methodParams = methodInfo.GetParameters();
3363
+
3364
+ ArgumentExpressionsCache.Clear();
3365
+ for (int i = 0; i < methodParams.Length; ++i)
3366
+ {
3367
+ Expression indexAccess = Expression.ArrayIndex(
3368
+ argsParameter,
3369
+ Expression.Constant(i)
3370
+ );
3371
+ Expression convertedArg = Expression.Convert(
3372
+ indexAccess,
3373
+ methodParams[i].ParameterType
3374
+ );
3375
+ ArgumentExpressionsCache.Add(convertedArg);
3376
+ }
3377
+
3378
+ // ReSharper disable once AssignNullToNotNullAttribute
3379
+ Expression instanceExpression = methodInfo.IsStatic
3380
+ ? null
3381
+ : Expression.Convert(componentParameter, methodInfo.DeclaringType);
3382
+ MethodCallExpression callExpression = Expression.Call(
3383
+ instanceExpression,
3384
+ methodInfo,
3385
+ ArgumentExpressionsCache
3386
+ );
3387
+ Expression<Action<MonoBehaviour, object[]>> lambda = Expression.Lambda<
3388
+ Action<MonoBehaviour, object[]>
3389
+ >(callExpression, componentParameter, argsParameter);
3390
+
3391
+ return lambda.Compile();
3392
+ }
3393
+ #endif
3394
+
3395
+ private void SendMessage(MonoBehaviour recipient, ref DxReflexiveMessage message)
3396
+ {
3397
+ if (!_recipientCache.Add(recipient))
3398
+ {
3399
+ return;
3400
+ }
3401
+
3402
+ Type componentType = recipient.GetType();
3403
+ if (
3404
+ !_methodCache.TryGetValue(
3405
+ componentType,
3406
+ out Dictionary<MethodSignatureKey, Action<MonoBehaviour, object[]>> methodCache
3407
+ )
3408
+ )
3409
+ {
3410
+ _methodCache[componentType] = methodCache =
3411
+ new Dictionary<MethodSignatureKey, Action<MonoBehaviour, object[]>>();
3412
+ }
3413
+
3414
+ MethodSignatureKey lookupKey = message.signatureKey;
3415
+ if (!methodCache.TryGetValue(lookupKey, out Action<MonoBehaviour, object[]> method))
3416
+ {
3417
+ MethodInfo methodInfo = null;
3418
+ try
3419
+ {
3420
+ methodInfo = componentType.GetMethod(
3421
+ message.method,
3422
+ ReflexiveMethodBindingFlags,
3423
+ null,
3424
+ message.parameterTypes,
3425
+ null
3426
+ );
3427
+ }
3428
+ catch (AmbiguousMatchException)
3429
+ {
3430
+ MethodInfo[] matchingMethods = componentType.GetMethods(
3431
+ ReflexiveMethodBindingFlags
3432
+ );
3433
+ foreach (MethodInfo matchingMethod in matchingMethods)
3434
+ {
3435
+ if (
3436
+ !string.Equals(
3437
+ matchingMethod.Name,
3438
+ message.method,
3439
+ StringComparison.Ordinal
3440
+ )
3441
+ || !ParameterTypesMatch(
3442
+ matchingMethod.GetParameters(),
3443
+ message.parameterTypes
3444
+ )
3445
+ )
3446
+ {
3447
+ continue;
3448
+ }
3449
+
3450
+ methodInfo = matchingMethod;
3451
+ break;
3452
+ }
3453
+ }
3454
+ catch
3455
+ {
3456
+ methodInfo = null;
3457
+ }
3458
+
3459
+ if (methodInfo != null)
3460
+ {
3461
+ method = CompileMethodAction(methodInfo);
3462
+ }
3463
+ methodCache[lookupKey] = method;
3464
+ }
3465
+
3466
+ method?.Invoke(recipient, message.parameters);
3467
+ }
3468
+
3469
+ private static bool ParameterTypesMatch(ParameterInfo[] methodParams, Type[] expectedTypes)
3470
+ {
3471
+ if (methodParams.Length != expectedTypes.Length)
3472
+ {
3473
+ return false;
3474
+ }
3475
+
3476
+ for (int i = 0; i < methodParams.Length; ++i)
3477
+ {
3478
+ if (methodParams[i].ParameterType != expectedTypes[i])
3479
+ {
3480
+ return false;
3481
+ }
3482
+ }
3483
+ return true;
3484
+ }
3183
3485
  }
3184
3486
  }