fix(battlenode): PlayedCardTribe degrades to 0 not empty; clan/tribe builder tests (M-HC-4e review)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -61,7 +61,7 @@ internal sealed class PlayActionsHandler : IFrameHandler
|
||||
// value would miss it). Prod always emits both on every knownList entry: clan as the int ClanType
|
||||
// ordinal, tribe as the comma-joined int TribeType string ("0" for none). Same senderSeat mapping.
|
||||
int playedClan = ctx.Engine.PlayedCardClan(senderSeat, playIdx, fallback: 0);
|
||||
string playedTribe = ctx.Engine.PlayedCardTribe(senderSeat, playIdx);
|
||||
string playedTribe = ctx.Engine.PlayedCardTribe(senderSeat, playIdx, fallback: "0");
|
||||
|
||||
var played = KnownListBuilder.BuildPlayedCard(
|
||||
deckMap, playIdx, orderList, cost: playedCost, spellboost: playedSpellboost,
|
||||
|
||||
@@ -302,13 +302,18 @@ internal sealed class SessionBattleEngine
|
||||
/// <see cref="BattleCardBase.Tribe"/>, whose getter folds in any skill-applied tribe CHANGE/ADD over
|
||||
/// <c>BaseParameter.Tribe</c> (and drops ALL when the resolved list has ≥2 entries) — so the wire carries
|
||||
/// the LIVE tribe, the faithful value over the static card-master one.
|
||||
/// <para>Same post-resolution zone search as <see cref="PlayedCardCost"/>; no engine / no card → "" (an
|
||||
/// engine that isn't owned this session emits no card, so the caller's BuildPlayedCard never fires).</para></summary>
|
||||
public string PlayedCardTribe(bool playerSeat, int idx)
|
||||
/// <para>Same post-resolution zone search + degrade-to-<paramref name="fallback"/> contract as
|
||||
/// <see cref="PlayedCardClan"/>: no engine / no card → <paramref name="fallback"/> (default <c>"0"</c>, the
|
||||
/// prod no-tribe form — NEVER empty, which is wire-illegal: prod always sends tribe as a non-empty string,
|
||||
/// the client reads it via <c>item.Value.ToString()</c> at NetworkBattleReceiver.cs:2382). The degrade is
|
||||
/// LIVE, not dead: a second concurrent battle that loses the single-active-engine gate has <c>_mgr is null</c>
|
||||
/// yet still emits a knownList entry (KnownListBuilder.BuildPlayedCard gates on the deck map, not engine
|
||||
/// ownership), so this path must hand back a legal wire value.</para></summary>
|
||||
public string PlayedCardTribe(bool playerSeat, int idx, string fallback = "0")
|
||||
{
|
||||
if (_mgr is null) return string.Empty;
|
||||
if (_mgr is null) return fallback;
|
||||
var card = FindByIndex(Seat(playerSeat), idx);
|
||||
if (card is null) return string.Empty;
|
||||
if (card is null) return fallback;
|
||||
var tribe = card.Tribe;
|
||||
// Prod's no-tribe form is the single "0" (TribeType.ALL == 0), never an empty string; an empty list
|
||||
// (defensive) renders the same "0".
|
||||
|
||||
@@ -125,6 +125,33 @@ public class KnownListBuilderTests
|
||||
Assert.That(KnownListBuilder.BuildPlayedCard(deckMap, 3, OrderListMove(3, 10, 20))!.Spellboost, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildPlayedCard_emits_clan_tribe_passed_by_caller()
|
||||
{
|
||||
// M-HC-4e: the handler reads the engine-resolved clan/tribe
|
||||
// (SessionBattleEngine.PlayedCardClan / PlayedCardTribe) and passes them in; BuildPlayedCard lands
|
||||
// them on the entry verbatim. (A wrong clan/tribe yields a different field — non-vacuity.)
|
||||
var deckMap = new Dictionary<int, long> { [3] = 101314020L };
|
||||
var entry = KnownListBuilder.BuildPlayedCard(
|
||||
deckMap, playIdx: 3, orderList: OrderListMove(3, 10, 20), cost: 3, spellboost: 2, clan: 8, tribe: "7,16");
|
||||
Assert.That(entry, Is.Not.Null);
|
||||
Assert.That(entry!.Clan, Is.EqualTo(8));
|
||||
Assert.That(entry.Tribe, Is.EqualTo("7,16"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildPlayedCard_defaults_clan_to_zero_and_tribe_to_string_zero_when_caller_passes_none()
|
||||
{
|
||||
// A play whose engine read degraded (single-active-engine gate: _mgr null → the accessor fallback)
|
||||
// emits clan 0 (ClanType.ALL ordinal) and tribe "0" (the prod no-tribe form, NEVER empty —
|
||||
// empty is wire-illegal). The param defaults match the accessor fallbacks.
|
||||
var deckMap = new Dictionary<int, long> { [3] = 101311010L };
|
||||
var entry = KnownListBuilder.BuildPlayedCard(deckMap, 3, OrderListMove(3, 10, 20));
|
||||
Assert.That(entry, Is.Not.Null);
|
||||
Assert.That(entry!.Clan, Is.EqualTo(0));
|
||||
Assert.That(entry.Tribe, Is.EqualTo("0"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RenameTargets_passes_isSelf_through_verbatim()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user