feat(battlenode): per-session charaId + single-active-engine gate (Phase 2 N2 carried-risk B)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ using RealTimeNetworkAgent = engine::RealTimeNetworkAgent;
|
||||
using Gungnir = engine::Gungnir;
|
||||
using NetworkNullLogger = engine::NetworkNullLogger;
|
||||
using ToolboxGame = engine::Wizard.ToolboxGame;
|
||||
using GameMgr = engine::GameMgr;
|
||||
using BattleUIContainer = engine::BattleUIContainer;
|
||||
using BackGroundBase = engine::BackGroundBase;
|
||||
using NullPlayerEmotion = engine::Wizard.Battle.Player.Emotion.NullPlayerEmotion;
|
||||
@@ -45,10 +46,23 @@ internal sealed class SessionBattleEngine
|
||||
|
||||
/// <summary>Construct the two-seat network battle from both decks + the master seed (design F-N-5).
|
||||
/// <paramref name="seatADeck"/>/<paramref name="seatBDeck"/> are the per-side deck orders the node
|
||||
/// already computed (BattleSessionState.GetShuffledDeck) and handed each client.</summary>
|
||||
/// already computed (BattleSessionState.GetShuffledDeck) and handed each client.
|
||||
/// <paramref name="seatAClass"/>/<paramref name="seatBClass"/> are each seat's class ordinal (1..8,
|
||||
/// the <c>CardClass</c> int value); they select the leader's class via the all-8-class
|
||||
/// ClassCharacterList EngineGlobalInit installs (chara_id == class_id for 1..8). The 3-arg overload
|
||||
/// behavior is preserved by the defaults (1/2), matching the test-harness charaIds.
|
||||
/// <para>NOTE: GameMgr (the leader chara ids set below) is a PROCESS GLOBAL. Setting per-session
|
||||
/// chara ids is therefore only safe while exactly one engine-backed battle exists at a time — the
|
||||
/// invariant <see cref="EngineSessionGate"/> enforces on the caller side.</para></summary>
|
||||
public void Setup(int masterSeed,
|
||||
IReadOnlyList<long> seatADeck, IReadOnlyList<long> seatBDeck)
|
||||
IReadOnlyList<long> seatADeck, IReadOnlyList<long> seatBDeck,
|
||||
int seatAClass = 1, int seatBClass = 2)
|
||||
{
|
||||
// Prime the engine's process-global statics (CardMaster, Wizard.Data, all-8-class Master,
|
||||
// GameMgr/netUser/udid). Idempotent (process-once); makes the LIVE host ready so Setup succeeds
|
||||
// here rather than throwing into the shadow's no-op path (Phase 2 N2, carried-risk A).
|
||||
EngineGlobalInit.EnsureInitialized();
|
||||
|
||||
// rng defaults to SeededRandomSource(masterSeed) inside the mgr — the stream is born aligned
|
||||
// with the seed the node handed both clients (F-N-5; O-N-2 "bit-aligned anyway").
|
||||
var mgr = new HeadlessNetworkBattleMgr(new SessionContentsCreator(masterSeed));
|
||||
@@ -75,6 +89,11 @@ internal sealed class SessionBattleEngine
|
||||
InitHeadlessViews(mgr); // turn/play cycle dereferences UI-container + emotion refs
|
||||
InstallHeadlessNetworkAgent(); // turn-flow resolve reads ToolboxGame.RealTimeNetworkAgent
|
||||
|
||||
// Per-session leader class: chara_id == class_id for 1..8 in the all-8-class ClassCharacterList,
|
||||
// so writing the seats' class ordinals into GameMgr's DataMgr resolves each leader's correct
|
||||
// class. Process-global — safe only under EngineSessionGate (see method remarks above).
|
||||
SetGameMgrCharaIds(seatAClass, seatBClass);
|
||||
|
||||
SeedDeck(mgr, seatADeck, isPlayer: true);
|
||||
SeedDeck(mgr, seatBDeck, isPlayer: false);
|
||||
|
||||
@@ -238,6 +257,19 @@ internal sealed class SessionBattleEngine
|
||||
ToolboxGame.SetRealTimeNetworkBattle(agent);
|
||||
}
|
||||
|
||||
// Write the two seats' class ordinals into GameMgr's DataMgr leader chara ids. Mirrors the test
|
||||
// seam HeadlessFixture.cs:202-204 (SetField(dm, "_playerCharaId"/"_enemyCharaId", ...)). chara_id ==
|
||||
// class_id for 1..8 in EngineGlobalInit's all-8-class ClassCharacterList, so the ordinal selects the
|
||||
// class. A non-positive ordinal (e.g. CardClass.None == 0) clamps to the default seat (1/2).
|
||||
// GameMgr is a process global → safe only under EngineSessionGate (one engine-backed battle at a
|
||||
// time).
|
||||
private static void SetGameMgrCharaIds(int a, int b)
|
||||
{
|
||||
var dm = GameMgr.GetIns().GetDataMgr();
|
||||
SetField(dm, "_playerCharaId", a <= 0 ? 1 : a);
|
||||
SetField(dm, "_enemyCharaId", b <= 0 ? 2 : b);
|
||||
}
|
||||
|
||||
private static void SetField(object obj, string name, object value)
|
||||
{
|
||||
var f = obj.GetType().GetField(name,
|
||||
|
||||
Reference in New Issue
Block a user