Follow-up to the multi-instancing migration. Wraps the process-shared engine
statics that aren't ambient-fronted but race between concurrent battles:
- UnityEngine.Resources._loaded: Dictionary -> ConcurrentDictionary.GetOrAdd
(the shared prefab cache keyed by path; concurrent first-misses produced
duplicate GameObjects + Dictionary corruption)
- UnityEngine.GameObject._components: Dictionary -> ConcurrentDictionary with
Interlocked.CompareExchange init (Resources.Load returns SHARED prefab
GameObjects, so two engines' Setup() can race on the same _components map
— surfaced as "Operations that change non-concurrent collections" crashes
during BattleManagerBase ctor's GetComponent<T>() chain)
- Wizard.LocalLog: single static lock around all mutating entry points
(StringBuilder _lastTraceLogStringBuilder + ~12 mutable string/bool/int
scratch fields; serializing the trace-log surface is cheap since logging
is not the hot path)
Flips SVSim.BattleEngine.Tests assembly Parallelizable scope from Self to
Fixtures and restructures MultiInstanceEngineTests.StressN_BaselineMatches so
Setup runs INSIDE Task.Run (was previously serialized as a workaround for the
LocalLog races). The fixture is also lifted to ParallelScope.All so the
two-engines and stress tests can run alongside each other.
Suite fully green under fixture parallelism (59/0/2 across 3 consecutive runs);
SVSim.UnitTests still 1054/0/0 — true multi-instance correctness is now proved
end-to-end in tests rather than gated behind a serial workaround.
Manifest sha refresh + new patch artifact for the LocalLog edit (decomp-origin);
the two shim files are authored, so no metadata update is needed for them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>