com.wallstop-studios.dxmessaging 2.1.0 → 2.1.2

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 (140) hide show
  1. package/.github/workflows/dotnet-tests.yml +72 -0
  2. package/.lychee.toml +4 -2
  3. package/AGENTS.md +1 -0
  4. package/Docs/Comparisons.md +4 -4
  5. package/Docs/EmitShorthands.md +2 -2
  6. package/Docs/Helpers.md +90 -75
  7. package/Docs/Install.md +2 -1
  8. package/Docs/Patterns.md +1 -1
  9. package/Docs/Performance.md +13 -11
  10. package/Docs/QuickStart.md +1 -2
  11. package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  12. package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +13 -2
  13. package/Editor/Analyzers/Microsoft.CodeAnalysis.dll +0 -0
  14. package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +11 -0
  15. package/Editor/Analyzers/System.Collections.Immutable.dll +0 -0
  16. package/Editor/Analyzers/System.Collections.Immutable.dll.meta +11 -0
  17. package/Editor/Analyzers/System.Reflection.Metadata.dll +0 -0
  18. package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +13 -2
  19. package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll +0 -0
  20. package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +11 -0
  21. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  22. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +3 -2
  23. package/Editor/AssemblyInfo.cs +3 -0
  24. package/Editor/AssemblyInfo.cs.meta +3 -0
  25. package/Editor/CustomEditors/MessagingComponentEditor.cs +21 -0
  26. package/Editor/SetupCscRsp.cs +133 -53
  27. package/Editor/Testing/MessagingComponentEditorHarness.cs +218 -0
  28. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +3 -0
  29. package/Editor/Testing.meta +3 -0
  30. package/README.md +9 -3
  31. package/Runtime/AssemblyInfo.cs +1 -0
  32. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs +1 -1
  33. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs +1 -1
  34. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +1 -1
  35. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs +1 -1
  36. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs +1 -1
  37. package/Runtime/Core/Diagnostics/MessageEmissionData.cs +26 -11
  38. package/Runtime/Core/Extensions/MessageBusExtensions.cs +2 -2
  39. package/Runtime/Core/Extensions/MessageExtensions.cs +2 -2
  40. package/Runtime/Core/InstanceId.cs +5 -3
  41. package/Runtime/Core/MessageBus/MessageBus.cs +4 -4
  42. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +2 -2
  43. package/Runtime/Core/MessageBus/MessagingRegistration.cs +3 -3
  44. package/Runtime/Core/MessageHandler.cs +34 -2
  45. package/Runtime/Core/MessageRegistrationToken.cs +2 -2
  46. package/Runtime/Core/Messages/IBroadcastMessage.cs +1 -1
  47. package/Runtime/Core/Messages/ITargetedMessage.cs +1 -1
  48. package/Runtime/Core/Messages/IUntargetedMessage.cs +1 -1
  49. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs +2 -0
  50. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs +2 -0
  51. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +2 -0
  52. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +2 -0
  53. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +2 -0
  54. package/Runtime/Unity/MessageAwareComponent.cs +2 -0
  55. package/Runtime/Unity/MessageBusProviderHandle.cs +4 -0
  56. package/Runtime/Unity/MessagingComponent.cs +27 -7
  57. package/Runtime/Unity/MessagingComponentInstaller.cs +2 -0
  58. package/Runtime/Unity/ScriptableMessageBusProvider.cs +2 -0
  59. package/Samples~/DI/Reflex/SampleInstaller.cs +6 -6
  60. package/Samples~/DI/VContainer/SampleLifetimeScope.cs +7 -9
  61. package/Samples~/DI/Zenject/SampleInstaller.cs +6 -8
  62. package/SourceGenerators/Directory.Build.props +9 -0
  63. package/{package-lock.json.meta → SourceGenerators/Directory.Build.props.meta} +2 -2
  64. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +19 -24
  65. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +87 -27
  66. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +24 -4
  67. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DocsSnippetCompilationTests.cs +193 -0
  68. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DocsSnippetCompilationTests.cs.meta +11 -0
  69. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DxAutoConstructorGeneratorDiagnosticsTests.cs +69 -0
  70. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DxAutoConstructorGeneratorDiagnosticsTests.cs.meta +11 -0
  71. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DxMessageIdGeneratorDiagnosticsTests.cs +66 -0
  72. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/DxMessageIdGeneratorDiagnosticsTests.cs.meta +11 -0
  73. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/GeneratorTestUtilities.cs +155 -0
  74. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/GeneratorTestUtilities.cs.meta +11 -0
  75. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj +20 -0
  76. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests/WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj.meta +7 -0
  77. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.Tests.meta +8 -0
  78. package/Tests/Editor/MessagingComponentEditorHarnessTests.cs +243 -0
  79. package/Tests/Editor/MessagingComponentEditorHarnessTests.cs.meta +3 -0
  80. package/Tests/Editor/MessagingComponentSerializationTests.cs +129 -0
  81. package/Tests/Editor/MessagingComponentSerializationTests.cs.meta +3 -0
  82. package/Tests/Editor/WallstopStudios.DxMessaging.Tests.Editor.asmdef +19 -0
  83. package/Tests/Editor/WallstopStudios.DxMessaging.Tests.Editor.asmdef.meta +3 -0
  84. package/Tests/Editor.meta +3 -0
  85. package/Tests/Runtime/Benchmarks/BenchmarkSession.cs +3 -0
  86. package/Tests/Runtime/Benchmarks/BenchmarkTestBase.cs +3 -0
  87. package/Tests/Runtime/Benchmarks/ComparisonPerformanceTests.cs +3 -0
  88. package/Tests/Runtime/Benchmarks/PerformanceTests.cs +137 -0
  89. package/Tests/Runtime/Core/AlternateBusTests.cs +3 -0
  90. package/Tests/Runtime/Core/BroadcastTests.cs +3 -0
  91. package/Tests/Runtime/Core/CyclicBufferTests.cs +3 -0
  92. package/Tests/Runtime/Core/DefaultBusFallbackTests.cs +5 -2
  93. package/Tests/Runtime/Core/DiagnosticsTests.cs +3 -0
  94. package/Tests/Runtime/Core/EdgeCaseTests.cs +3 -0
  95. package/Tests/Runtime/Core/EnablementTests.cs +3 -0
  96. package/Tests/Runtime/Core/Extensions/MessageExtensionsProviderTests.cs +2 -2
  97. package/Tests/Runtime/Core/GenericMessageTests.cs +3 -0
  98. package/Tests/Runtime/Core/GlobalAcceptAllTests.cs +3 -0
  99. package/Tests/Runtime/Core/InterceptorCancellationTests.cs +3 -0
  100. package/Tests/Runtime/Core/LifecycleTests.cs +3 -0
  101. package/Tests/Runtime/Core/MessageEmissionDataTests.cs +70 -0
  102. package/Tests/Runtime/Core/MessageEmissionDataTests.cs.meta +11 -0
  103. package/Tests/Runtime/Core/MessagingComponentLifecycleTests.cs +3 -0
  104. package/Tests/Runtime/Core/MessagingTestBase.cs +3 -0
  105. package/Tests/Runtime/Core/MutationDedupeTests.cs +3 -0
  106. package/Tests/Runtime/Core/MutationDestructionTests.cs +3 -0
  107. package/Tests/Runtime/Core/MutationDuringEmissionTests.cs +3 -0
  108. package/Tests/Runtime/Core/MutationGlobalAddTests.cs +3 -0
  109. package/Tests/Runtime/Core/MutationInterceptorTests.cs +3 -0
  110. package/Tests/Runtime/Core/MutationPostProcessorAcrossHandlersTests.cs +3 -0
  111. package/Tests/Runtime/Core/MutationPostProcessorMoreTests.cs +3 -0
  112. package/Tests/Runtime/Core/MutationPriorityTests.cs +3 -0
  113. package/Tests/Runtime/Core/NominalTests.cs +3 -0
  114. package/Tests/Runtime/Core/OrderingTests.cs +3 -0
  115. package/Tests/Runtime/Core/OverDeregistrationTests.cs +3 -0
  116. package/Tests/Runtime/Core/PostProcessorTests.cs +3 -0
  117. package/Tests/Runtime/Core/ReflexiveErrorTests.cs +3 -0
  118. package/Tests/Runtime/Core/ReflexiveMessageWarningTests.cs +4 -1
  119. package/Tests/Runtime/Core/ReflexiveTests.cs +3 -0
  120. package/Tests/Runtime/Core/RegistrationTests.cs +3 -0
  121. package/Tests/Runtime/Core/StringShorthandTests.cs +3 -0
  122. package/Tests/Runtime/Core/TargetedTests.cs +3 -0
  123. package/Tests/Runtime/Core/TypedShorthandTests.cs +3 -0
  124. package/Tests/Runtime/Core/UntargetedEquivalenceTests.cs +3 -0
  125. package/Tests/Runtime/Core/UntargetedPrefreezeTests.cs +14 -78
  126. package/Tests/Runtime/Core/UntargetedTests.cs +3 -0
  127. package/Tests/Runtime/Integrations/Reflex/ReflexIntegrationTests.cs +4 -1
  128. package/Tests/Runtime/Integrations/VContainer/VContainerIntegrationTests.cs +3 -0
  129. package/Tests/Runtime/Integrations/Zenject/ZenjectIntegrationTests.cs +3 -0
  130. package/Tests/Runtime/Scripts/Components/GenericMessageAwareComponent.cs +3 -0
  131. package/Tests/Runtime/Scripts/Components/ManualListenerComponent.cs +3 -0
  132. package/Tests/Runtime/Scripts/Components/ReflexiveReceiverComponent.cs +3 -0
  133. package/Tests/Runtime/TestUtilities/UnityFixtureBase.cs +3 -0
  134. package/Tests/Runtime/Unity/MessageBusProviderAssetTests.cs +3 -0
  135. package/Tests/Runtime/Unity/MessageBusProviderHandleTests.cs +87 -3
  136. package/Tests/Runtime/Unity/MessagingComponentInstallerSceneTests.cs +109 -0
  137. package/Tests/Runtime/Unity/MessagingComponentInstallerSceneTests.cs.meta +11 -0
  138. package/Tests/Runtime/Unity/MessagingComponentProviderIntegrationTests.cs +159 -17
  139. package/Tests/Runtime/WallstopStudios.DxMessaging.Tests.Runtime.csproj +20 -7
  140. package/package.json +1 -1
@@ -6,6 +6,7 @@ namespace DxMessaging.Editor
6
6
  using System.Collections.Generic;
7
7
  using System.IO;
8
8
  using System.Linq;
9
+ using System.Security.Cryptography;
9
10
  using UnityEditor;
10
11
  using UnityEngine;
11
12
  using Object = UnityEngine.Object;
@@ -20,11 +21,11 @@ namespace DxMessaging.Editor
20
21
  )
21
22
  .Replace("\\", "/");
22
23
 
23
- private static readonly string AnalyzerPathRelative =
24
- "Packages/com.wallstop-studios.dxmessaging/Editor/Analyzers/";
25
-
26
- private static readonly string LibraryPathRelative =
27
- "Library/PackageCache/com.wallstop-studios.dxmessaging/Editor/Analyzers/";
24
+ private static readonly string[] AnalyzerDirectories =
25
+ {
26
+ "Packages/com.wallstop-studios.dxmessaging/Editor/Analyzers/",
27
+ "Library/PackageCache/com.wallstop-studios.dxmessaging/Editor/Analyzers/",
28
+ };
28
29
 
29
30
  private static readonly string SourceGeneratorDllName =
30
31
  "WallstopStudios.DxMessaging.SourceGenerators.dll";
@@ -34,13 +35,18 @@ namespace DxMessaging.Editor
34
35
  SourceGeneratorDllName,
35
36
  "Microsoft.CodeAnalysis.dll",
36
37
  "Microsoft.CodeAnalysis.CSharp.dll",
38
+ "System.Text.Encodings.Web.dll",
37
39
  "System.Reflection.Metadata.dll",
38
40
  "System.Runtime.CompilerServices.Unsafe.dll",
39
41
  "System.Collections.Immutable.dll",
42
+ "System.Memory.dll",
43
+ "System.Buffers.dll",
44
+ "System.Threading.Tasks.Extensions.dll",
45
+ "System.Numerics.Vectors.dll",
46
+ "System.Text.Encoding.CodePages.dll",
47
+ "Microsoft.Bcl.AsyncInterfaces.dll",
40
48
  };
41
49
 
42
- private static readonly string LibraryArgument = $"-a:\"{LibraryPathRelative}\"";
43
-
44
50
  private static readonly HashSet<string> DllNames = new(StringComparer.OrdinalIgnoreCase);
45
51
 
46
52
  static SetupCscRsp()
@@ -69,17 +75,14 @@ namespace DxMessaging.Editor
69
75
  }
70
76
  }
71
77
 
72
- string[] dllRelativeDirectories = { LibraryPathRelative, AnalyzerPathRelative };
73
-
74
- bool anyFound = false;
75
- foreach (
76
- string requiredDllName in RequiredDllNames.Where(dllName =>
77
- !DllNames.Contains(dllName)
78
- )
79
- )
78
+ foreach (string requiredDllName in RequiredDllNames)
80
79
  {
81
- bool found = false;
82
- foreach (string relativeDirectory in dllRelativeDirectories)
80
+ if (DllNames.Contains(requiredDllName))
81
+ {
82
+ continue;
83
+ }
84
+
85
+ foreach (string relativeDirectory in AnalyzerDirectories)
83
86
  {
84
87
  try
85
88
  {
@@ -92,52 +95,62 @@ namespace DxMessaging.Editor
92
95
  const string pluginsDirectory =
93
96
  "Assets/Plugins/Editor/WallstopStudios.DxMessaging/";
94
97
  string outputAsset = $"{pluginsDirectory}{requiredDllName}";
95
- string sourceAsset = $"{relativeDirectory}{requiredDllName}";
96
98
  if (!Directory.Exists(pluginsDirectory))
97
99
  {
98
100
  Directory.CreateDirectory(pluginsDirectory);
99
101
  AssetDatabase.Refresh();
100
102
  }
101
- if (!File.Exists(outputAsset))
103
+ bool needsCopy = FilesDiffer(sourceFile, outputAsset);
104
+ if (needsCopy)
102
105
  {
103
- File.Copy(sourceAsset, outputAsset);
106
+ File.Copy(sourceFile, outputAsset, true);
104
107
  AssetDatabase.ImportAsset(outputAsset);
105
- found = true;
106
108
  }
107
- else
109
+
110
+ if (requiredDllName == SourceGeneratorDllName)
111
+ {
112
+ Object loadedDll = AssetDatabase.LoadMainAssetAtPath(outputAsset);
113
+ if (loadedDll != null)
114
+ {
115
+ string[] existingLabels = AssetDatabase.GetLabels(loadedDll);
116
+ if (!existingLabels.Contains("RoslynAnalyzer"))
117
+ {
118
+ List<string> newLabels = existingLabels.ToList();
119
+ newLabels.Add("RoslynAnalyzer");
120
+ AssetDatabase.SetLabels(loadedDll, newLabels.ToArray());
121
+ }
122
+ }
123
+ }
124
+
125
+ if (AssetImporter.GetAtPath(outputAsset) is PluginImporter importer)
108
126
  {
109
- FileInfo sourceInfo = new(sourceAsset);
110
- FileInfo destInfo = new(outputAsset);
127
+ bool importerDirty = false;
111
128
 
112
- if (destInfo.LastWriteTime < sourceInfo.LastWriteTime)
129
+ if (importer.GetCompatibleWithAnyPlatform())
113
130
  {
114
- // Source file is newer, so copy the file (overwrite destination)
115
- File.Copy(sourceAsset, outputAsset, true);
116
- AssetDatabase.ImportAsset(outputAsset);
117
- found = true;
131
+ importer.SetCompatibleWithAnyPlatform(false);
132
+ importerDirty = true;
118
133
  }
119
- else
134
+
135
+ if (importer.GetExcludeFromAnyPlatform("Editor"))
120
136
  {
121
- continue;
137
+ importer.SetExcludeFromAnyPlatform("Editor", false);
138
+ importerDirty = true;
122
139
  }
123
- }
124
140
 
125
- if (requiredDllName == SourceGeneratorDllName)
126
- {
127
- Object loadedDll = AssetDatabase.LoadMainAssetAtPath(outputAsset);
128
- AssetDatabase.SetLabels(loadedDll, new[] { "RoslynAnalyzer" });
129
- }
141
+ if (!importer.GetExcludeFromAnyPlatform("Standalone"))
142
+ {
143
+ importer.SetExcludeFromAnyPlatform("Standalone", true);
144
+ importerDirty = true;
145
+ }
130
146
 
131
- PluginImporter importer =
132
- AssetImporter.GetAtPath(outputAsset) as PluginImporter;
133
- if (importer != null)
134
- {
135
- importer.SetCompatibleWithAnyPlatform(false);
136
- importer.SetExcludeFromAnyPlatform("Editor", false);
137
- importer.SetExcludeFromAnyPlatform("Standalone", false);
138
- importer.SaveAndReimport();
147
+ if (importerDirty || needsCopy)
148
+ {
149
+ importer.SaveAndReimport();
150
+ }
139
151
  }
140
152
 
153
+ DllNames.Add(requiredDllName);
141
154
  break;
142
155
  }
143
156
  catch (Exception e)
@@ -147,16 +160,36 @@ namespace DxMessaging.Editor
147
160
  );
148
161
  }
149
162
  }
150
-
151
- anyFound |= found;
152
163
  }
153
164
 
154
- if (anyFound)
165
+ if (DllNames.Count > 0)
155
166
  {
156
167
  AssetDatabase.Refresh();
157
168
  }
158
169
  }
159
170
 
171
+ private static bool FilesDiffer(string sourcePath, string destinationPath)
172
+ {
173
+ if (!File.Exists(destinationPath))
174
+ {
175
+ return true;
176
+ }
177
+
178
+ FileInfo sourceInfo = new(sourcePath);
179
+ FileInfo destinationInfo = new(destinationPath);
180
+ if (sourceInfo.Length != destinationInfo.Length)
181
+ {
182
+ return true;
183
+ }
184
+
185
+ using FileStream sourceStream = File.OpenRead(sourcePath);
186
+ using FileStream destinationStream = File.OpenRead(destinationPath);
187
+ using SHA256 sha256 = SHA256.Create();
188
+ byte[] sourceHash = sha256.ComputeHash(sourceStream);
189
+ byte[] destinationHash = sha256.ComputeHash(destinationStream);
190
+ return !sourceHash.AsSpan().SequenceEqual(destinationHash);
191
+ }
192
+
160
193
  private static void EnsureCscRsp()
161
194
  {
162
195
  try
@@ -168,20 +201,67 @@ namespace DxMessaging.Editor
168
201
  }
169
202
 
170
203
  string rspContent = File.ReadAllText(RspFilePath);
171
- if (rspContent.Contains(LibraryArgument, StringComparison.OrdinalIgnoreCase))
204
+ bool modified = false;
205
+ foreach (string analyzerArgument in GetAnalyzerArguments())
172
206
  {
173
- return;
207
+ if (rspContent.Contains(analyzerArgument, StringComparison.OrdinalIgnoreCase))
208
+ {
209
+ continue;
210
+ }
211
+
212
+ File.AppendAllText(RspFilePath, analyzerArgument + Environment.NewLine);
213
+ modified = true;
174
214
  }
175
215
 
176
- File.AppendAllText(RspFilePath, $"{LibraryArgument}{Environment.NewLine}");
177
- AssetDatabase.ImportAsset("csc.rsp");
178
- Debug.Log("Updated csc.rsp.");
216
+ if (modified)
217
+ {
218
+ AssetDatabase.ImportAsset("csc.rsp");
219
+ Debug.Log("Updated csc.rsp.");
220
+ }
179
221
  }
180
222
  catch (IOException ex)
181
223
  {
182
224
  Debug.LogError($"Failed to modify csc.rsp: {ex}");
183
225
  }
184
226
  }
227
+
228
+ private static IEnumerable<string> GetAnalyzerArguments()
229
+ {
230
+ HashSet<string> yielded = new(StringComparer.OrdinalIgnoreCase);
231
+ string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
232
+
233
+ foreach (string directory in AnalyzerDirectories)
234
+ {
235
+ foreach (string dllName in RequiredDllNames)
236
+ {
237
+ string absoluteDirectory = Path.IsPathRooted(directory)
238
+ ? directory
239
+ : Path.GetFullPath(Path.Combine(projectRoot, directory));
240
+
241
+ string absoluteAnalyzerPath = Path.Combine(absoluteDirectory, dllName);
242
+ if (!File.Exists(absoluteAnalyzerPath))
243
+ {
244
+ continue;
245
+ }
246
+
247
+ string projectRelativePath = FileUtil.GetProjectRelativePath(
248
+ absoluteAnalyzerPath
249
+ );
250
+ if (string.IsNullOrEmpty(projectRelativePath))
251
+ {
252
+ continue;
253
+ }
254
+
255
+ string normalizedRelativePath = projectRelativePath.Replace("\\", "/");
256
+ if (!yielded.Add(normalizedRelativePath))
257
+ {
258
+ continue;
259
+ }
260
+
261
+ yield return $"-a:\"{normalizedRelativePath}\"";
262
+ }
263
+ }
264
+ }
185
265
  }
186
266
  }
187
267
  #endif
@@ -0,0 +1,218 @@
1
+ #if UNITY_EDITOR
2
+ namespace DxMessaging.Editor.Testing
3
+ {
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using System.Linq;
7
+ using Core;
8
+ using Core.Diagnostics;
9
+ using Core.MessageBus;
10
+ using Unity;
11
+ using UnityEngine;
12
+
13
+ /// <summary>
14
+ /// Captures inspector-oriented diagnostics for <see cref="MessagingComponent"/> instances without relying on GUI APIs.
15
+ /// Intended for automated editor tests that need to validate inspector state transitions.
16
+ /// </summary>
17
+ internal static class MessagingComponentEditorHarness
18
+ {
19
+ private static readonly MessageEmissionData[] EmptyEmissions =
20
+ Array.Empty<MessageEmissionData>();
21
+
22
+ internal static MessagingComponentInspectorState Capture(MessagingComponent component)
23
+ {
24
+ if (component == null)
25
+ {
26
+ throw new ArgumentNullException(nameof(component));
27
+ }
28
+
29
+ bool globalDiagnosticsEnabled = false;
30
+ IReadOnlyList<MessageEmissionData> globalHistory = EmptyEmissions;
31
+
32
+ if (MessageHandler.MessageBus is MessageBus concreteBus)
33
+ {
34
+ globalDiagnosticsEnabled = concreteBus.DiagnosticsMode;
35
+ if (globalDiagnosticsEnabled && concreteBus._emissionBuffer.Count > 0)
36
+ {
37
+ globalHistory = concreteBus._emissionBuffer.ToArray();
38
+ }
39
+ }
40
+
41
+ List<ListenerDiagnosticsView> listenerViews = component
42
+ ._registeredListeners.OrderBy(pair => pair.Key.GetInstanceID())
43
+ .Select(pair => CreateListenerView(pair.Key, pair.Value))
44
+ .ToList();
45
+
46
+ ProviderDiagnosticsView providerDiagnostics = CreateProviderDiagnostics(component);
47
+
48
+ return new MessagingComponentInspectorState(
49
+ globalDiagnosticsEnabled,
50
+ globalHistory,
51
+ listenerViews,
52
+ providerDiagnostics
53
+ );
54
+ }
55
+
56
+ private static ListenerDiagnosticsView CreateListenerView(
57
+ MonoBehaviour listener,
58
+ MessageRegistrationToken token
59
+ )
60
+ {
61
+ MessageRegistrationView[] registrations = token
62
+ ._metadata.OrderBy(pair => pair.Key)
63
+ .Select(pair => new MessageRegistrationView(
64
+ pair.Key,
65
+ pair.Value,
66
+ token._callCounts.TryGetValue(pair.Key, out int callCount) ? callCount : 0
67
+ ))
68
+ .ToArray();
69
+
70
+ IReadOnlyList<MessageEmissionData> emissionHistory =
71
+ token._emissionBuffer.Count > 0 ? token._emissionBuffer.ToArray() : EmptyEmissions;
72
+
73
+ return new ListenerDiagnosticsView(
74
+ listener,
75
+ token.DiagnosticMode,
76
+ token.Enabled,
77
+ registrations,
78
+ emissionHistory
79
+ );
80
+ }
81
+
82
+ internal static ProviderDiagnosticsView CreateProviderDiagnostics(
83
+ MessagingComponent component
84
+ )
85
+ {
86
+ bool autoConfigure = component.AutoConfigureSerializedProviderOnAwake;
87
+ bool hasSerializedProvider =
88
+ component.SerializedProviderAsset != null || component.HasSerializedProvider;
89
+ bool hasRuntimeProvider = component.HasRuntimeProvider;
90
+ bool hasMessageBusOverride = component.HasMessageBusOverride;
91
+ bool serializedProviderMissingWarning = autoConfigure && !hasSerializedProvider;
92
+
93
+ bool serializedProviderNullBusWarning = false;
94
+ if (hasSerializedProvider)
95
+ {
96
+ IMessageBus resolvedBus = component.SerializedProviderHandle.ResolveBus();
97
+ serializedProviderNullBusWarning = resolvedBus == null;
98
+ }
99
+
100
+ return new ProviderDiagnosticsView(
101
+ autoConfigure,
102
+ hasSerializedProvider,
103
+ hasRuntimeProvider,
104
+ hasMessageBusOverride,
105
+ serializedProviderMissingWarning,
106
+ serializedProviderNullBusWarning
107
+ );
108
+ }
109
+ }
110
+
111
+ internal sealed class MessagingComponentInspectorState
112
+ {
113
+ internal MessagingComponentInspectorState(
114
+ bool globalDiagnosticsEnabled,
115
+ IReadOnlyList<MessageEmissionData> globalEmissionHistory,
116
+ IReadOnlyList<ListenerDiagnosticsView> listeners,
117
+ ProviderDiagnosticsView providerDiagnostics
118
+ )
119
+ {
120
+ GlobalDiagnosticsEnabled = globalDiagnosticsEnabled;
121
+ GlobalEmissionHistory =
122
+ globalEmissionHistory
123
+ ?? throw new ArgumentNullException(nameof(globalEmissionHistory));
124
+ Listeners = listeners ?? throw new ArgumentNullException(nameof(listeners));
125
+ ProviderDiagnostics = providerDiagnostics;
126
+ }
127
+
128
+ internal bool GlobalDiagnosticsEnabled { get; }
129
+
130
+ internal IReadOnlyList<MessageEmissionData> GlobalEmissionHistory { get; }
131
+
132
+ internal IReadOnlyList<ListenerDiagnosticsView> Listeners { get; }
133
+
134
+ internal ProviderDiagnosticsView ProviderDiagnostics { get; }
135
+ }
136
+
137
+ internal sealed class ListenerDiagnosticsView
138
+ {
139
+ internal ListenerDiagnosticsView(
140
+ MonoBehaviour listener,
141
+ bool diagnosticsEnabled,
142
+ bool tokenEnabled,
143
+ IReadOnlyList<MessageRegistrationView> registrations,
144
+ IReadOnlyList<MessageEmissionData> emissionHistory
145
+ )
146
+ {
147
+ Listener = listener;
148
+ DiagnosticsEnabled = diagnosticsEnabled;
149
+ TokenEnabled = tokenEnabled;
150
+ Registrations = registrations ?? throw new ArgumentNullException(nameof(registrations));
151
+ EmissionHistory =
152
+ emissionHistory ?? throw new ArgumentNullException(nameof(emissionHistory));
153
+ }
154
+
155
+ internal MonoBehaviour Listener { get; }
156
+
157
+ internal bool DiagnosticsEnabled { get; }
158
+
159
+ internal bool TokenEnabled { get; }
160
+
161
+ internal IReadOnlyList<MessageRegistrationView> Registrations { get; }
162
+
163
+ internal IReadOnlyList<MessageEmissionData> EmissionHistory { get; }
164
+ }
165
+
166
+ internal readonly struct MessageRegistrationView
167
+ {
168
+ internal MessageRegistrationView(
169
+ MessageRegistrationHandle handle,
170
+ MessageRegistrationMetadata metadata,
171
+ int callCount
172
+ )
173
+ {
174
+ Handle = handle;
175
+ Metadata = metadata;
176
+ CallCount = callCount;
177
+ }
178
+
179
+ internal MessageRegistrationHandle Handle { get; }
180
+
181
+ internal MessageRegistrationMetadata Metadata { get; }
182
+
183
+ internal int CallCount { get; }
184
+ }
185
+
186
+ internal readonly struct ProviderDiagnosticsView
187
+ {
188
+ internal ProviderDiagnosticsView(
189
+ bool autoConfigureSerializedProviderOnAwake,
190
+ bool hasSerializedProvider,
191
+ bool hasRuntimeProvider,
192
+ bool hasMessageBusOverride,
193
+ bool serializedProviderMissingWarning,
194
+ bool serializedProviderNullBusWarning
195
+ )
196
+ {
197
+ AutoConfigureSerializedProviderOnAwake = autoConfigureSerializedProviderOnAwake;
198
+ HasSerializedProvider = hasSerializedProvider;
199
+ HasRuntimeProvider = hasRuntimeProvider;
200
+ HasMessageBusOverride = hasMessageBusOverride;
201
+ SerializedProviderMissingWarning = serializedProviderMissingWarning;
202
+ SerializedProviderNullBusWarning = serializedProviderNullBusWarning;
203
+ }
204
+
205
+ internal bool AutoConfigureSerializedProviderOnAwake { get; }
206
+
207
+ internal bool HasSerializedProvider { get; }
208
+
209
+ internal bool HasRuntimeProvider { get; }
210
+
211
+ internal bool HasMessageBusOverride { get; }
212
+
213
+ internal bool SerializedProviderMissingWarning { get; }
214
+
215
+ internal bool SerializedProviderNullBusWarning { get; }
216
+ }
217
+ }
218
+ #endif
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 2624c9e8eb5243ebb71545774f411c2c
3
+ timeCreated: 1761880591
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: a61780c8ba084b29bb44fdefc9b7fcf2
3
+ timeCreated: 1761880591
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Unity](https://img.shields.io/badge/Unity-2021.3+-black.svg)](https://unity.com/releases/editor)<br/>
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)<br/>
5
- [![Version](https://img.shields.io/badge/version-2.0.0--rc27-green.svg)](package.json)<br/>
5
+ [![Version](https://img.shields.io/npm/v/com.wallstop-studios.dxmessaging.svg)](https://www.npmjs.com/package/com.wallstop-studios.dxmessaging)<br/>
6
6
  [![Performance: OS-Specific Benchmarks](https://img.shields.io/badge/Performance-OS--specific-blueviolet.svg)](Docs/Performance.md)<br/>
7
7
  [![Markdown Link Validity](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-validity.yml/badge.svg)](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-validity.yml)<br/>
8
8
  [![Markdown Link Text Check](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-text-check.yml/badge.svg)](https://github.com/wallstop/DxMessaging/actions/workflows/markdown-link-text-check.yml)
@@ -11,6 +11,8 @@
11
11
 
12
12
  Think of it as **the event system Unity should have built-in** — one that actually scales.
13
13
 
14
+ Need install instructions for Git URLs, scoped registries, or tarballs? Jump to the [Install Guide](Docs/Install.md).
15
+
14
16
  ## Table of Contents
15
17
 
16
18
  - [30-Second Elevator Pitch](#30-second-elevator-pitch)
@@ -62,12 +64,15 @@ Think of it as **the event system Unity should have built-in** — one that actu
62
64
 
63
65
  ### 1. Install
64
66
 
65
- Via Unity Package Manager → Add package from git URL:
67
+ Via Unity Package Manager → Add package from git URL (see the [Install Guide](Docs/Install.md) for scoped registry, tarball, and offline options):
66
68
 
67
- ```text
69
+ ```bash
70
+ # Unity Package Manager > Add package from git URL...
68
71
  https://github.com/wallstop/DxMessaging.git
69
72
  ```
70
73
 
74
+ Prefer OpenUPM, scoped registries, or local tarballs? The [Install Guide](Docs/Install.md) covers every path in detail.
75
+
71
76
  ### 2. Define Your First Message
72
77
 
73
78
  ```csharp
@@ -632,6 +637,7 @@ See [full comparison](Docs/Comparisons.md) for detailed analysis with code examp
632
637
 
633
638
  ### 📖 Reference
634
639
 
640
+ - [Install Guide](Docs/Install.md) — All install options (Git URL, scoped registry, tarball, manual copy)
635
641
  - [Glossary](Docs/Glossary.md) — All terms explained in plain English
636
642
  - [Quick Reference](Docs/QuickReference.md) — Cheat sheet
637
643
  - [API Reference](Docs/Reference.md) — Complete API
@@ -9,3 +9,4 @@ using System.Runtime.CompilerServices;
9
9
  [assembly: InternalsVisibleTo("WallstopStudios.DxMessaging.Tests.Runtime.Reflex")]
10
10
  [assembly: InternalsVisibleTo("WallstopStudios.DxMessaging.Tests.Runtime.VContainer")]
11
11
  [assembly: InternalsVisibleTo("WallstopStudios.DxMessaging.Tests.Runtime.Zenject")]
12
+ [assembly: InternalsVisibleTo("WallstopStudios.DxMessaging.Tests.Editor")]
@@ -14,7 +14,7 @@ namespace DxMessaging.Core.Attributes
14
14
  /// <code>
15
15
  /// [DxMessaging.Core.Attributes.DxUntargetedMessage]
16
16
  /// [DxMessaging.Core.Attributes.DxAutoConstructor]
17
- /// public readonly struct VideoSettingsChanged
17
+ /// public readonly partial struct VideoSettingsChanged
18
18
  /// {
19
19
  /// public readonly int width;
20
20
  /// public readonly int height;
@@ -13,7 +13,7 @@ namespace DxMessaging.Core.Attributes
13
13
  /// <example>
14
14
  /// <code>
15
15
  /// [DxMessaging.Core.Attributes.DxBroadcastMessage]
16
- /// public readonly struct TookDamage
16
+ /// public readonly partial struct TookDamage
17
17
  /// {
18
18
  /// public readonly int amount;
19
19
  /// public TookDamage(int amount) { this.amount = amount; }
@@ -12,7 +12,7 @@ namespace DxMessaging.Core.Attributes
12
12
  /// <example>
13
13
  /// <code>
14
14
  /// [DxAutoConstructor]
15
- /// public readonly struct Example
15
+ /// public readonly partial struct Example
16
16
  /// {
17
17
  /// public readonly int required;
18
18
  /// [DxOptionalParameter] public readonly int optional; // defaults to 0
@@ -14,7 +14,7 @@ namespace DxMessaging.Core.Attributes
14
14
  /// <example>
15
15
  /// <code>
16
16
  /// [DxMessaging.Core.Attributes.DxTargetedMessage]
17
- /// public readonly struct HealRequest
17
+ /// public readonly partial struct HealRequest
18
18
  /// {
19
19
  /// public readonly int amount;
20
20
  /// public HealRequest(int amount) { this.amount = amount; }
@@ -14,7 +14,7 @@ namespace DxMessaging.Core.Attributes
14
14
  /// <example>
15
15
  /// <code>
16
16
  /// [DxMessaging.Core.Attributes.DxUntargetedMessage]
17
- /// public readonly struct WorldRegenerated
17
+ /// public readonly partial struct WorldRegenerated
18
18
  /// {
19
19
  /// public readonly int seed;
20
20
  /// public WorldRegenerated(int seed) { this.seed = seed; }
@@ -1,8 +1,11 @@
1
1
  namespace DxMessaging.Core.Diagnostics
2
2
  {
3
3
  using System;
4
- #if UNITY_2017_1_OR_NEWER
4
+ using System.Linq;
5
+ #if UNITY_2021_3_OR_NEWER
5
6
  using UnityEngine;
7
+ #else
8
+ using System.Diagnostics;
6
9
  #endif
7
10
 
8
11
  /// <summary>
@@ -45,7 +48,7 @@ namespace DxMessaging.Core.Diagnostics
45
48
  private static string GetAccurateStackTrace()
46
49
  {
47
50
  string fullStackTrace;
48
- #if UNITY_2017_1_OR_NEWER
51
+ #if UNITY_2021_3_OR_NEWER
49
52
  fullStackTrace = StackTraceUtility.ExtractStackTrace();
50
53
  #else
51
54
  fullStackTrace = new StackTrace(true).ToString();
@@ -57,18 +60,30 @@ namespace DxMessaging.Core.Diagnostics
57
60
 
58
61
  string[] lines = fullStackTrace.Split(NewlineSeparators, StringSplitOptions.None);
59
62
 
60
- int startIndex = 1;
61
- while (
62
- startIndex < lines.Length
63
- && lines[startIndex].Contains("DxMessaging", StringComparison.OrdinalIgnoreCase)
64
- )
63
+ string[] trimmedLines = lines
64
+ .Where(line => !string.IsNullOrWhiteSpace(line) && !IsInternalFrame(line))
65
+ .ToArray();
66
+
67
+ return trimmedLines.Length == 0
68
+ ? string.Empty
69
+ : string.Join(JoinSeparator, trimmedLines);
70
+ }
71
+
72
+ private static bool IsInternalFrame(string line)
73
+ {
74
+ if (string.IsNullOrWhiteSpace(line))
65
75
  {
66
- ++startIndex;
76
+ return false;
67
77
  }
68
78
 
69
- return lines.Length <= startIndex
70
- ? string.Empty
71
- : string.Join(JoinSeparator, lines, startIndex, lines.Length - startIndex);
79
+ if (!line.Contains("DxMessaging.", StringComparison.Ordinal))
80
+ {
81
+ return false;
82
+ }
83
+
84
+ return line.Contains("DxMessaging.Core.", StringComparison.Ordinal)
85
+ || line.Contains("DxMessaging.Unity.", StringComparison.Ordinal)
86
+ || line.Contains("DxMessaging.Editor.", StringComparison.Ordinal);
72
87
  }
73
88
  }
74
89
  }