fix(net): FA1 review round -- zero-id tree rejection, monarch clear, 0x001F builder
Applies both MUST-FIX items and the code-facing SHOULD-FIX items from the dual-lens FA1 review (docs/research/2026-08-12-fa1-review-mechanism.md, docs/research/2026-08-12-fa1-review-blast.md): Mechanism MF-1 / blast SF-2: AllegianceHierarchy::Add @0x005B6E90 wraps its entire body in `if (_id != 0)` -- a record whose own id is zero discards the WHOLE message, for both the monarch and a child record, and this is also what makes treeParent == 0 unconditionally fatal for a non-monarch record. ReadAllegianceProfileBody now rejects CharacterId == 0 on both paths; four new boundary tests in AllegianceProfileVersionGateTests.cs (zero-id monarch, zero-id child, zero treeParent, plus the existing orphan/self-parent/duplicate trio). Mechanism MF-2: added the missing 0x001F AllegianceUpdateRequest builder -- the structural twin of the fellowship 0x00A6 this slice already repaired -- with golden-vector tests for both on/off. Mechanism SF-1 / blast SF-3: UnPack's last act before returning success forces the monarch's MayPassupExperience to false regardless of the wire bit or the HasPackedLevel-absent legacy-compat fallback. Ported at the end of the record loop; the pre-existing HasPackedLevel-absent test moved off the monarch record (which the new clear makes indistinguishable from "the fallback never fired") onto a vassal record, and a new test proves the monarch clear fires even when the wire bit explicitly asks for true. Mechanism SF-2: removed ParseFellowshipDisband's invented body-length validation -- retail's DispatchUI_Disband reads only the opcode and never inspects a trailing body. The parser now always succeeds; the matching test flips from asserting rejection to asserting acceptance. Mechanism SF-3: added the D5 `<<1` shareLoot-shape test at the 0x02C0 FellowshipUpdateFellow site -- previously only pinned at 0x02BE, so a future split of the shared ReadFellow helper could silently reintroduce a bool read on this leg undetected. Mechanism SF-5: renumbered the version-gate comments in ReadAllegianceProfileBody to the true AllegianceVersion enum values (1-11, matching acclient.h's SpokespersonAdded..ApprovedVassal) instead of wire-appearance order, which only reached 10 and silently dropped gate 5 (BannedCharactersAdded, which is real but gates nothing in UnPack -- now called out explicitly). Fixed the stale "lane B §12" citation in SocialActions.cs to the actual master-table row. Blast SF-1: pinned the two retail-faithful but user-visible behavior changes FA1 made to the ALREADY-LIVE `@allegiance info` command -- reversed vassal print order (3-vassal test through FormatAllegianceInfoLines) and malformed-tree silent-drop (test at the GameEventWiring registration layer, which is `if (info is null) return;`). Blast SF-4: fixed a doc comment citing a nonexistent `ConfirmationResponseTests` class; the actual class is `ConfirmationTripleTests`. Blast SF-5: cross-referenced the confirmation-triple discriminator's split representation (ConfirmationType on the response leg only; bare uint on the two inbound legs production actually reads) at both sites, so FA4 inherits a stated decision rather than an unexplained inconsistency. Full Release suite: 13,158 passed / 4 skipped / 0 failed (13,162 total), up from the pre-fix-round 13,149/4/0 (+9 tests this round). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bc693728a6
commit
ed30808720
8 changed files with 385 additions and 34 deletions
|
|
@ -503,7 +503,14 @@ public static class GameEvents
|
|||
BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(8)) != 0);
|
||||
}
|
||||
|
||||
/// <summary>0x0274 CharacterConfirmationRequest — server-driven modal confirm.</summary>
|
||||
/// <summary>
|
||||
/// 0x0274 CharacterConfirmationRequest — server-driven modal confirm.
|
||||
/// <see cref="Type"/> is a bare <c>uint</c>, not <see cref="ConfirmationType"/>
|
||||
/// — this is the leg <c>GameplayConfirmationController.HandleRequest</c>
|
||||
/// actually consumes in production today. See <see cref="ConfirmationType"/>'s
|
||||
/// doc comment (blast review SF-5) for why the triple currently carries
|
||||
/// its discriminator two different ways.
|
||||
/// </summary>
|
||||
public readonly record struct CharacterConfirmationRequest(
|
||||
uint Type,
|
||||
uint ContextId,
|
||||
|
|
@ -526,7 +533,10 @@ public static class GameEvents
|
|||
/// <summary>
|
||||
/// 0x0276 CharacterConfirmationDone — server cancellation/completion of the
|
||||
/// outstanding confirmation tuple. Retail dispatches the same type/context
|
||||
/// pair to <c>RecvNotice_AbortConfirmationRequest</c>.
|
||||
/// pair to <c>RecvNotice_AbortConfirmationRequest</c>. <see cref="Type"/>
|
||||
/// is a bare <c>uint</c>, not <see cref="ConfirmationType"/> — the leg
|
||||
/// <c>GameplayConfirmationController.HandleDone</c> actually consumes;
|
||||
/// see <see cref="ConfirmationType"/>'s doc comment (blast review SF-5).
|
||||
/// </summary>
|
||||
public readonly record struct CharacterConfirmationDone(uint Type, uint ContextId);
|
||||
|
||||
|
|
@ -548,6 +558,19 @@ public static class GameEvents
|
|||
/// (lane B §3.15, lane C §1.3). Campaign FA needs exactly
|
||||
/// <see cref="SwearAllegiance"/> (1) and <see cref="Fellowship"/> (4)
|
||||
/// — D6.
|
||||
///
|
||||
/// <para>
|
||||
/// FA1 review round (blast SF-5): this enum currently types ONLY the
|
||||
/// response-side leg (<see cref="ConfirmationResponse.Type"/>) —
|
||||
/// <see cref="CharacterConfirmationRequest.Type"/> and
|
||||
/// <see cref="CharacterConfirmationDone.Type"/>, the two legs
|
||||
/// production actually reads today, remain bare <c>uint</c>. This is a
|
||||
/// deliberate, not-yet-decided split, not a double parser: FA1 did not
|
||||
/// touch the inbound legs. Before FA4 wires fellowship (4) and
|
||||
/// allegiance (1) confirmations, either promote both inbound records
|
||||
/// to <see cref="ConfirmationType"/> or treat this note as the standing
|
||||
/// decision that the enum stays response-side only.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public enum ConfirmationType : uint
|
||||
{
|
||||
|
|
@ -570,7 +593,7 @@ public static class GameEvents
|
|||
/// This record + parser exist to give the triple a complete, TYPED
|
||||
/// representation in Core.Net (the <see cref="ConfirmationType"/>
|
||||
/// enum, not a bare <c>uint</c>) and a round-trip conformance check —
|
||||
/// see <c>ConfirmationResponseTests</c> for the golden-vector /
|
||||
/// see <c>ConfirmationTripleTests</c> for the golden-vector /
|
||||
/// round-trip pair against <c>BuildConfirmationResponse</c>.
|
||||
/// </summary>
|
||||
public readonly record struct ConfirmationResponse(
|
||||
|
|
@ -742,11 +765,16 @@ public static class GameEvents
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>0x02BF FellowshipDisband</c> — empty body
|
||||
/// (<c>DispatchUI_Disband @0x006A5E80</c> reads only the opcode; ACE
|
||||
/// writes no body — lane B §3.11).
|
||||
/// <c>0x02BF FellowshipDisband</c> — <c>DispatchUI_Disband
|
||||
/// @0x006A5E80</c> reads ONLY the opcode and calls straight into the
|
||||
/// handler; it never inspects, validates, or even looks at a body
|
||||
/// length. ACE writes no body today (lane B §3.11), but encoding a
|
||||
/// length check retail itself does not perform would make a future
|
||||
/// non-empty body (an ACE change, a trailing pad) silently swallow the
|
||||
/// disband and leave the roster stuck in a fellowship the server
|
||||
/// already destroyed. Accept unconditionally — this always succeeds.
|
||||
/// </summary>
|
||||
public static bool ParseFellowshipDisband(ReadOnlySpan<byte> payload) => payload.Length == 0;
|
||||
public static bool ParseFellowshipDisband(ReadOnlySpan<byte> payload) => true;
|
||||
|
||||
/// <summary>
|
||||
/// <c>0x01C9 FellowshipFellowUpdateDone</c> — dead in the Sept-2013
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue