fix(net): FA1 -- repair fellowship builders, add 0x0290/0x0291, allegiance Kick
Campaign FA slice FA1 (lane B field-order sections §3.1-§3.7, lane C §3.2, §1.4). Two latent acdream builder defects repaired, both cited in lane B §5.2: - BuildFellowshipCreate (0x00A2) invented a nonexistent "openness" byte and silently sent it as the low byte of shareXP -- ACE would read an INVERTED shareXP value. Corrected to retail's real shape: [str16L name][u32 shareXP]. shareXP is the FellowshipShareXP character option, not a dialog checkbox. - BuildFellowshipUpdate(open:) mislabeled 0x00A6 as fellowship openness; it is FellowshipUpdateRequest -- panel VISIBILITY. Renamed to BuildFellowshipUpdateRequest(panelOpen:); the wire bytes were already correct, only the name/doc were wrong. ACE gates the whole 0x02C0 member-vitals stream on this message (lane B §4.5) -- a prerequisite for live vitals once FA4 wires the panel. Two builders added that acdream never had at all: - BuildFellowshipAssignNewLeader (0x0290) -- retail's leader-Quit path sends this before 0x00A3 disband=0 (lane B §2.5). - BuildFellowshipChangeOpenness (0x0291) -- the REAL openness toggle. - AllegianceRequests.BuildKick -- wire-identical to BuildBreak (both are Event_BreakAllegiance 0x001E); named separately so FA2's panel command surface can distinguish "break from patron" from "kick a vassal" (lane C §1.4). AllegianceInfoRequest (0x027B) was already live via ClientCommandRequests.BuildAllegianceInfoRequest -- not duplicated. Wrong-shape tests at SocialActionsTests.cs:53-105 re-pinned with hand-computed golden byte vectors deriving each field from the cited lane-B sections (not generated by calling the builder under test, per the OP1 convention this file already follows for BuildSetCharacterOptions). AllegianceRequestsTests.cs gained golden vectors for the existing Swear/Break builders plus the new Kick alias. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bb48d2c89d
commit
7be86f47f6
4 changed files with 264 additions and 35 deletions
|
|
@ -3,25 +3,32 @@ using System.Buffers.Binary;
|
|||
namespace AcDream.Core.Net.Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Outbound allegiance GameActions. Both Swear and Break carry a
|
||||
/// Outbound allegiance GameActions. Swear, Break, and Kick all carry a
|
||||
/// single <c>uint32</c> target-guid payload inside the standard
|
||||
/// <c>0xF7B1</c> GameAction envelope.
|
||||
///
|
||||
/// <para>
|
||||
/// Wire layout (r11 §2.1 / §2.3):
|
||||
/// Wire layout — byte-verified against
|
||||
/// <c>docs/research/2026-08-11-fa-allegiance-wire.md</c> §3.2 (lane C),
|
||||
/// three-way agreed with ACE + Chorizite there (the retail anchor is the
|
||||
/// opcode literal store inside each <c>CM_Allegiance::Event_*</c> sender,
|
||||
/// the strongest possible provenance):
|
||||
/// <code>
|
||||
/// u32 0xF7B1
|
||||
/// u32 gameActionSequence
|
||||
/// u32 subOpcode (0x001D or 0x001E)
|
||||
/// u32 subOpcode (0x001D, 0x001E)
|
||||
/// u32 targetGuid
|
||||
/// </code>
|
||||
/// total 0x10 bytes for both. No change was needed here in Campaign FA
|
||||
/// slice FA1 (2026-08-11) — the pre-existing shape already matched.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Server replies with <c>GameEventAllegianceUpdate</c> (0x0020) and
|
||||
/// <c>GameEventAllegianceAllegianceUpdateDone</c> (0x01C8) on success,
|
||||
/// or a <c>WeenieError</c> on failure (already sworn, already maxed
|
||||
/// vassals, target not online, etc — see r11 §2.1 for the full list).
|
||||
/// vassals, target not online, etc — see lane C §2 master table for the
|
||||
/// full list).
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class AllegianceRequests
|
||||
|
|
@ -39,12 +46,32 @@ public static class AllegianceRequests
|
|||
/// <summary>
|
||||
/// Break your pledge to <paramref name="targetGuid"/>. Target can be
|
||||
/// your patron (breaking from) OR your vassal (breaking them away).
|
||||
/// Retail's Break button (element <c>0x10000264</c>) always targets
|
||||
/// your own patron — see <see cref="BuildKick"/> for the vassal-facing
|
||||
/// sibling button.
|
||||
/// </summary>
|
||||
public static byte[] BuildBreak(uint gameActionSequence, uint targetGuid)
|
||||
{
|
||||
return Build(gameActionSequence, BreakOpcode, targetGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kick a vassal out of your allegiance. Wire-identical to
|
||||
/// <see cref="BuildBreak"/> — both are <c>Event_BreakAllegiance(u32)
|
||||
/// @0x006A69DA</c> (opcode <c>0x001E</c>); retail's Kick button
|
||||
/// (<c>gmAllegianceUI::CloseKickConfirmationDialog @0x00490B00</c>)
|
||||
/// targets the selected vassal (<c>m_iidPossibleKickedVassal</c>)
|
||||
/// where Break targets <c>AllegianceProfile::GetPatron(self)</c> (lane
|
||||
/// C §1.4). Named separately purely so FA2's panel command surface can
|
||||
/// distinguish "break from patron" from "kick a vassal" without both
|
||||
/// call sites reading like the same action; the golden vector is
|
||||
/// identical to <see cref="BuildBreak"/>'s.
|
||||
/// </summary>
|
||||
public static byte[] BuildKick(uint gameActionSequence, uint vassalGuid)
|
||||
{
|
||||
return Build(gameActionSequence, BreakOpcode, vassalGuid);
|
||||
}
|
||||
|
||||
private static byte[] Build(uint seq, uint sub, uint targetGuid)
|
||||
{
|
||||
byte[] body = new byte[16];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue