using AcDream.Core.Net.Packets; namespace AcDream.Core.Net.Messages; /// /// Outbound GameAction message announcing that the client has /// finished loading into the world. Without this, the server keeps the /// character in a transitional "exiting portal space" state forever — /// other players see the character rendered as a stationary purple haze /// (AC's loading-screen indicator) instead of a real avatar, and the /// server doesn't push initial property updates / equipment overrides /// to the client either. /// /// /// Wire layout (see /// references/ACE/Source/ACE.Server/Network/GameAction/GameActionPacket.cs /// and references/ACE/Source/ACE.Server/Network/GameAction/Actions/GameActionLoginComplete.cs): /// /// /// u32 game-message opcode = 0xF7B1 (GameAction) /// u32 sequence — ACE's GameActionPacket.HandleGameAction /// reads this and currently ignores its value (// TODO: verify /// sequence in the source). 0 is fine. /// u32 GameActionType opcode = 0x000000A1 (LoginComplete) /// (no payload) /// /// /// /// Retail clients send it once the portal-space transition animation finishes. /// acdream's F751 teleport path now does the same through /// TeleportAnimSequencer.FireLoginComplete. Initial session bootstrap /// still sends from the PlayerCreate handler; that remaining ordering gap is /// tracked as TS-28 in the divergence register. /// /// public static class GameActionLoginComplete { public const uint GameActionOpcode = 0xF7B1u; public const uint LoginCompleteActionType = 0x000000A1u; /// /// Build the body bytes for an outbound GameAction(LoginComplete). /// Layout: opcode(4) + sequence(4) + actionType(4) = 12 bytes total. /// public static byte[] Build() { var w = new PacketWriter(16); w.WriteUInt32(GameActionOpcode); w.WriteUInt32(0u); // sequence — server ignores per ACE source w.WriteUInt32(LoginCompleteActionType); return w.ToArray(); } }