feat(engine-ambient): delete static fallbacks; add MultiInstanceEngineTests
Step 8 (final) of multi-instancing migration. All per-battle statics now require a BattleAmbient scope — unwrapped writes throw InvalidOperationException (fail-fast forcing function). MultiInstanceEngineTests proves correctness: two parallel battles resolve independently, N=4/8/16 stress matches sequential baseline, GameMgr.GetIns throws without scope. Migration complete. EngineSessionGate gone. Suite fully green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
Multi-instancing migration (Step 8 — forcing function): delete the static RealTimeNetworkAgent
|
||||
fallback the earlier .ambient-rtna patch added as a coexistence shim. RealTimeNetworkAgent is now
|
||||
strictly per-session ambient. Supersedes the intermediate .ambient-rtna patch (kept for the audit
|
||||
trail).
|
||||
|
||||
Semantics:
|
||||
- RealTimeNetworkAgent (getter) : Current?.NetworkAgent — soft (null when no scope).
|
||||
Engine code reads this via `ToolboxGame.RealTimeNetworkAgent?.Foo`-style patterns (network
|
||||
send paths gated on a non-null agent), so a null on the unwrapped path is the correct
|
||||
degrade — not a throw.
|
||||
- SetRealTimeNetworkBattle : BattleAmbient.Require().NetworkAgent = agent — strict (writes must
|
||||
land on the per-session ctx; forcing function).
|
||||
- DestroyNetworkAgent : reads Current, destroys, nulls the ambient NetworkAgent in-place.
|
||||
Outside a scope is a no-op (nothing to destroy in the ambient world).
|
||||
|
||||
In-file edits:
|
||||
- line ~28 declaration `private static RealTimeNetworkAgent _realTimeNetworkAgentFallback;` DELETED
|
||||
- lines ~29-32 getter body no fallback chain
|
||||
- lines ~61-66 Set body Require() (strict)
|
||||
- lines ~68-78 Destroy body Current-based; no fallback branch
|
||||
|
||||
--- Engine/Wizard/ToolboxGame.cs (~lines 28-32)
|
||||
- private static RealTimeNetworkAgent _realTimeNetworkAgentFallback;
|
||||
public static RealTimeNetworkAgent RealTimeNetworkAgent
|
||||
{
|
||||
- get => SVSim.BattleEngine.Ambient.BattleAmbient.Current?.NetworkAgent ?? _realTimeNetworkAgentFallback;
|
||||
+ // Soft read: returns null when no scope is active, mirroring GetIns. Engine code reads
|
||||
+ // this via `ToolboxGame.RealTimeNetworkAgent?.Foo`-style patterns (network-send paths
|
||||
+ // gated on a non-null agent), so a null on the unwrapped path is the correct degrade —
|
||||
+ // not a throw.
|
||||
+ get => SVSim.BattleEngine.Ambient.BattleAmbient.Current?.NetworkAgent;
|
||||
}
|
||||
|
||||
--- Engine/Wizard/ToolboxGame.cs (~lines 61-78)
|
||||
public static void SetRealTimeNetworkBattle(RealTimeNetworkAgent agent)
|
||||
{
|
||||
- var c = SVSim.BattleEngine.Ambient.BattleAmbient.Current;
|
||||
- if (c != null) c.NetworkAgent = agent;
|
||||
- else _realTimeNetworkAgentFallback = agent;
|
||||
+ // Strict: must be inside a scope to set the per-session agent. Forcing-function — any
|
||||
+ // historical unwrapped caller (no production callsite remains) now fails fast.
|
||||
+ SVSim.BattleEngine.Ambient.BattleAmbient.Require().NetworkAgent = agent;
|
||||
}
|
||||
|
||||
public static void DestroyNetworkAgent()
|
||||
{
|
||||
var c = SVSim.BattleEngine.Ambient.BattleAmbient.Current;
|
||||
- var current = c?.NetworkAgent ?? _realTimeNetworkAgentFallback;
|
||||
+ var current = c?.NetworkAgent;
|
||||
if (current != null)
|
||||
{
|
||||
Object.DestroyImmediate(current.gameObject);
|
||||
- if (c != null) c.NetworkAgent = null;
|
||||
- else _realTimeNetworkAgentFallback = null;
|
||||
+ c.NetworkAgent = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user