com.wallstop-studios.dxmessaging 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +211 -2
  2. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +69 -62
  3. package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +3 -3
  4. package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +3 -3
  5. package/Editor/Analyzers/System.Collections.Immutable.dll.meta +3 -3
  6. package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +3 -3
  7. package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +3 -3
  8. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  9. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +15 -2
  10. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  11. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +2 -2
  12. package/Editor/AssemblyInfo.cs.meta +9 -1
  13. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +24 -15
  14. package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +9 -1
  15. package/Editor/DxMessagingEditorIdle.cs +62 -0
  16. package/{Runtime/Core/Internal/TypedDispatchLinkIndex.cs.meta → Editor/DxMessagingEditorIdle.cs.meta} +1 -1
  17. package/Editor/DxMessagingEditorInitializer.cs +112 -15
  18. package/Editor/DxMessagingEditorInitializer.cs.meta +9 -1
  19. package/Editor/DxMessagingEditorLog.cs +32 -0
  20. package/Editor/DxMessagingEditorLog.cs.meta +11 -0
  21. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +135 -12
  22. package/Editor/Settings/DxMessagingSettings.cs +92 -31
  23. package/Editor/Settings/DxMessagingSettings.cs.meta +9 -1
  24. package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +9 -1
  25. package/Editor/SetupCscRsp.cs +339 -173
  26. package/Editor/SetupCscRsp.cs.meta +9 -1
  27. package/Editor/Testing/MessagingComponentEditorHarness.cs +1 -1
  28. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +9 -1
  29. package/README.md +17 -18
  30. package/Runtime/AssemblyInfo.cs.meta +9 -1
  31. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +9 -1
  32. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +9 -1
  33. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
  34. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +9 -1
  35. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +9 -1
  36. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +9 -1
  37. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
  38. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
  39. package/Runtime/Core/DataStructure/CyclicBuffer.cs +44 -26
  40. package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +9 -1
  41. package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +9 -1
  42. package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +9 -1
  43. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +9 -1
  44. package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
  45. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +9 -1
  46. package/Runtime/Core/Extensions/IListExtensions.cs.meta +9 -1
  47. package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
  48. package/Runtime/Core/Helper/MessageCache.cs.meta +9 -1
  49. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +9 -1
  50. package/Runtime/Core/InstanceId.cs +25 -1
  51. package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
  52. package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
  53. package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
  54. package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
  55. package/Runtime/Core/Internal/TypedSlots.cs +5 -21
  56. package/Runtime/Core/MessageBus/IMessageBus.cs +12 -12
  57. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
  58. package/Runtime/Core/MessageBus/Internal/BusSlots.cs +7 -6
  59. package/Runtime/Core/MessageBus/MessageBus.cs +2313 -2936
  60. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
  61. package/Runtime/Core/MessageHandler.cs +1023 -1143
  62. package/Runtime/Core/MessageRegistrationToken.cs +425 -47
  63. package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +9 -1
  64. package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +9 -1
  65. package/Runtime/Core/Messages/StringMessage.cs.meta +9 -1
  66. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +9 -1
  67. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +9 -1
  68. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +9 -1
  69. package/Runtime/Unity/MessageAwareComponent.cs +46 -1
  70. package/Runtime/Unity/MessagingComponent.cs +43 -10
  71. package/Runtime/WallstopStudios.DxMessaging.asmdef +1 -1
  72. package/Samples~/DI/README.md +7 -7
  73. package/SourceGenerators/Directory.Build.props +50 -3
  74. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
  75. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
  76. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +9 -1
  77. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +39 -46
  78. package/SourceGenerators/global.json +7 -0
  79. package/SourceGenerators/global.json.meta +7 -0
  80. package/package.json +27 -40
  81. package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs +0 -51
@@ -31,6 +31,39 @@ namespace DxMessaging.Editor.Settings
31
31
  private const string FormatComment =
32
32
  "# One fully-qualified type name per line. Lines starting with # are comments.";
33
33
 
34
+ /// <summary>
35
+ /// Test seam controlling WHEN deferred regeneration runs. Production schedules the work on
36
+ /// the next editor tick via <see cref="EditorApplication.delayCall"/>; tests substitute a
37
+ /// capturing scheduler so they can assert no synchronous AssetDatabase import happens and
38
+ /// then drive the deferred work deterministically without touching the filesystem.
39
+ /// </summary>
40
+ internal static Action<Action> DeferralScheduler { get; set; } =
41
+ work => EditorApplication.delayCall += () => work();
42
+
43
+ /// <summary>
44
+ /// Test seam for Unity editor state. Production uses Unity's update/compile flags; tests
45
+ /// substitute a deterministic busy/idle predicate to prove deferred work requeues.
46
+ /// </summary>
47
+ internal static Func<bool> CanMutateAssetDatabase { get; set; } =
48
+ DxMessaging.Editor.DxMessagingEditorIdle.CanMutateAssetDatabase;
49
+
50
+ /// <summary>
51
+ /// Test seam controlling WHAT regeneration does. Production performs the real
52
+ /// write + <see cref="AssetDatabase.ImportAsset"/> via <see cref="RegenerateSidecarCore"/>;
53
+ /// tests substitute a recorder so they can observe whether the apply happens synchronously
54
+ /// or only after the deferred tick, without writing the on-disk sidecar.
55
+ /// </summary>
56
+ internal static Action<DxMessagingSettings> SidecarApplier { get; set; } =
57
+ RegenerateSidecarCore;
58
+
59
+ /// <summary>
60
+ /// Test seam for the follow-up <c>csc.rsp</c> sync. Production schedules the
61
+ /// <c>-additionalfile</c> normalization after sidecar regeneration, which covers deferred
62
+ /// <c>OnValidate</c> writes that finish after <see cref="SetupCscRsp"/>'s startup pass.
63
+ /// </summary>
64
+ internal static Action CscRspAdditionalFileSyncScheduler { get; set; } =
65
+ ScheduleDefaultCscRspAdditionalFileSync;
66
+
34
67
  /// <summary>
35
68
  /// Regenerates the sidecar text file from the supplied settings asset. Writes only when
36
69
  /// the on-disk content differs from what would be written, matching the
@@ -39,11 +72,15 @@ namespace DxMessaging.Editor.Settings
39
72
  /// </summary>
40
73
  /// <param name="settings">The settings asset. May be <c>null</c> -- no-op in that case.</param>
41
74
  /// <remarks>
42
- /// When called while Unity is mid-compile or mid-asset-import (e.g., from a
43
- /// <c>ScriptableObject.OnValidate</c> that fires during a domain reload), the actual
44
- /// regen is deferred via <see cref="EditorApplication.delayCall"/> so we don't trip
45
- /// AssetDatabase reentrancy guards. Direct calls from EditMode tests run synchronously
46
- /// because tests don't execute during update/compile.
75
+ /// This is the entry point for explicit, user-driven edits (the "Ignore this type" button,
76
+ /// settings asset Inspector edits) and for EditMode tests: when Unity is idle the regen runs
77
+ /// synchronously for immediate feedback, and when Unity is mid-compile or mid-asset-import
78
+ /// it falls back to <see cref="RegenerateSidecarDeferred"/>. It MUST NOT be called from a
79
+ /// deserialization callback such as <c>ScriptableObject.OnValidate</c> -- those callbacks
80
+ /// can fire during the domain-load asset-import-worker window where
81
+ /// <see cref="EditorApplication.isUpdating"/> and <see cref="EditorApplication.isCompiling"/>
82
+ /// are both <c>false</c>, so this method would import synchronously and crash the editor
83
+ /// (issue #210). Use <see cref="RegenerateSidecarDeferred"/> from those callbacks instead.
47
84
  /// </remarks>
48
85
  public static void RegenerateSidecar(DxMessagingSettings settings)
49
86
  {
@@ -52,13 +89,97 @@ namespace DxMessaging.Editor.Settings
52
89
  return;
53
90
  }
54
91
 
55
- if (EditorApplication.isUpdating || EditorApplication.isCompiling)
92
+ if (!CanCurrentlyMutateAssetDatabase())
56
93
  {
57
- EditorApplication.delayCall += () => RegenerateSidecarCore(settings);
94
+ RegenerateSidecarDeferred(settings);
58
95
  return;
59
96
  }
60
97
 
61
- RegenerateSidecarCore(settings);
98
+ ApplySidecarAndScheduleCscRspSync(settings);
99
+ }
100
+
101
+ /// <summary>
102
+ /// Schedules sidecar regeneration for the next editor tick, NEVER writing or importing
103
+ /// synchronously. This is the only safe entry point from
104
+ /// <see cref="ScriptableObject.OnValidate"/> and other asset-deserialization callbacks.
105
+ /// </summary>
106
+ /// <param name="settings">The settings asset. May be <c>null</c> -- no-op in that case.</param>
107
+ /// <remarks>
108
+ /// A synchronous <see cref="AssetDatabase.ImportAsset"/> issued from within asset
109
+ /// deserialization re-enters the asset importer and hard-crashes the native editor
110
+ /// (<c>GuidReservations::Reserve</c> abort, issue #210, observed on Unity 6000.4+). The
111
+ /// dangerous window includes the domain-load asset-import worker, during which
112
+ /// <see cref="EditorApplication.isUpdating"/> and <see cref="EditorApplication.isCompiling"/>
113
+ /// are both <c>false</c> -- so the flag-based guard in <see cref="RegenerateSidecar"/> is
114
+ /// not sufficient on its own and this method defers unconditionally. The captured
115
+ /// <paramref name="settings"/> reference is safe to hold: <see cref="EditorApplication.delayCall"/>
116
+ /// is cleared on domain reload, so the callback never outlives its domain, and the
117
+ /// lifetime-aware null check covers the asset being destroyed (deleted) within the domain.
118
+ /// Scheduling here cannot throw, and the deferred regeneration self-guards via
119
+ /// <see cref="RegenerateSidecarCore"/>'s internal try/catch, so callers such as
120
+ /// <c>OnValidate</c> need no surrounding exception guard.
121
+ /// </remarks>
122
+ internal static void RegenerateSidecarDeferred(DxMessagingSettings settings)
123
+ {
124
+ if (settings == null)
125
+ {
126
+ return;
127
+ }
128
+
129
+ DxMessaging.Editor.DxMessagingEditorIdle.ScheduleAssetDatabaseMutation(
130
+ () =>
131
+ {
132
+ if (settings == null)
133
+ {
134
+ return;
135
+ }
136
+ ApplySidecarAndScheduleCscRspSync(settings);
137
+ },
138
+ DeferralScheduler,
139
+ CanCurrentlyMutateAssetDatabase
140
+ );
141
+ }
142
+
143
+ private static bool CanCurrentlyMutateAssetDatabase()
144
+ {
145
+ Func<bool> canMutateAssetDatabase =
146
+ CanMutateAssetDatabase
147
+ ?? DxMessaging.Editor.DxMessagingEditorIdle.CanMutateAssetDatabase;
148
+ return canMutateAssetDatabase();
149
+ }
150
+
151
+ private static void ApplySidecarAndScheduleCscRspSync(DxMessagingSettings settings)
152
+ {
153
+ try
154
+ {
155
+ SidecarApplier(settings);
156
+ }
157
+ finally
158
+ {
159
+ ScheduleCscRspAdditionalFileSync();
160
+ }
161
+ }
162
+
163
+ private static void ScheduleCscRspAdditionalFileSync()
164
+ {
165
+ try
166
+ {
167
+ Action scheduler =
168
+ CscRspAdditionalFileSyncScheduler ?? ScheduleDefaultCscRspAdditionalFileSync;
169
+ scheduler();
170
+ }
171
+ catch (Exception ex)
172
+ {
173
+ DxMessaging.Editor.DxMessagingEditorLog.LogWarning(
174
+ "Failed to schedule csc.rsp base-call ignore additionalfile sync.",
175
+ ex
176
+ );
177
+ }
178
+ }
179
+
180
+ private static void ScheduleDefaultCscRspAdditionalFileSync()
181
+ {
182
+ DxMessaging.Editor.SetupCscRsp.ScheduleAdditionalFileForIgnoreListSync();
62
183
  }
63
184
 
64
185
  private static void RegenerateSidecarCore(DxMessagingSettings settings)
@@ -88,8 +209,9 @@ namespace DxMessaging.Editor.Settings
88
209
  }
89
210
  catch (Exception ex)
90
211
  {
91
- Debug.LogWarning(
92
- $"[DxMessaging] Failed to write base-call ignore sidecar at '{SidecarAssetPath}': {ex.Message}"
212
+ DxMessaging.Editor.DxMessagingEditorLog.LogWarning(
213
+ $"Failed to write base-call ignore sidecar at '{SidecarAssetPath}'.",
214
+ ex
93
215
  );
94
216
  }
95
217
  }
@@ -126,8 +248,9 @@ namespace DxMessaging.Editor.Settings
126
248
  }
127
249
  catch (Exception ex)
128
250
  {
129
- Debug.LogWarning(
130
- $"[DxMessaging] Failed to read base-call ignore sidecar at '{SidecarAssetPath}': {ex.Message}"
251
+ DxMessaging.Editor.DxMessagingEditorLog.LogWarning(
252
+ $"Failed to read base-call ignore sidecar at '{SidecarAssetPath}'.",
253
+ ex
131
254
  );
132
255
  return Array.Empty<string>();
133
256
  }
@@ -25,7 +25,7 @@ namespace DxMessaging.Editor.Settings
25
25
  [SerializeField]
26
26
  [HideInInspector]
27
27
  [FormerlySerializedAs("_enableDiagnosticsInEditor")]
28
- private bool _legacyEnableDiagnosticsInEditor;
28
+ internal bool _legacyEnableDiagnosticsInEditor;
29
29
 
30
30
  [SerializeField]
31
31
  internal int _messageBufferSize = IMessageBus.DefaultMessageBufferSize;
@@ -47,8 +47,12 @@ namespace DxMessaging.Editor.Settings
47
47
  /// </summary>
48
48
  public DiagnosticsTarget DiagnosticsTargets
49
49
  {
50
- get => _diagnosticsTargets;
51
- set => _diagnosticsTargets = value;
50
+ get => EffectiveDiagnosticsTargets;
51
+ set
52
+ {
53
+ _diagnosticsTargets = value;
54
+ _legacyEnableDiagnosticsInEditor = false;
55
+ }
52
56
  }
53
57
 
54
58
  /// <summary>
@@ -128,8 +132,8 @@ namespace DxMessaging.Editor.Settings
128
132
  /// </para>
129
133
  /// <para>
130
134
  /// Toggling this property is observable via a deferred
131
- /// <see cref="DxMessaging.Editor.Analyzers.DxMessagingConsoleHarvester.RescanNow"/> so the
132
- /// inspector overlay refreshes without waiting for the next compile.
135
+ /// <see cref="DxMessaging.Editor.Analyzers.DxMessagingConsoleHarvester.ScheduleRescanWhenIdle"/>
136
+ /// so the inspector overlay refreshes without waiting for the next compile.
133
137
  /// </para>
134
138
  /// </remarks>
135
139
  public bool UseConsoleBridge
@@ -143,24 +147,51 @@ namespace DxMessaging.Editor.Settings
143
147
  }
144
148
  _useConsoleBridge = value;
145
149
  EditorUtility.SetDirty(this);
146
- EditorApplication.delayCall += DxMessaging
147
- .Editor
148
- .Analyzers
149
- .DxMessagingConsoleHarvester
150
- .RescanNow;
150
+ DxMessaging.Editor.Analyzers.DxMessagingConsoleHarvester.ScheduleRescanWhenIdle();
151
151
  }
152
152
  }
153
153
 
154
154
  /// <summary>
155
- /// Fully-qualified type names excluded from the base-call check. Editable via the Project Settings UI
156
- /// or via the Inspector overlay's "Ignore this type" button.
155
+ /// Fully-qualified type names excluded from the base-call check. Editable on the settings
156
+ /// asset Inspector or via the Inspector overlay's "Ignore this type" button.
157
157
  /// </summary>
158
158
  public IReadOnlyList<string> BaseCallIgnoredTypes => _baseCallIgnoredTypes;
159
159
 
160
160
  /// <summary>
161
- /// Loads the settings asset if present, otherwise creates it with sensible defaults.
161
+ /// Diagnostics target value after applying legacy serialized fields in memory. This does
162
+ /// not mark the settings asset dirty or persist the migration.
162
163
  /// </summary>
163
- internal static DxMessagingSettings GetOrCreateSettings()
164
+ internal DiagnosticsTarget EffectiveDiagnosticsTargets
165
+ {
166
+ get
167
+ {
168
+ if (
169
+ _diagnosticsTargets == DiagnosticsTarget.Off
170
+ && _legacyEnableDiagnosticsInEditor
171
+ )
172
+ {
173
+ return DiagnosticsTarget.Editor;
174
+ }
175
+ return _diagnosticsTargets;
176
+ }
177
+ }
178
+
179
+ /// <summary>
180
+ /// Loads the settings asset without creating, saving, or migrating it. Returns
181
+ /// <c>null</c> when no asset exists.
182
+ /// </summary>
183
+ /// <remarks>
184
+ /// Unlike <see cref="GetOrCreateSettings"/>, this performs NO <c>AssetDatabase</c> mutation
185
+ /// (<c>CreateAsset</c>/<c>SaveAssets</c>/durable legacy migration), so it is safe to call
186
+ /// during the domain-load asset-import window -- e.g. from an <c>[InitializeOnLoad]</c>
187
+ /// static constructor -- where a synchronous mutation re-enters the importer and crashes
188
+ /// the native editor (issue #210). Legacy serialized fields are still reflected through
189
+ /// effective property getters so passive callers see the intended value while the durable
190
+ /// migration waits for a safe editor-idle tick. Callers that need the asset
191
+ /// created/migrated must schedule <see cref="GetOrCreateSettings"/> off that window (e.g.
192
+ /// via <see cref="DxMessaging.Editor.DxMessagingEditorIdle.ScheduleAssetDatabaseMutation"/>).
193
+ /// </remarks>
194
+ internal static DxMessagingSettings LoadSettingsPassive()
164
195
  {
165
196
  DxMessagingSettings settings = AssetDatabase.LoadAssetAtPath<DxMessagingSettings>(
166
197
  SettingsPath
@@ -175,6 +206,21 @@ namespace DxMessaging.Editor.Settings
175
206
  .FirstOrDefault(asset => asset != null);
176
207
  }
177
208
 
209
+ return settings;
210
+ }
211
+
212
+ /// <summary>
213
+ /// Loads the settings asset if present, otherwise creates it with sensible defaults.
214
+ /// </summary>
215
+ /// <remarks>
216
+ /// Performs <c>AssetDatabase</c> mutations (create on first run, legacy-field migration), so
217
+ /// it MUST NOT be called synchronously during the domain-load asset-import window (issue
218
+ /// #210). Use <see cref="LoadSettingsPassive"/> for mutation-free reads in that context.
219
+ /// </remarks>
220
+ internal static DxMessagingSettings GetOrCreateSettings()
221
+ {
222
+ DxMessagingSettings settings = LoadSettingsPassive();
223
+
178
224
  if (settings == null)
179
225
  {
180
226
  settings = CreateInstance<DxMessagingSettings>();
@@ -191,13 +237,8 @@ namespace DxMessaging.Editor.Settings
191
237
  AssetDatabase.SaveAssets();
192
238
  }
193
239
 
194
- if (
195
- settings._diagnosticsTargets == DiagnosticsTarget.Off
196
- && settings._legacyEnableDiagnosticsInEditor
197
- )
240
+ if (settings.ApplyLegacyDiagnosticsMigration())
198
241
  {
199
- settings._diagnosticsTargets = DiagnosticsTarget.Editor;
200
- settings._legacyEnableDiagnosticsInEditor = false;
201
242
  EditorUtility.SetDirty(settings);
202
243
  AssetDatabase.SaveAssets();
203
244
  }
@@ -205,6 +246,21 @@ namespace DxMessaging.Editor.Settings
205
246
  return settings;
206
247
  }
207
248
 
249
+ internal bool ApplyLegacyDiagnosticsMigration()
250
+ {
251
+ if (!_legacyEnableDiagnosticsInEditor)
252
+ {
253
+ return false;
254
+ }
255
+
256
+ if (_diagnosticsTargets == DiagnosticsTarget.Off)
257
+ {
258
+ _diagnosticsTargets = DiagnosticsTarget.Editor;
259
+ }
260
+ _legacyEnableDiagnosticsInEditor = false;
261
+ return true;
262
+ }
263
+
208
264
  /// <summary>
209
265
  /// Returns a serialized wrapper for use in SettingsProvider inspectors.
210
266
  /// </summary>
@@ -216,10 +272,7 @@ namespace DxMessaging.Editor.Settings
216
272
  private void OnEnable()
217
273
  {
218
274
  // Defensive: the field can be null if the asset was saved before this field existed.
219
- if (_baseCallIgnoredTypes == null)
220
- {
221
- _baseCallIgnoredTypes = new List<string>();
222
- }
275
+ EnsureIgnoreListInitialized();
223
276
  // Intentionally NOT regenerating the sidecar here. OnEnable fires on every domain reload
224
277
  // and play-mode entry; the sidecar on disk is already consistent with what we'd write
225
278
  // (RegenerateSidecar is idempotent, but ImportAsset still produces churn). Regen runs
@@ -227,12 +280,22 @@ namespace DxMessaging.Editor.Settings
227
280
  }
228
281
 
229
282
  private void OnValidate()
283
+ {
284
+ EnsureIgnoreListInitialized();
285
+ // Issue #210: OnValidate fires during asset deserialization, including the domain-load
286
+ // asset-import-worker window where EditorApplication.isUpdating/isCompiling are both
287
+ // false. Writing + importing the sidecar synchronously there re-enters the asset
288
+ // importer and hard-crashes the native editor (GuidReservations::Reserve abort on Unity
289
+ // 6000.4+). Always defer to the next editor tick; never import synchronously from here.
290
+ DxMessagingBaseCallIgnoreSync.RegenerateSidecarDeferred(this);
291
+ }
292
+
293
+ private void EnsureIgnoreListInitialized()
230
294
  {
231
295
  if (_baseCallIgnoredTypes == null)
232
296
  {
233
297
  _baseCallIgnoredTypes = new List<string>();
234
298
  }
235
- TryRegenerateSidecar();
236
299
  }
237
300
 
238
301
  /// <summary>
@@ -245,10 +308,7 @@ namespace DxMessaging.Editor.Settings
245
308
  {
246
309
  return;
247
310
  }
248
- if (_baseCallIgnoredTypes == null)
249
- {
250
- _baseCallIgnoredTypes = new List<string>();
251
- }
311
+ EnsureIgnoreListInitialized();
252
312
  if (
253
313
  _baseCallIgnoredTypes.Any(entry =>
254
314
  string.Equals(entry, fullyQualifiedTypeName, System.StringComparison.Ordinal)
@@ -296,8 +356,9 @@ namespace DxMessaging.Editor.Settings
296
356
  }
297
357
  catch (System.Exception ex)
298
358
  {
299
- Debug.LogWarning(
300
- $"[DxMessaging] Failed to regenerate base-call ignore sidecar: {ex.Message}"
359
+ DxMessaging.Editor.DxMessagingEditorLog.LogWarning(
360
+ "Failed to regenerate base-call ignore sidecar.",
361
+ ex
301
362
  );
302
363
  }
303
364
  }
@@ -1,3 +1,11 @@
1
1
  fileFormatVersion: 2
2
2
  guid: 477d53eef70c4496b945199c62169dfb
3
- timeCreated: 1749668079
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -1,3 +1,11 @@
1
1
  fileFormatVersion: 2
2
2
  guid: e4353b92a69a4703abc268fb885a1224
3
- timeCreated: 1749668115
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: