using System; using System.Collections.Generic; using System.Linq; using AcDream.Core.Net.Packets; using Xunit; namespace AcDream.Core.Net.Tests.Packets; /// /// Value-conformance for the transport flag word against ACE's /// Source/ACE.Server/Network/PacketHeaderFlags.cs, read 2026-07-29 /// during the wire-stack audit. /// /// These are wire-format facts, not choices: a flag bit is the gate for /// an optional-header section, so a single wrong value silently shifts every /// subsequent section's offset and corrupts the packet checksum. The enum is /// small enough to pin exhaustively, and doing so makes any future edit a /// deliberate act rather than a typo. /// /// The audit found acdream's enum a 23-of-23 match with ACE's, including /// the sparse gaps (nothing between 0x04 and 0x100, nothing between 0x00800000 /// and 0x01000000). This test freezes that result. /// public class PacketHeaderFlagsConformanceTests { /// /// Transcribed from ACE's PacketHeaderFlags enum, in declaration order. /// The trailing comments are ACE's own. /// private static readonly (string Name, uint Value)[] AceFlags = { ("None", 0x00000000u), ("Retransmission", 0x00000001u), ("EncryptedChecksum", 0x00000002u), // can't be paired with 0x1 ("BlobFragments", 0x00000004u), ("ServerSwitch", 0x00000100u), ("LogonServerAddr", 0x00000200u), ("EmptyHeader1", 0x00000400u), ("Referral", 0x00000800u), ("RequestRetransmit", 0x00001000u), // Nak ("RejectRetransmit", 0x00002000u), // Empty Ack ("AckSequence", 0x00004000u), // Pak ("Disconnect", 0x00008000u), // Empty Header 2 ("LoginRequest", 0x00010000u), ("WorldLoginRequest", 0x00020000u), ("ConnectRequest", 0x00040000u), ("ConnectResponse", 0x00080000u), ("NetError", 0x00100000u), ("NetErrorDisconnect", 0x00200000u), ("CICMDCommand", 0x00400000u), ("TimeSync", 0x01000000u), ("EchoRequest", 0x02000000u), ("EchoResponse", 0x04000000u), ("Flow", 0x08000000u), }; public static TheoryData Flags() { var data = new TheoryData(); foreach ((string name, uint value) in AceFlags) data.Add(name, value); return data; } [Theory] [MemberData(nameof(Flags))] public void Flag_MatchesAceValue(string name, uint aceValue) { Assert.True( Enum.IsDefined(typeof(PacketHeaderFlags), aceValue) || aceValue == 0, $"ACE declares {name} = 0x{aceValue:X8}; acdream has no such value."); object parsed = Enum.Parse(typeof(PacketHeaderFlags), name); Assert.Equal(aceValue, (uint)(PacketHeaderFlags)parsed); } [Fact] public void EnumHasNoMembersAceDoesNotDeclare() { HashSet ace = AceFlags.Select(f => f.Name).ToHashSet(StringComparer.Ordinal); string[] ours = Enum.GetNames(typeof(PacketHeaderFlags)); string[] extra = ours.Where(n => !ace.Contains(n)).ToArray(); Assert.True( extra.Length == 0, $"acdream declares flags ACE does not: {string.Join(", ", extra)}"); Assert.Equal(AceFlags.Length, ours.Length); } }