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>
91 lines
3.1 KiB
C#
91 lines
3.1 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
using AcDream.Core.Net.Messages;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Net.Tests.Messages;
|
|
|
|
public sealed class AllegianceRequestsTests
|
|
{
|
|
[Fact]
|
|
public void BuildSwear_EncodesOpcodeAndTarget()
|
|
{
|
|
byte[] body = AllegianceRequests.BuildSwear(gameActionSequence: 3, patronGuid: 0xAAAAu);
|
|
|
|
Assert.Equal(16, body.Length);
|
|
Assert.Equal(AllegianceRequests.GameActionEnvelope,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body));
|
|
Assert.Equal(3u,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4)));
|
|
Assert.Equal(AllegianceRequests.SwearOpcode,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
|
Assert.Equal(0xAAAAu,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildBreak_EncodesOpcodeAndTarget()
|
|
{
|
|
byte[] body = AllegianceRequests.BuildBreak(gameActionSequence: 5, targetGuid: 0xBBBBu);
|
|
Assert.Equal(AllegianceRequests.BreakOpcode,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
|
Assert.Equal(0xBBBBu,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
|
|
}
|
|
|
|
// FA1: golden byte vectors, hand-computed from lane C §3.2
|
|
// (`CM_Allegiance::Event_SwearAllegiance`/`Event_BreakAllegiance`),
|
|
// not generated by calling the builder under test.
|
|
[Fact]
|
|
public void BuildSwear_GoldenByteVector()
|
|
{
|
|
byte[] body = AllegianceRequests.BuildSwear(gameActionSequence: 3, patronGuid: 0xAAAAu);
|
|
|
|
byte[] expected =
|
|
[
|
|
0xB1, 0xF7, 0x00, 0x00, // envelope 0xF7B1
|
|
0x03, 0x00, 0x00, 0x00, // seq 3
|
|
0x1D, 0x00, 0x00, 0x00, // opcode 0x001D
|
|
0xAA, 0xAA, 0x00, 0x00, // targetGuid 0xAAAA
|
|
];
|
|
|
|
Assert.Equal(expected, body);
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildBreak_GoldenByteVector()
|
|
{
|
|
byte[] body = AllegianceRequests.BuildBreak(gameActionSequence: 5, targetGuid: 0xBBBBu);
|
|
|
|
byte[] expected =
|
|
[
|
|
0xB1, 0xF7, 0x00, 0x00, // envelope 0xF7B1
|
|
0x05, 0x00, 0x00, 0x00, // seq 5
|
|
0x1E, 0x00, 0x00, 0x00, // opcode 0x001E
|
|
0xBB, 0xBB, 0x00, 0x00, // targetGuid 0xBBBB
|
|
];
|
|
|
|
Assert.Equal(expected, body);
|
|
}
|
|
|
|
// New builder — BuildKick is BuildBreak's opcode with a vassal target
|
|
// (lane C §1.4). Same golden vector shape as BuildBreak, proving the
|
|
// wire is identical while the call site reads distinctly for FA2.
|
|
[Fact]
|
|
public void BuildKick_GoldenByteVector_SameShapeAsBreak()
|
|
{
|
|
byte[] body = AllegianceRequests.BuildKick(gameActionSequence: 6, vassalGuid: 0x50000042u);
|
|
|
|
byte[] expected =
|
|
[
|
|
0xB1, 0xF7, 0x00, 0x00, // envelope 0xF7B1
|
|
0x06, 0x00, 0x00, 0x00, // seq 6
|
|
0x1E, 0x00, 0x00, 0x00, // opcode 0x001E — SAME as Break
|
|
0x42, 0x00, 0x00, 0x50, // vassalGuid 0x50000042
|
|
];
|
|
|
|
Assert.Equal(expected, body);
|
|
Assert.Equal(AllegianceRequests.BreakOpcode,
|
|
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
|
|
}
|
|
}
|