# R8 — Network Protocol Atlas **Scope:** exhaustive map of every AC retail network opcode the client must see, send, or at least tolerate. This document is the single reference for every port ticket under phases C, D, E (chat), and F (inventory/combat/ spells). If an opcode is listed here as "unhandled," we already know what it is and what it needs — the only remaining work is wiring up the parser + event surface. **Inputs cross-referenced:** - `references/ACE/Source/ACE.Server/Network/GameMessages/GameMessageOpcode.cs` — canonical S2C primary-opcode enum. - `references/ACE/Source/ACE.Server/Network/GameAction/GameActionType.cs` — canonical C2S GameAction sub-opcode enum. - `references/ACE/Source/ACE.Server/Network/GameEvent/GameEventType.cs` — canonical S2C GameEvent sub-opcode enum. - `references/ACE/Source/ACE.Server/Network/Handlers/*.cs` — handler implementations (what the server accepts, what fields it expects). - `references/Chorizite.ACProtocol/Chorizite.ACProtocol/protocol.xml` — the authoritative XML generated from decompiled client stubs. - `references/holtburger/crates/holtburger-protocol/src/opcodes.rs` — curated "what a real client actually sends / listens for" list, 829 lines, with cross-validated comments. - `docs/research/decompiled/chunk_006B0000.c` (confirmed 0xF7B0 switch). - `src/AcDream.Core.Net/` — what acdream handles today. **How to read the tables:** - "Dir": `C→S` (client to server), `S→C` (server to client), or `bi` (can travel either direction — rare; mostly DDD and TurbineChat). - "Envelope": `GM` = standalone GameMessage (primary opcode at the top of the body), `GA` = client-to-server GameAction inside the 0xF7B1 envelope, `GE` = server-to-client GameEvent inside the 0xF7B0 envelope. Login- phase opcodes use neither envelope (they ride on header flags). - "Status": `done` = full parse/dispatch + event surface in acdream today; `partial` = parsed but not exposed as an event, or built but not generated by player input; `stub` = recognised opcode but body is ignored; `unhandled` = acdream does not parse this opcode at all today. - "Prio": rank for minimum viable play. `P0` blocks login. `P1` blocks basic in-world feedback (chat, health, combat). `P2` blocks inventory/ spells. `P3` is housing, allegiance, fellowship. `P4` is admin, minigames, barber. --- ## 1. Transport layer — recap AC's wire stack is a three-layer stack on top of UDP: ``` UDP datagram ├─ 20-byte PacketHeader src/AcDream.Core.Net/Packets/PacketHeader.cs ├─ N×MessageFragment (0..many, depending on header.Flags) │ ├─ 16-byte MessageFragmentHeader src/AcDream.Core.Net/Packets/MessageFragmentHeader.cs │ └─ fragment payload (≤448 bytes) └─ optional retransmit body (if Retransmission flag set) ``` ### 1.1 PacketHeader (20 bytes, LE) | Off | Name | Type | Notes | |----:|-----------|------|-------| | 0 | Sequence | u32 | Monotonic per direction. `0` = control/handshake packets. | | 4 | Flags | u32 | `PacketHeaderFlags` bitmask (see below). | | 8 | Checksum | u32 | Hash32 of header+body with this field replaced by `0xBADD70DD`. When `EncryptedChecksum` flag is set, XOR with one 32-bit word of ISAAC keystream before transmit. | | 12 | Id | u16 | Session id, assigned in ConnectRequest by the server. | | 14 | Time | u16 | Server-time echo for latency tracking. Clients may send 0. | | 16 | DataSize | u16 | Total body size (excludes this 20-byte header). | | 18 | Iteration | u16 | Retransmit iteration counter. | ### 1.2 PacketHeaderFlags (32-bit bitmask, wire source of truth) | Value | Name | Meaning | |------:|------|---------| | 0x00000001 | Retransmission | Body starts with one u32 retransmit-sequence, then the original body. | | 0x00000002 | EncryptedChecksum | The Checksum field has been XOR'd with the next ISAAC keystream word. | | 0x00000004 | BlobFragments | Body contains one or more MessageFragment blocks. | | 0x00000100 | ServerSwitch | Optional body prefix: `u32 ServerSwitchType` (0=World, 1=Logon). | | 0x00000200 | LogonServerAddr | Body prefix: a sockaddr blob (u16 family, u16 port, u32 IPv4, 8 bytes padding). | | 0x00000400 | EmptyHeader1 | Observed rarely; legacy. | | 0x00000800 | Referral | Body prefix: u64 cookie steering the client to another server. | | 0x00001000 | RequestRetransmit | Body: u32 count + count×u32 sequences to retransmit. | | 0x00002000 | RejectRetransmit | Body: u32 count + count×u32 rejected sequences. | | 0x00004000 | AckSequence | Body: u32 highest-sequence-received. | | 0x00008000 | Disconnect | No body; teardown. | | 0x00010000 | LoginRequest | Body is the LoginRequest blob. | | 0x00020000 | WorldLoginRequest | (unused today, pre-live legacy.) | | 0x00040000 | ConnectRequest | Body: `double serverTime, u64 cookie, u32 clientId, u32 serverSeed, u32 clientSeed, u32 padding`. | | 0x00080000 | ConnectResponse | Body: `u64 cookie` (echo). | | 0x00100000 | NetError | Body: `u32 errorCode`. | | 0x00200000 | NetErrorDisconnect | Body: `u32 errorCode, u32 reason` + disconnect. | | 0x00400000 | CICMDCommand | Legacy admin control channel. | | 0x01000000 | TimeSync | Body: `double clientTime`. | | 0x02000000 | EchoRequest | Body: `float clientTime`. | | 0x04000000 | EchoResponse | Body: `float clientTime, float serverTime`. | | 0x08000000 | Flow | Body: `u32 bytesSinceLast, u16 delta, u16 pad` — TCP-style flow control. | ### 1.3 MessageFragmentHeader (16 bytes, LE) | Off | Name | Type | Notes | |----:|-----------|------|-------| | 0 | Sequence | u32 | Per-message sequence. Outbound messages set the high bit (`0x80000000`). | | 4 | Id | u32 | Logical message id — fragments with the same Id belong to the same logical GameMessage. | | 8 | Count | u16 | Total fragments in this logical message. | | 10 | TotalSize | u16 | Total bytes of this fragment **including** the 16-byte header. Max 464. | | 12 | Index | u16 | 0-based index of this fragment in the logical message. | | 14 | Queue | u16 | `GameMessageGroup` — `Event=1, Control=2, Weenie=3, Logon=4, Database=5, UIQueue=9, SmartBox=10`, etc. | ### 1.4 ISAAC encryption Each direction has its own `IsaacRandom` (ported in `Cryptography/`), seeded from the 4-byte server seed (for the inbound stream) and 4-byte client seed (for the outbound stream) that the server returns in the ConnectRequest handshake. Every packet with `EncryptedChecksum` set XORs the header Checksum field with the next u32 drawn from that direction's ISAAC keystream. Cleartext control packets (ACKs, login, connect response, disconnect) do NOT advance ISAAC. ### 1.5 Ack protocol (source: holtburger `session/receive.rs`) **Every** received server packet whose `Sequence > 0` and that does NOT already carry the `AckSequence` flag gets exactly one ack queued back. This is the "eager ack" pattern — there is no periodic ack timer in the retail client. The ack packet is: ``` PacketHeader { Sequence = last client sequence sent (no increment), Flags = AckSequence, Id = session id, } body = u32 server_sequence_being_acked // cleartext — no ISAAC ``` Failing to ack → ACE emits "Network Timeout" after ~60s. Symptom visible to other clients: character appears as a stationary purple loading haze. ### 1.6 Sequence counters (four flavours, movement-only) Separate from the packet-level Sequence, a player carries four u16 sequence counters that the server uses to detect stale/out-of-order movement messages. These live on `WorldSession` as `_instanceSequence`, `_teleportSequence`, `_serverControlSequence`, `_forcePositionSequence`. They're seeded from the player's own CreateObject and updated whenever an inbound UpdatePosition or PlayerTeleport arrives. Every outbound MoveToState / AutonomousPosition must echo them or ACE silently rejects the message. --- ## 2. GameMessage opcodes (S→C primary) These ride as standalone messages — the first u32 of the fragment body is the opcode itself, followed immediately by the payload. | Hex | Name | Envelope | Dir | Status | Prio | Notes | |----:|------|:--------:|:---:|:------:|:----:|-------| | 0x0024 | InventoryRemoveObject | GM | S→C | unhandled | P2 | `u32 guid` — client-side inventory eviction. | | 0x0197 | SetStackSize | GM | S→C | unhandled | P2 | `u32 guid, u32 newStackSize`. | | 0x019E | PlayerKilled | GM | S→C | unhandled | P1 | `u32 victimGuid, u32 killerGuid, string message`. Triggers the dead-player state. | | 0x01E0 | EmoteText | GM | S→C | unhandled | P1 | `string message` — "The Olthoi growls at you." class feedback. | | 0x01E2 | SoulEmote | GM | S→C | unhandled | P3 | `u32 senderGuid, u32 motion, string text`. | | 0x02BB | HearSpeech | GM | S→C | unhandled | P1 | `string16L text, string16L sender, u32 senderGuid, u32 chatType`. Local-area speech. | | 0x02BC | HearRangedSpeech | GM | S→C | unhandled | P1 | Same layout as HearSpeech but longer radius (shouts). | | 0x02CD | PrivateUpdatePropertyInt | GM | S→C | unhandled | P1 | `u32 guid, u32 propertyId, u32 value` — Only sent to the object's owner. | | 0x02CE | PublicUpdatePropertyInt | GM | S→C | unhandled | P1 | Same as above; sent to everyone who sees the object. | | 0x02CF / 0x02D0 | Private/PublicUpdatePropertyInt64 | GM | S→C | unhandled | P1 | `u32 guid, u32 propId, u64 value`. | | 0x02D1 / 0x02D2 | Private/PublicUpdatePropertyBool | GM | S→C | unhandled | P1 | `u32 guid, u32 propId, u32 value`. | | 0x02D3 / 0x02D4 | Private/PublicUpdatePropertyFloat | GM | S→C | unhandled | P1 | `u32 guid, u32 propId, f64 value`. | | 0x02D5 / 0x02D6 | Private/PublicUpdatePropertyString | GM | S→C | unhandled | P1 | `u32 guid, u32 propId, string16L value`. | | 0x02D7 / 0x02D8 | Private/PublicUpdatePropertyDataID | GM | S→C | unhandled | P2 | `u32 guid, u32 propId, u32 dataId`. | | 0x02D9 / 0x02DA | Private/PublicUpdatePropertyInstanceID | GM | S→C | unhandled | P2 | `u32 guid, u32 propId, u32 iid`. | | 0x02DB / 0x02DC | Private/PublicUpdatePosition | GM | S→C | unhandled | P2 | `u32 guid, u32 propId, PositionPack`. | | 0x02DD / 0x02DE | Private/PublicUpdateSkill | GM | S→C | unhandled | P2 | `u32 guid, u32 skillId, SkillValue`. | | 0x02DF / 0x02E0 | Private/PublicUpdateSkillLevel | GM | S→C | unhandled | P2 | Skill-level only. | | 0x02E3 / 0x02E4 | Private/PublicUpdateAttribute | GM | S→C | unhandled | P1 | Strength/Endurance/etc. | | 0x02E7 / 0x02E8 | Private/PublicUpdateVital | GM | S→C | unhandled | P1 | Max HP/Stam/Mana + regen. | | 0x02E9 | PrivateUpdateAttribute2ndLevel | GM | S→C | unhandled | P1 | Current vital value (the fast-tick channel). | | 0xEA60 | AdminEnvirons | GM | S→C | unhandled | P4 | Admin-tool overlay. | | 0xF619 | PositionAndMovement | GM | S→C | unhandled | P4 | Ghost opcode — declared but never fired by ACE. | | 0xF625 | ObjDescEvent | GM | S→C | unhandled | P1 | `u32 guid, ObjectDescription` — full re-send of visual description (body parts, textures, palettes). Critical for seeing other players' gear changes. | | 0xF643 | CharacterCreateResponse / CharacterRestoreResponse | GM | S→C | unhandled | P0+ | `u32 responseCode` — login-phase, only relevant after char-create. Same opcode, two semantics (disambiguated by session state). | | 0xF653 | CharacterLogOff | GM | bi | partial | P0 | No payload. Client sends before Disconnect to release the character lock immediately. acdream sends it from `Dispose`. | | 0xF655 | CharacterDelete | GM | bi | unhandled | P4 | `u32 slot` — char-select-screen deletion. | | 0xF656 | CharacterCreate | GM | C→S | unhandled | P4 | Full character-creation blob — heritage, gender, starting town, appearance. | | 0xF657 | CharacterEnterWorld | GM | C→S | done | P0 | `u32 characterGuid, string16L account`. Built by `Messages/CharacterEnterWorld.cs`. | | 0xF658 | CharacterList | GM | S→C | done | P0 | `u32 serverName?, u32 numChars, char[] chars, u32 accountSlots, u32 accountGuid`. Parsed in `Messages/CharacterList.cs`. | | 0xF659 | CharacterError | GM | S→C | unhandled | P0 | `u32 errorCode` — login failure enum. We should parse this to give the user a real error instead of a timeout. | | 0xF6EA | ForceObjectDescSend | GM | bi | unhandled | P2 | `u32 guid`. Both directions use the same opcode; C→S asks the server to re-send, S→C pushes a forced refresh. | | **0xF745** | **CreateObject** | GM | S→C | done | P0 | Parser: `Messages/CreateObject.cs` (538 lines). Layout: `u32 guid, WeenieHeader, ObjectDescription, PhysicsData`. Emits `EntitySpawned` event. | | 0xF746 | PlayerCreate | GM | S→C | partial | P0 | Identifies our own character guid. acdream watches for this to send LoginComplete. Does NOT parse the body (it's a full CreateObject-shaped payload — we rely on 0xF745 for that to arrive separately, though retail bundles both). | | 0xF747 | ObjectDelete | GM | S→C | unhandled | P1 | `u32 guid, u16 instanceSequence`. When an object leaves our bubble or is destroyed. Without handling this, stale entities accumulate forever. | | **0xF748** | **UpdatePosition** | GM | S→C | done | P0 | Parser: `Messages/UpdatePosition.cs`. Emits `PositionUpdated` event. | | 0xF749 | ParentEvent | GM | S→C | unhandled | P1 | `u32 parentGuid, u32 childGuid, u32 equipLocation, u32 placementId` — item equipped / child attached. Needed to draw weapons in hands, clothing over armour. | | 0xF74A | PickupEvent | GM | S→C | unhandled | P2 | `u32 guid, u32 animationType` — player picks up an item, animation dispatched. | | 0xF74B | SetState | GM | S→C | unhandled | P1 | `u32 guid, u32 physicsState, u16 instanceSeq, u16 stateSeq` — door opens, chest unlocks, visibility flips. | | **0xF74C** | **UpdateMotion / MovementEvent** | GM | S→C | done | P0 | Parser: `Messages/UpdateMotion.cs`. Emits `MotionUpdated` event. Two names are the same opcode — ACE aliases. | | 0xF74E | VectorUpdate | GM | S→C | unhandled | P1 | `u32 guid, f32 vx, f32 vy, f32 vz, f32 avx, f32 avy, f32 avz, u16 seq, u16 pad` — velocity + angular velocity. Missile tracking, continuous turns. | | 0xF750 | Sound | GM | S→C | unhandled | P2 | `u32 guid, u32 soundId, f32 volume` — spatialised SFX trigger. | | **0xF751** | **PlayerTeleport** | GM | S→C | done | P0 | Parser inline in `WorldSession.cs`. Emits `TeleportStarted` with `u16 teleportSequence`. | | 0xF752 | AutonomyLevel | GM | S→C | unhandled | P1 | `u32 autonomyLevel` — server tells client how much physics trust to grant. Default 0 = full client sim; higher = server authoritative. | | 0xF753 | AutonomousPosition | GM | S→C | unhandled | P1 | Server-forced position resync. Same body as C→S AutonomousPosition (see §3). Causes a snap. | | 0xF754 | PlayScriptId | GM | S→C | unhandled | P2 | `u32 guid, u32 scriptId` — trigger a pre-compiled script effect. | | 0xF755 | PlayEffect / PlayScriptType | GM | S→C | unhandled | P2 | `u32 guid, u32 effectType, f32 scale` — particle/visual overlay. | | **0xF7B0** | **GameEvent** | GM | S→C | unhandled | P1 | The container for every "ordered" server event. Sub-opcode table in §4. | | **0xF7B1** | **GameAction** | GM | C→S | done (partial) | P0 | acdream builds LoginComplete, MoveToState, AutonomousPosition, Jump. Sub-opcode table in §3. | | 0xF7C1 | AccountBanned | GM | S→C | unhandled | P4 | `u32 bannedUntilUnixTime, string16L reason`. | | 0xF7C8 | CharacterEnterWorldRequest | GM | C→S | done | P0 | Built by `Messages/CharacterEnterWorld.cs::BuildEnterWorldRequestBody`. No payload. | | 0xF7C9 | JumpNonAutonomous | GM | bi | unhandled | P4 | Server-side legacy jump; not sent by live ACE. | | 0xF7CA | ReceiveAccountData | GM | S→C | unhandled | P4 | Admin inspection. | | 0xF7CB | ReceivePlayerData | GM | S→C | unhandled | P4 | Admin inspection. | | 0xF7CC | GetServerVersion | GM | C→S | unhandled | P4 | Admin-only. | | 0xF7CD | FriendsOld | GM | C→S | unhandled | P4 | Obsolete. | | 0xF7D9 | CharacterRestore | GM | C→S | unhandled | P4 | Restore a deleted character within grace period. | | 0xF7DB | UpdateObject | GM | S→C | unhandled | P1 | Heavy update — re-serializes the complete WorldObject. Used on gear swaps, morphs, level-ups. | | 0xF7DC | AccountBoot / AccountBooted | GM | S→C | unhandled | P1 | `string16L reason` — server-initiated kick. Should transition to Failed state. | | 0xF7DE | TurbineChat | GM | bi | unhandled | P1 | Turbine's social chat (General/Trade/LFG/Allegiance/Society). Complex nested blob, see §7.4. | | 0xF7DF | CharacterEnterWorldServerReady | GM | S→C | done | P0 | No payload. acdream awaits it in EnterWorld as an ack that the server got our request. | | 0xF7E0 | ServerMessage / TextboxString | GM | S→C | unhandled | P1 | `string16L text, u32 chatType`. System chat line in the main log. | | 0xF7E1 | ServerName / WorldInfo | GM | S→C | unhandled | P0 | `u32 currentConnections, u32 maxConnections, string16L serverName`. Displayed on char-select. | | 0xF7E2 | DDD_DataMessage | GM | bi | unhandled | P4 | Patch-stream file chunk — only triggers when dat versions mismatch. | | 0xF7E3 | DDD_RequestDataMessage | GM | C→S | unhandled | P4 | `u32 fileType, u32 fileId` — client asks for a file. | | 0xF7E4 | DDD_ErrorMessage | GM | S→C | unhandled | P4 | `u32 fileType, u32 fileId, u32 errorCode`. | | **0xF7E5** | **DDD_Interrogation** | GM | S→C | done | P0 | No payload beyond opcode. Server asks "what dat versions do you have?" | | **0xF7E6** | **DDD_InterrogationResponse** | GM | C→S | done | P0 | Built by `Messages/DddInterrogationResponse.cs` — reports language=1 English, zero known lists. | | 0xF7E7 | DDD_BeginDDD | GM | S→C | unhandled | P4 | Server tells client patching will begin; `u32 totalIterations, u64 totalBytes, ...`. | | 0xF7E8 | DDD_BeginPullDDD | GM | S→C | unhandled | P4 | Alternative patch entry. | | 0xF7E9 | DDD_IterationData | GM | S→C | unhandled | P4 | One iteration record during patching. | | 0xF7EA | DDD_EndDDD | GM | bi | unhandled | P4 | Patching finished / client acknowledges. | **Total primary S→C opcodes:** 63. **Currently handled by acdream:** 7 (CharacterList, CharacterEnterWorldServerReady, CreateObject, UpdateMotion, UpdatePosition, PlayerTeleport, DDD_Interrogation) plus the PlayerCreate trigger. **Unhandled:** 56 — many of them property updates that share a single parser. --- ## 3. GameAction sub-opcodes (C→S, inside 0xF7B1 envelope) Wire layout of every GameAction on the outbound side: ``` u32 0xF7B1 // GameMessage opcode (GameAction envelope) u32 sequence // monotonic per-session action counter u32 actionType // one of the values below // variable ``` ACE's `GameActionPacket.HandleGameAction` reads the sequence field and ignores its value (`// TODO: verify sequence` in the source), but live clients still increment it — acdream does too via `WorldSession.NextGameActionSequence()`. | Hex | Name | Dir | Status | Prio | Payload | |----:|------|:---:|:------:|:----:|---------| | 0x0005 | SetSingleCharacterOption | C→S | unhandled | P3 | `u32 optionKey, u32 value`. Appear-offline, show-cloak, etc. | | 0x0008 | TargetedMeleeAttack | C→S | unhandled | P1 | `u32 targetGuid, u32 attackHeight, f32 powerLevel`. | | 0x000A | TargetedMissileAttack | C→S | unhandled | P2 | `u32 targetGuid, u32 attackHeight, f32 accuracyLevel`. | | 0x000F | SetAfkMode | C→S | unhandled | P3 | `u32 enabled`. | | 0x0010 | SetAfkMessage | C→S | unhandled | P3 | `string16L message`. | | 0x0015 | Talk | C→S | unhandled | P1 | `string16L message` — local-area chat. | | 0x0017 | RemoveFriend | C→S | unhandled | P3 | `u32 guid`. | | 0x0018 | AddFriend | C→S | unhandled | P3 | `string16L name`. | | 0x0019 | PutItemInContainer | C→S | unhandled | P2 | `u32 itemGuid, u32 containerGuid, u32 placement`. | | 0x001A | GetAndWieldItem | C→S | unhandled | P2 | `u32 itemGuid, u32 equipLocation`. | | 0x001B | DropItem | C→S | unhandled | P2 | `u32 itemGuid`. | | 0x001D | SwearAllegiance | C→S | unhandled | P3 | `u32 patronGuid`. | | 0x001E | BreakAllegiance | C→S | unhandled | P3 | `u32 otherGuid`. | | 0x001F | AllegianceUpdateRequest | C→S | unhandled | P3 | `u32 onOff`. | | 0x0025 | RemoveAllFriends | C→S | unhandled | P3 | — | | 0x0026 | TeleToPklArena | C→S | unhandled | P3 | — | | 0x0027 | TeleToPkArena | C→S | unhandled | P3 | — | | 0x002C | TitleSet | C→S | unhandled | P3 | `u32 titleId`. | | 0x0030 | QueryAllegianceName | C→S | unhandled | P3 | — | | 0x0031 | ClearAllegianceName | C→S | unhandled | P3 | — | | 0x0032 | TalkDirect | C→S | unhandled | P1 | `string16L target, string16L message` — whisper by location/target. | | 0x0033 | SetAllegianceName | C→S | unhandled | P3 | `string16L name`. | | 0x0035 | UseWithTarget | C→S | unhandled | P2 | `u32 sourceGuid, u32 targetGuid`. | | 0x0036 | Use | C→S | unhandled | P2 | `u32 guid` — click a door, Lifestone, portal, corpse, etc. | | 0x003B–0x0042 | Allegiance officer/chat/house | C→S | unhandled | P3 | Various allegiance admin ops. | | 0x0044 | RaiseVital | C→S | unhandled | P2 | `u32 vital, u64 xpSpent`. | | 0x0045 | RaiseAttribute | C→S | unhandled | P2 | `u32 attr, u64 xpSpent`. | | 0x0046 | RaiseSkill | C→S | unhandled | P2 | `u32 skillId, u64 xpSpent`. | | 0x0047 | TrainSkill | C→S | unhandled | P2 | `u32 skillId, u32 credits`. | | 0x0048 | CastUntargetedSpell | C→S | unhandled | P2 | `u32 spellId`. | | 0x004A | CastTargetedSpell | C→S | unhandled | P2 | `u32 targetGuid, u32 spellId`. | | 0x0053 | ChangeCombatMode | C→S | unhandled | P1 | `u32 combatMode` — Peace/Melee/Missile/Magic. | | 0x0054 | StackableMerge | C→S | unhandled | P2 | `u32 mergeFromGuid, u32 mergeToGuid, u32 amount`. | | 0x0055 | StackableSplitToContainer | C→S | unhandled | P2 | `u32 stackGuid, u32 containerGuid, u32 placement, u32 amount`. | | 0x0056 | StackableSplitTo3D | C→S | unhandled | P2 | `u32 stackGuid, u32 amount`. | | 0x0058 | ModifyCharacterSquelch | C→S | unhandled | P3 | `u32 guid, u32 squelchType, bool add`. | | 0x0059 | ModifyAccountSquelch | C→S | unhandled | P3 | `string16L account, u32 squelchType, bool add`. | | 0x005B | ModifyGlobalSquelch | C→S | unhandled | P3 | `u32 channelId, bool enable`. | | 0x005D | Tell | C→S | unhandled | P1 | `string16L targetName, string16L message` — whisper by name. | | 0x005F | Buy | C→S | unhandled | P2 | `u32 vendorGuid, u32 count, ItemProfile[count]`. | | 0x0060 | Sell | C→S | unhandled | P2 | Same shape. | | 0x0063 | TeleToLifestone | C→S | unhandled | P2 | — | | **0x00A1** | **LoginComplete** | C→S | done | P0 | No payload. Built by `Messages/GameActionLoginComplete.cs`. | | 0x00A2 | FellowshipCreate | C→S | unhandled | P3 | `string16L name, bool openness, bool shareXP`. | | 0x00A3 | FellowshipQuit | C→S | unhandled | P3 | `bool disband`. | | 0x00A4 | FellowshipDismiss | C→S | unhandled | P3 | `u32 guid`. | | 0x00A5 | FellowshipRecruit | C→S | unhandled | P3 | `u32 guid`. | | 0x00A6 | FellowshipUpdateRequest | C→S | unhandled | P3 | `bool open`. | | 0x00AA–0x00AE | Book* | C→S | unhandled | P4 | Book add/modify/delete/query pages. | | 0x00BF | SetInscription | C→S | unhandled | P3 | `u32 itemGuid, string16L text`. | | 0x00C8 | IdentifyObject | C→S | unhandled | P1 | `u32 guid`. Assess target; server replies with 0xF7B0/0x00C9. | | 0x00CD | GiveObjectRequest | C→S | unhandled | P2 | `u32 targetGuid, u32 itemGuid, u32 amount`. | | 0x00D6 | AdvocateTeleport | C→S | unhandled | P4 | Admin. | | 0x0140 | AbuseLogRequest | C→S | unhandled | P4 | — | | 0x0145 | AddChannel | C→S | unhandled | P3 | `string16L channelName`. | | 0x0146 | RemoveChannel | C→S | unhandled | P3 | `string16L channelName`. | | 0x0147 | ChatChannel | C→S | unhandled | P1 | `u32 channelId, string16L message`. | | 0x0148 | ListChannels | C→S | unhandled | P3 | — | | 0x0149 | IndexChannels | C→S | unhandled | P3 | — | | 0x0195 | NoLongerViewingContents | C→S | unhandled | P2 | `u32 containerGuid`. | | 0x019B | StackableSplitToWield | C→S | unhandled | P2 | `u32 stackGuid, u32 equipLocation, u32 amount`. | | 0x019C | AddShortCut | C→S | unhandled | P3 | `u32 slot, u32 objectType, u32 targetId`. | | 0x019D | RemoveShortCut | C→S | unhandled | P3 | `u32 slot`. | | 0x01A1 | SetCharacterOptions | C→S | unhandled | P3 | Client option bitmap. | | 0x01A8 | RemoveSpellC2S | C→S | unhandled | P2 | `u32 spellId`. | | 0x01B7 | CancelAttack | C→S | unhandled | P1 | — | | 0x01BF | QueryHealth | C→S | unhandled | P1 | `u32 guid`. Server replies with GameEvent UpdateHealth (0x01C0). | | 0x01C2 | QueryAge | C→S | unhandled | P4 | — | | 0x01C4 | QueryBirth | C→S | unhandled | P4 | — | | 0x01DF | Emote | C→S | unhandled | P2 | `string16L emote`. | | 0x01E1 | SoulEmote | C→S | unhandled | P3 | `string16L emote`. | | 0x01E3 | AddSpellFavorite | C→S | unhandled | P3 | `u32 spellId, u32 bar, u32 slot`. | | 0x01E4 | RemoveSpellFavorite | C→S | unhandled | P3 | `u32 bar, u32 slot`. | | 0x01E9 | PingRequest | C→S | unhandled | P1 | `u32 clientId`. Server replies with 0xF7B0/0x01EA. Double-duty keepalive. | | 0x01F6 | OpenTradeNegotiations | C→S | unhandled | P3 | `u32 targetGuid`. | | 0x01F7 | CloseTradeNegotiations | C→S | unhandled | P3 | — | | 0x01F8 | AddToTrade | C→S | unhandled | P3 | `u32 itemGuid, u32 slotIndex`. | | 0x01FA | AcceptTrade | C→S | unhandled | P3 | — | | 0x01FB | DeclineTrade | C→S | unhandled | P3 | — | | 0x0204 | ResetTrade | C→S | unhandled | P3 | — | | 0x0216 | ClearPlayerConsentList | C→S | unhandled | P4 | — | | 0x0217 | DisplayPlayerConsentList | C→S | unhandled | P4 | — | | 0x0218 | RemoveFromPlayerConsentList | C→S | unhandled | P4 | `u32 guid`. | | 0x0219 | AddPlayerPermission | C→S | unhandled | P4 | `u32 guid`. | | 0x021A | RemovePlayerPermission | C→S | unhandled | P4 | `u32 guid`. | | 0x021C | BuyHouse | C→S | unhandled | P3 | `u32 slumlordGuid, PackableListU32 allegianceHouseIds`. | | 0x021E | HouseQuery | C→S | unhandled | P3 | `u32 houseGuid`. | | 0x021F | AbandonHouse | C→S | unhandled | P3 | — | | 0x0221 | RentHouse | C→S | unhandled | P3 | `u32 houseGuid, PackableListU32 itemIds`. | | 0x0224 | SetDesiredComponentLevel | C→S | unhandled | P3 | `u32 level`. | | 0x0245 | AddPermanentGuest | C→S | unhandled | P3 | `string16L name`. | | 0x0246 | RemovePermanentGuest | C→S | unhandled | P3 | `string16L name`. | | 0x0247 | SetOpenHouseStatus | C→S | unhandled | P3 | `bool open`. | | 0x0249 | ChangeStoragePermission | C→S | unhandled | P3 | — | | 0x024A | BootSpecificHouseGuest | C→S | unhandled | P3 | `string16L name`. | | 0x024C | RemoveAllStoragePermission | C→S | unhandled | P3 | — | | 0x024D | RequestFullGuestList | C→S | unhandled | P3 | — | | 0x0254–0x0256 | Allegiance MOTD | C→S | unhandled | P3 | — | | 0x0258 | QueryLord | C→S | unhandled | P3 | — | | 0x025C | AddAllStoragePermission | C→S | unhandled | P3 | — | | 0x025E | RemoveAllPermanentGuests | C→S | unhandled | P3 | — | | 0x025F | BootEveryone | C→S | unhandled | P3 | — | | 0x0262 | TeleToHouse | C→S | unhandled | P3 | — | | 0x0263 | QueryItemMana | C→S | unhandled | P2 | `u32 guid`. | | 0x0266 | SetHooksVisibility | C→S | unhandled | P3 | `bool visible`. | | 0x0267 | ModifyAllegianceGuestPermission | C→S | unhandled | P3 | — | | 0x0268 | ModifyAllegianceStoragePermission | C→S | unhandled | P3 | — | | 0x0269–0x026E | Chess | C→S | unhandled | P4 | Chess minigame. | | 0x0270 | ListAvailableHouses | C→S | unhandled | P3 | — | | 0x0275 | ConfirmationResponse | C→S | unhandled | P2 | `u32 confirmationType, u32 contextId, bool accepted`. | | 0x0277 | BreakAllegianceBoot | C→S | unhandled | P3 | `u32 otherGuid, bool accountBoot`. | | 0x0278 | TeleToMansion | C→S | unhandled | P3 | — | | 0x0279 | Suicide | C→S | unhandled | P3 | — | | 0x027B | AllegianceInfoRequest | C→S | unhandled | P3 | `string16L name`. | | 0x027D | CreateTinkeringTool | C→S | unhandled | P2 | `u32 ustGuid, u32 targetGuid`. Salvage-items-with. | | 0x0286 | SpellbookFilter | C→S | unhandled | P3 | Filter bitmap. | | 0x028D | TeleToMarketPlace | C→S | unhandled | P2 | — | | 0x028F | EnterPkLite | C→S | unhandled | P3 | — | | 0x0290 | FellowshipAssignNewLeader | C→S | unhandled | P3 | `u32 guid`. | | 0x0291 | FellowshipChangeOpenness | C→S | unhandled | P3 | `bool open`. | | 0x02A0–0x02A7, 0x02AB | Allegiance admin | C→S | unhandled | P3 | Chat boot / bans / officers. | | 0x02AF, 0x02B2 | Admin plugin query responses | C→S | unhandled | P4 | — | | 0x0311 | FinishBarber | C→S | unhandled | P3 | Full barber blob. | | 0x0316 | AbandonContract | C→S | unhandled | P3 | `u32 contractId`. | | **0xF61B** | **Jump** | C→S | done (partial) | P0 | Built by `Messages/JumpAction.cs`. Payload: `f32 extent, Vector3 jumpVelocity` + position/sequences. | | **0xF61C** | **MoveToState** | C→S | done | P0 | Built by `Messages/MoveToState.cs`. Full RawMotionState + WorldPosition + 4 u16 sequences + contact byte. | | 0xF61E | DoMovementCommand | C→S | unhandled | P4 | Legacy — live client doesn't use this path. | | 0xF649 | TurnTo | C→S | unhandled | P4 | Legacy — unused. | | 0xF661 | StopMovementCommand | C→S | unhandled | P4 | Legacy. | | 0xF6EA | ForceObjectDescSend | C→S | unhandled | P2 | `u32 guid` — ask server to re-emit ObjDescEvent for this entity. Useful for reloading avatar appearance. | | 0xF745 | ObjectCreate | C→S | unhandled | P4 | Legacy admin spawn. | | 0xF747 | ObjectDelete | C→S | unhandled | P4 | Legacy admin despawn. | | 0xF74C | MovementEvent | C→S | unhandled | P4 | Legacy. | | 0xF750 | ApplySoundEffect | C→S | unhandled | P4 | Admin. | | 0xF752 | AutonomyLevel | C→S | unhandled | P2 | `u32 level` — ack the server's autonomy level. | | **0xF753** | **AutonomousPosition** | C→S | done | P0 | Built by `Messages/AutonomousPosition.cs`. Same body as MoveToState but without the motion command list. Heartbeat every ~200ms while moving. | | 0xF755 | ApplyVisualEffect | C→S | unhandled | P4 | Admin. | | 0xF7C9 | JumpNonAutonomous | C→S | unhandled | P4 | Server-controlled jump — client never sends. | **Total GameAction C→S opcodes:** 149. **Currently handled:** 4 (LoginComplete, MoveToState, Jump, AutonomousPosition). **Unhandled:** 145 — but the vast majority are P3/P4 and unblocking only requires 5-8 P1/P2 adds (ChatChannel, Talk, Tell, Use, UseWithTarget, IdentifyObject, QueryHealth, ChangeCombatMode, CastTargetedSpell, CastUntargetedSpell, TargetedMeleeAttack, TargetedMissileAttack, CancelAttack, PingRequest). --- ## 4. GameEvent sub-opcodes (S→C, inside 0xF7B0 envelope) Wire layout of every GameEvent on the inbound side (source: `references/ACE/Source/ACE.Server/Network/GameEvent/GameEventMessage.cs`): ``` u32 0xF7B0 // GameMessage opcode (GameEvent envelope) u32 guid // session's player guid (0 if session has no player yet) u32 gameEventSequence // per-session, incremented by server u32 eventType // one of the values below // variable ``` | Hex | Name | Dir | Status | Prio | Payload | |----:|------|:---:|:------:|:----:|---------| | 0x0003 | AllegianceUpdateAborted | S→C | unhandled | P3 | — | | 0x0004 | PopupString | S→C | unhandled | P1 | `string16L message` — modal dialog. | | 0x0013 | PlayerDescription | S→C | unhandled | P0 | Large blob — full player stats, skills, attributes, vitals, spellbook, inventory, equipment, titles, contracts. Sent right after LoginComplete. Parsing this is the gate to everything UI. | | 0x0020 | AllegianceUpdate | S→C | unhandled | P3 | Allegiance hierarchy delta. | | 0x0021 | FriendsListUpdate | S→C | unhandled | P3 | `u32 listType, u32 count, FriendEntry[count]`. | | 0x0022 | InventoryPutObjInContainer | S→C | unhandled | P2 | `u32 itemGuid, u32 containerGuid, u32 placement`. | | 0x0023 | WieldObject | S→C | unhandled | P1 | `u32 guid, u32 equipLoc, u32 wielderGuid`. | | 0x0029 | CharacterTitle | S→C | unhandled | P3 | — | | 0x002B | UpdateTitle | S→C | unhandled | P3 | `u32 newTitleId`. | | 0x0052 | CloseGroundContainer | S→C | unhandled | P2 | `u32 containerGuid`. | | 0x0062 | ApproachVendor / VendorInfoEvent | S→C | unhandled | P2 | Vendor inventory blob — item list, buy/sell rates. | | 0x0075 | StartBarber | S→C | unhandled | P3 | Barber UI trigger. | | 0x00A0 | InventoryServerSaveFailed | S→C | unhandled | P2 | `u32 guid` — revert a local inventory op. | | 0x00A3 | FellowshipQuit | S→C | unhandled | P3 | Echo. | | 0x00A4 | FellowshipDismiss | S→C | unhandled | P3 | Echo. | | 0x00B4 | BookDataResponse | S→C | unhandled | P4 | Book header + inline pages. | | 0x00B5 | BookModifyPageResponse | S→C | unhandled | P4 | — | | 0x00B6 | BookAddPageResponse | S→C | unhandled | P4 | — | | 0x00B7 | BookDeletePageResponse | S→C | unhandled | P4 | — | | 0x00B8 | BookPageDataResponse | S→C | unhandled | P4 | Text of a single page. | | 0x00C3 | IdentifyObject/GetInscriptionResponse | S→C | unhandled | P3 | `u32 guid, string16L text`. | | 0x00C9 | IdentifyObjectResponse | S→C | unhandled | P1 | Full AppraiseInfo — properties tables keyed by enum. Essential for target info UI. | | 0x0147 | ChannelBroadcast | S→C | unhandled | P1 | `u32 channelId, string16L senderName, string16L message`. | | 0x0148 | ChannelList | S→C | unhandled | P3 | `u32 count, string16L[count] names`. | | 0x0149 | ChannelIndex | S→C | unhandled | P3 | — | | 0x0196 | ViewContents | S→C | unhandled | P2 | `u32 containerGuid, u32 count, (u32 guid, u32 slot)[count]`. | | 0x019A | InventoryPutObjectIn3D | S→C | unhandled | P2 | `u32 guid` — item appears on the ground. | | 0x01A7 | AttackDone | S→C | unhandled | P1 | `u32 attackSequence, u32 weenieError`. | | 0x01A8 | MagicRemoveSpell | S→C | unhandled | P2 | `u32 spellId`. | | 0x01AC | VictimNotification | S→C | unhandled | P1 | `string16L attackerName, u32 attackerGuid, u32 damageType, u32 damage, u32 hitQuadrant, u32 crit, u32 attackType`. | | 0x01AD | KillerNotification | S→C | unhandled | P1 | `string16L victimName, u32 victimGuid`. | | 0x01B1 | AttackerNotification | S→C | unhandled | P1 | `string16L defenderName, u32 damageType, u32 damage, f32 damagePercent`. | | 0x01B2 | DefenderNotification | S→C | unhandled | P1 | `string16L attackerName, u32 attackerGuid, u32 damageType, u32 damage, u32 hitQuadrant, u32 crit`. | | 0x01B3 | EvasionAttackerNotification | S→C | unhandled | P1 | `string16L defenderName`. | | 0x01B4 | EvasionDefenderNotification | S→C | unhandled | P1 | `string16L attackerName`. | | 0x01B8 | CombatCommenceAttack | S→C | unhandled | P1 | No payload. "You swing your weapon." | | 0x01C0 | UpdateHealth | S→C | unhandled | P1 | `u32 targetGuid, f32 healthPercent`. Reply to QueryHealth. | | 0x01C3 | QueryAgeResponse | S→C | unhandled | P4 | — | | 0x01C7 | UseDone | S→C | unhandled | P1 | `u32 weenieError` — the Use event's completion signal. | | 0x01C8 | AllegianceAllegianceUpdateDone | S→C | unhandled | P3 | — | | 0x01C9 | FellowshipFellowUpdateDone | S→C | unhandled | P3 | — | | 0x01CA | FellowshipFellowStatsDone | S→C | unhandled | P3 | — | | 0x01CB | ItemAppraiseDone | S→C | unhandled | P4 | Ghost — never emitted. | | 0x01E2 | Emote | S→C | unhandled | P1 | Same as GM 0x01E0 but GameEvent subclass. | | 0x01EA | PingResponse | S→C | unhandled | P1 | `u32 clientId` echo. | | 0x01F4 | SetSquelchDB | S→C | unhandled | P3 | Squelch list sync. | | 0x01FD | RegisterTrade | S→C | unhandled | P3 | — | | 0x01FE | OpenTrade | S→C | unhandled | P3 | — | | 0x01FF | CloseTrade | S→C | unhandled | P3 | — | | 0x0200 | AddToTrade | S→C | unhandled | P3 | `u32 itemGuid, u32 slotIndex`. | | 0x0201 | RemoveFromTrade | S→C | unhandled | P3 | — | | 0x0202 | AcceptTrade | S→C | unhandled | P3 | `u32 initiatorGuid`. | | 0x0203 | DeclineTrade | S→C | unhandled | P3 | — | | 0x0205 | ResetTrade | S→C | unhandled | P3 | — | | 0x0207 | TradeFailure | S→C | unhandled | P3 | `u32 errorCode`. | | 0x0208 | ClearTradeAcceptance | S→C | unhandled | P3 | — | | 0x021D | HouseProfile | S→C | unhandled | P3 | — | | 0x0225 | HouseData | S→C | unhandled | P3 | — | | 0x0226 | HouseStatus | S→C | unhandled | P3 | — | | 0x0227 | UpdateRentTime | S→C | unhandled | P3 | — | | 0x0228 | UpdateRentPayment | S→C | unhandled | P3 | — | | 0x0248 | HouseUpdateRestrictions | S→C | unhandled | P3 | — | | 0x0257 | UpdateHAR | S→C | unhandled | P3 | — | | 0x0259 | HouseTransaction | S→C | unhandled | P3 | — | | 0x0264 | QueryItemManaResponse | S→C | unhandled | P2 | `u32 itemGuid, f32 manaPercent`. | | 0x0271 | AvailableHouses | S→C | unhandled | P3 | — | | 0x0274 | CharacterConfirmationRequest | S→C | unhandled | P2 | `u32 type, u32 contextId, u32 otherGuid, string16L message`. | | 0x0276 | CharacterConfirmationDone | S→C | unhandled | P2 | `u32 contextId, bool accepted`. | | 0x027A | AllegianceLoginNotification | S→C | unhandled | P3 | — | | 0x027C | AllegianceInfoResponse | S→C | unhandled | P3 | — | | 0x0281 | JoinGameResponse | S→C | unhandled | P4 | Chess/minigame. | | 0x0282 | StartGame | S→C | unhandled | P0 | "World finished loading" signal (duplicate of LoginComplete flow in some clients). | | 0x0283 | MoveResponse | S→C | unhandled | P4 | Minigame. | | 0x0284 | OpponentTurn | S→C | unhandled | P4 | Minigame. | | 0x0285 | OpponentStalemate | S→C | unhandled | P4 | Minigame. | | 0x028A | WeenieError | S→C | unhandled | P1 | `u32 errorCode` — generic game-logic failure (ported from `WeenieError` enum in shared models). | | 0x028B | WeenieErrorWithString | S→C | unhandled | P1 | `u32 errorCode, string16L interpolation`. | | 0x028C | GameOver | S→C | unhandled | P4 | Minigame. | | 0x0295 | SetTurbineChatChannels | S→C | unhandled | P1 | `u32 count, TurbineChatChannelConfig[count]` — configures which chat rooms to show. | | 0x02AE | AdminQueryPluginList | S→C | unhandled | P4 | — | | 0x02B1 | AdminQueryPlugin | S→C | unhandled | P4 | — | | 0x02B3 | AdminQueryPluginResponse | S→C | unhandled | P4 | — | | 0x02B4 | SalvageOperationsResult | S→C | unhandled | P2 | Salvage result blob. | | 0x02BD | Tell | S→C | unhandled | P1 | `string16L message, string16L senderName, u32 senderGuid, u32 targetGuid, u32 chatType`. | | 0x02BE | FellowshipFullUpdate | S→C | unhandled | P3 | — | | 0x02BF | FellowshipDisband | S→C | unhandled | P3 | — | | 0x02C0 | FellowshipUpdateFellow | S→C | unhandled | P3 | Member delta. | | 0x02C1 | MagicUpdateSpell | S→C | unhandled | P2 | `u32 spellId`. Adds spell to spellbook. | | 0x02C2 | MagicUpdateEnchantment | S→C | unhandled | P1 | `Enchantment` blob — buff/debuff on player. | | 0x02C3 | MagicRemoveEnchantment | S→C | unhandled | P1 | `u32 layerId, u32 spellId`. | | 0x02C4 | MagicUpdateMultipleEnchantments | S→C | unhandled | P1 | `u32 count, Enchantment[count]`. | | 0x02C5 | MagicRemoveMultipleEnchantments | S→C | unhandled | P1 | Same shape. | | 0x02C6 | MagicPurgeEnchantments | S→C | unhandled | P1 | — | | 0x02C7 | MagicDispelEnchantment | S→C | unhandled | P1 | Single dispel. | | 0x02C8 | MagicDispelMultipleEnchantments | S→C | unhandled | P1 | Multi dispel. | | 0x02C9 | PortalStormBrewing | S→C | unhandled | P3 | — | | 0x02CA | PortalStormImminent | S→C | unhandled | P3 | — | | 0x02CB | PortalStorm | S→C | unhandled | P3 | — | | 0x02CC | PortalStormSubsided | S→C | unhandled | P3 | — | | 0x02EB | CommunicationTransientString | S→C | unhandled | P1 | `string16L message, u32 chatType` — ticker-style message. | | 0x0312 | MagicPurgeBadEnchantments | S→C | unhandled | P2 | — | | 0x0314 | SendClientContractTrackerTable | S→C | unhandled | P3 | Full contract list. | | 0x0315 | SendClientContractTracker | S→C | unhandled | P3 | Single contract update. | **Total GameEvent opcodes:** 94. **Currently handled:** 0. **Unhandled:** 94 — the 0xF7B0 path is completely absent from acdream today. --- ## 5. Login-phase opcodes ### 5.1 Order (source: `WorldSession.Connect` + `EnterWorld` + holtburger `session/handshake`): ``` C→S: LoginRequest (header flag 0x00010000, custom auth body on port N) S→C: ConnectRequest (header flag 0x00040000, returns seeds + cookie + clientId) ←200ms race delay→ C→S: ConnectResponse (header flag 0x00080000, echo cookie — sent to port N+1) S→C: ServerName (GM 0xF7E1) S→C: CharacterList (GM 0xF658) C→S: CharacterEnterWorldRequest (GM 0xF7C8) S→C: CharacterEnterWorldServerReady (GM 0xF7DF) C→S: CharacterEnterWorld (GM 0xF657) S→C: DDD_Interrogation (GM 0xF7E5) C→S: DDD_InterrogationResponse (GM 0xF7E6) S→C: PlayerCreate (GM 0xF746) + optional UpdateObjects for initial bubble C→S: GameAction(LoginComplete) (GA 0x00A1) S→C: GameEvent(PlayerDescription) (GE 0x0013) — full stat sheet S→C: GameEvent(SetTurbineChatChannels) (GE 0x0295) S→C: many GameMessageCreateObject for the initial bubble ``` ### 5.2 Timing + latency requirements - The **200ms race delay** before ConnectResponse is required because the retail client's UDP flow sends the response before the server has finished allocating the session on port N+1. 200ms is the minimum that reliably works against live ACE. Shorter delays cause intermittent "connection refused" from the kernel. - Every server packet with `Sequence > 0` needs an ack within ~60s regardless of phase (see §1.5). Failing this → `Network Timeout`. - **LoginComplete must follow PlayerCreate, not EnterWorld.** Sending LoginComplete too early keeps the server in a transitional state and other clients see the player as a purple loading haze. - **DDD_InterrogationResponse must be sent promptly** after receiving DDD_Interrogation (within a few hundred ms). If the server decides DATs are out of sync it will boot the account. - If DAT patching is **enabled** on the server, DDD_Interrogation → DDD_BeginDDD → multiple DDD_DataMessage → DDD_EndDDD can push megabytes of data before LoginComplete is viable. --- ## 6. In-world opcodes — functional groups ### 6.1 Movement (the hot path) | Opcode | Dir | Cadence | Notes | |:------:|:---:|:-------:|-------| | 0xF61C MoveToState | C→S | On key state change | Sent when the motion command set changes (start/stop walking, sidestep, turn). Full RawMotionState, position, rotation, sequences, contact byte. | | 0xF753 AutonomousPosition | C→S | Every ~200ms while moving | Heartbeat confirming the client is still at position X. No motion commands — just position + sequences. | | 0xF61B Jump | C→S | On jump key | Same structure as MoveToState but with `f32 extent, Vector3 velocity` prepended. | | 0xF748 UpdatePosition | S→C | Per-entity, variable | Every entity in the bubble gets these as they move. We parse + fire `PositionUpdated`. | | 0xF74C UpdateMotion | S→C | On motion change | NPC starts walking, creature enters combat, door opens, etc. | | 0xF74E VectorUpdate | S→C | Per-tick for flying objects | Missiles, projectiles, continuous-turn casts. Unhandled today. | | 0xF751 PlayerTeleport | S→C | On teleport | Player is being moved through portal space. acdream fires `TeleportStarted` and re-sends LoginComplete at destination. | | 0xF753 AutonomousPosition | S→C | On server-forced snap | When the server rejects a client-reported position (collision fail, cheat detection), it pushes this to snap the client back. | ### 6.2 Object lifecycle | Opcode | Dir | Notes | |:------:|:---:|-------| | 0xF745 CreateObject | S→C | Initial spawn of anything visible. 538-line parser in `Messages/CreateObject.cs`. | | 0xF746 PlayerCreate | S→C | Special-case of CreateObject for our own player. Same body shape; different semantic. Used as the LoginComplete trigger. | | 0xF747 ObjectDelete | S→C | `u32 guid, u16 seq`. **Unhandled** — this is the #1 "must add" gap after chat. Without it, stale entities pile up. | | 0xF7DB UpdateObject | S→C | Full re-serialize. Used for gear/appearance changes, morphs. | | 0xF625 ObjDescEvent | S→C | Visual-description-only re-send. Lighter than UpdateObject. | | 0xF6EA ForceObjectDescSend | bi | Client can C→S to refresh; server sends S→C to force. | ### 6.3 Chat (primary channel) | Opcode | Dir | Notes | |:------:|:---:|-------| | GA 0x0015 Talk | C→S | Local area say. | | GA 0x005D Tell | C→S | Whisper by name. | | GA 0x0032 TalkDirect | C→S | Whisper by target. | | GA 0x0147 ChatChannel | C→S | Send to named channel. | | GA 0x01DF Emote | C→S | /e text. | | GM 0x02BB HearSpeech | S→C | Nearby chat. | | GM 0x02BC HearRangedSpeech | S→C | Shouts. | | GM 0x01E0 EmoteText | S→C | Environmental emote. | | GE 0x02BD Tell | S→C | Whisper inbound. | | GE 0x0147 ChannelBroadcast | S→C | Custom channel line. | | GM 0xF7E0 ServerMessage | S→C | System chat. | | GM 0xF7DE TurbineChat | bi | Turbine's social chat (General/Trade/LFG/Allegiance/Society). Nested binary blob — non-trivial parser. | | GE 0x0295 SetTurbineChatChannels | S→C | Configure Turbine chat rooms on login. | ### 6.4 Combat | Opcode | Dir | Notes | |:------:|:---:|-------| | GA 0x0008 TargetedMeleeAttack | C→S | `targetGuid, attackHeight, powerLevel`. | | GA 0x000A TargetedMissileAttack | C→S | `targetGuid, attackHeight, accuracyLevel`. | | GA 0x01B7 CancelAttack | C→S | — | | GA 0x0053 ChangeCombatMode | C→S | Peace/Melee/Missile/Magic. | | GA 0x01BF QueryHealth | C→S | Request health % for a target. | | GE 0x01C0 UpdateHealth | S→C | Reply to QueryHealth; `f32 healthPercent`. | | GE 0x01A7 AttackDone | S→C | End-of-swing. | | GE 0x01AC VictimNotification | S→C | You took damage. | | GE 0x01AD KillerNotification | S→C | You killed X. | | GE 0x01B1 AttackerNotification | S→C | You did damage. | | GE 0x01B2 DefenderNotification | S→C | You were attacked. | | GE 0x01B3 EvasionAttackerNotification | S→C | Target dodged you. | | GE 0x01B4 EvasionDefenderNotification | S→C | You dodged. | | GE 0x01B8 CombatCommenceAttack | S→C | Swing-start flag. | | GM 0x019E PlayerKilled | S→C | Fatal blow. | ### 6.5 Spells & enchantments | Opcode | Dir | Notes | |:------:|:---:|-------| | GA 0x0048 CastUntargetedSpell | C→S | `u32 spellId`. | | GA 0x004A CastTargetedSpell | C→S | `u32 targetGuid, u32 spellId`. | | GA 0x01A8 RemoveSpellC2S | C→S | Un-learn a spell. | | GA 0x01E3/0x01E4 Add/RemoveSpellFavorite | C→S | Hotbar slot management. | | GE 0x02C1 MagicUpdateSpell | S→C | Add/refresh a spellbook entry. | | GE 0x02C2 MagicUpdateEnchantment | S→C | Buff/debuff applied. | | GE 0x02C3 MagicRemoveEnchantment | S→C | Buff expired/dispelled. | | GE 0x02C4/0x02C5 MagicUpdate/RemoveMultiple | S→C | Batch variants. | | GE 0x02C6 MagicPurgeEnchantments | S→C | Full clear. | | GE 0x0312 MagicPurgeBadEnchantments | S→C | Clear debuffs only. | | GE 0x02C7/0x02C8 MagicDispel[Multiple]Enchantment | S→C | Dispelled-by-player. | | GE 0x01A8 MagicRemoveSpell | S→C | Spell removed from book. | ### 6.6 Inventory & items | Opcode | Dir | Notes | |:------:|:---:|-------| | GA 0x0019 PutItemInContainer | C→S | `itemGuid, containerGuid, placement`. | | GA 0x001A GetAndWieldItem | C→S | Pick up and equip in one step. | | GA 0x001B DropItem | C→S | — | | GA 0x0036 Use | C→S | — | | GA 0x0035 UseWithTarget | C→S | — | | GA 0x00C8 IdentifyObject | C→S | — | | GA 0x0054/0x0055/0x0056 Stackable\* | C→S | Split / merge. | | GA 0x005F/0x0060 Buy/Sell | C→S | — | | GE 0x0022 InventoryPutObjInContainer | S→C | — | | GE 0x0023 WieldObject | S→C | — | | GE 0x019A InventoryPutObjectIn3D | S→C | Dropped to ground. | | GE 0x0196 ViewContents | S→C | Container list. | | GE 0x00C9 IdentifyObjectResponse | S→C | Appraise reply. | | GE 0x0062 ApproachVendor | S→C | Vendor inventory. | | GM 0xF74A PickupEvent | S→C | Pickup animation flag. | | GM 0x0024 InventoryRemoveObject | S→C | — | | GM 0x0197 SetStackSize | S→C | — | | GM 0xF749 ParentEvent | S→C | Equip / attach. | --- ## 7. Frequency / priority | Rank | Rate (approx, with 10 other players nearby) | Opcodes | Why | |:----:|---------------------------------------------|---------|-----| | **Very hot** | 2-5 Hz per player, per creature | UpdatePosition, UpdateMotion, AutonomousPosition (both directions) | Physics sync. Must not allocate on hot path. | | **Hot** | ~0.5 Hz per player | VectorUpdate, MoveToState | Movement delta. | | **Warm** | ~1 msg every few seconds | HearSpeech, ChannelBroadcast, Tell, EmoteText, UpdateHealth, attack notifications | Chat + combat feedback. | | **Cool** | ~1 msg / min | ObjectCreate bursts on bubble enter, ObjectDelete on bubble exit, UpdateObject on gear swap | Bubble churn. | | **Cold** | Once per session or event | CharacterList, DDD, LoginComplete, PlayerDescription, SetTurbineChatChannels, PlayerKilled, PlayerTeleport | Login + rare triggers. | | **Background** | Driven by player action | Use, IdentifyObject, Buy, PingRequest, spell casting | Player explicit. | Planning implication: the top two tiers (physics sync) must be zero-allocation parses. acdream's current `CreateObject.TryParse` and `UpdatePosition.TryParse` already are — they use `ReadOnlySpan` and `BinaryPrimitives`. New parsers should match that pattern. --- ## 8. Gap analysis — what's missing for minimum viable play Grouped by how much they unblock: ### 8.1 P0 / session health (must-have for any session to survive) - **0xF747 ObjectDelete** — without this, stale entities pile up forever. High impact, ~30-line parser, emit `EntityDespawned` event. - **0xF752 AutonomyLevel (S→C)** — parse and store. ACE live sends this regularly; ignoring it is fine today but we should not crash on unknown payloads. - **0xF7DC AccountBoot** — transition to `Failed` with the server's reason string instead of a timeout. - **0xF659 CharacterError** — surface login failures as errors rather than a CharacterList timeout. ### 8.2 P1 / basic in-world feedback (first thing a user will miss) - **Chat stack**: 0x02BB HearSpeech, 0x02BC HearRangedSpeech, 0x01E0 EmoteText, 0xF7E0 ServerMessage, 0xF7B0/0x0147 ChannelBroadcast, 0xF7B0/0x02BD Tell, 0xF7B0/0x02EB CommunicationTransientString, 0xF7B0/0x0004 PopupString. Plus outbound 0xF7B1/0x0015 Talk, /0x005D Tell, /0x0147 ChatChannel, /0x01DF Emote. - **Property updates (0x02CD..0x02EA)**: 22 opcodes, but they all share a compact pattern — `u32 guid, u32 propId, value`. One generic parser + 8 value-type branches covers everything. Fires per-property events. - **GameEvent envelope (0xF7B0)**: must ship the dispatcher skeleton before any GameEvent can be handled. - **Combat events (0x01AC..0x01B8)**: receiving only; one parser per message. Lets floating combat text + hit indicators work. - **0x02C2/0x02C4 Magic[Multiple]UpdateEnchantment + 0x02C3/0x02C5 remove**: buff/debuff HUD. - **0xF625 ObjDescEvent** + **0xF7DB UpdateObject**: for seeing other players change gear. Critical for multiplayer immersion. - **GameEvent PlayerDescription (0x0013)** — initial character sheet. Gate for inventory/spellbook UI. Biggest message in the protocol — ~30 KB of interleaved property tables. ### 8.3 P2 / inventory, assess, vendor - 0x00C9 IdentifyObjectResponse + GA 0x00C8 IdentifyObject. - Full inventory flow: GA 0x0019, 0x001A, 0x001B, 0x0054-0x0056, and the mirror GameEvents 0x0022, 0x0023, 0x019A, 0x0196. - GE 0x0062 ApproachVendor + GA 0x005F Buy / 0x0060 Sell. - GA 0x0036 Use + 0x0035 UseWithTarget + GE 0x01C7 UseDone. ### 8.4 P3 / social, housing, allegiance Large opcode count but highly modular — each subsystem is ~15-20 opcodes. Skip until post-beta. ### 8.5 P4 / admin, minigames, barber, char-create Zero visibility impact for combat-focused play. Park. --- ## 9. Port plan — where the WorldSession dispatcher needs to expand ### 9.1 Refactor the dispatcher skeleton first (phase D prerequisite) Today `WorldSession.ProcessDatagram` is a long `if/else` ladder. Before adding more handlers, split it into a dispatcher class. Layout: ``` AcDream.Core.Net/ ├── Messages/ │ ├── GameMessageDispatcher.cs // top-level opcode → parser router │ ├── GameEventDispatcher.cs // inside 0xF7B0 │ ├── GameActionBuilder.cs // fluent builder for 0xF7B1 envelope │ └── └── WorldSession.cs // becomes thin, delegates to above ``` Each dispatcher exposes `register(opcode, parser)` so phase D can bolt on new handlers without modifying the dispatcher class body. ### 9.2 Phase D — chat + session health Scope: - Add the GameEvent envelope dispatcher (0xF7B0). - Ship ObjectDelete (0xF747), AutonomyLevel (0xF752), AccountBoot (0xF7DC), CharacterError (0xF659). - Ship the chat group (11 opcodes) + outbound Talk/Tell/ChatChannel. - Ship PopupString, CommunicationTransientString, SetTurbineChatChannels as UI sidechannels. - Add an `IChatSurface` event set on WorldSession: `Said`, `Whispered`, `ChannelMessage`, `SystemMessage`, `Popup`, `Transient`. Acceptance: log in, receive and post chat, see server announcements, get kicked gracefully. ### 9.3 Phase E — property & stats Scope: - Ship the 22 property-update opcodes behind one generic `PropertyUpdateParser` keyed on PropertyId. - Ship PlayerDescription (GE 0x0013) — the single biggest parser job. - Ship UpdateHealth (GE 0x01C0), QueryHealth (GA 0x01BF). - Ship ObjDescEvent (0xF625), UpdateObject (0xF7DB). - Expose `IPlayer` interface that plugins can observe: `Attributes, Skills, Vitals, Inventory, Spellbook`. Acceptance: stat sheet renders correctly, health bars update live, can assess nearby NPCs. ### 9.4 Phase F — combat + spells Scope: - Ship combat notifications (0x01AC..0x01B8). - Ship attack actions (GA 0x0008, 0x000A, 0x01B7, 0x0053). - Ship enchantments (GE 0x02C1..0x02C8, 0x0312). - Ship cast actions (GA 0x0048, 0x004A). Acceptance: can kill a lurker, see float text + health, land a self-buff and watch it tick. ### 9.5 Phase G — inventory Scope: - Ship the GA/GE inventory families. - Ship ApproachVendor + Buy/Sell. - Ship PickupEvent + ParentEvent. Acceptance: loot a drudge, sell to vendor, equip a weapon, see it render on the avatar. ### 9.6 Deferred: housing, allegiance, fellowship, books, barber, chess All the P3/P4 tails. Ship when we need them, not before. --- ## 10. Collision + sub-type notes (the ultrathink) - **0xF643 CharacterCreateResponse vs CharacterRestoreResponse.** Same opcode, two meanings. Disambiguated entirely by session state — if the client just sent 0xF656 CharacterCreate the reply is a create response; if it just sent 0xF7D9 CharacterRestore it's a restore response. Parsers must key on context, not opcode. ACE has a duplicate entry in the enum (`CharacterRestoreResponse = 0xF643, // This is a duplicate...`). Holtburger keeps them combined under `CharacterCreateResponse`. - **0xF74C: MovementEvent and Motion are the same opcode.** ACE aliases `Motion = 0xF74C` and `MovementEvent = 0xF74C` — semantically identical, just two names. Our `UpdateMotion.cs` already handles this. - **0xF753 AutonomousPosition goes both ways** with the **same body layout** but different semantics. Outbound = "I'm still here"; inbound = "no you're not, here's where you really are." Parser can be shared; the client just needs to know direction to choose the right side-effect (`PositionUpdated` vs `ForcedResync`). - **0x0147 ChatChannel vs ChannelBroadcast.** C→S uses 0x0147 inside GameAction (envelope 0xF7B1); S→C uses 0x0147 inside GameEvent (envelope 0xF7B0). Same wire opcode, different parser because different envelope. This is true for many ID collisions — the envelope disambiguates them. - **0x0148/0x0149 Channel List/Index** — same C→S/S→C pairing pattern. - **0x00A3/0x00A4 FellowshipQuit/Dismiss** — same pairing; C→S in GameAction, S→C in GameEvent. - **0xF653 CharacterLogOff** — bi-directional, same opcode, no envelope. Client sends to request logout; server sends the echo before Disconnect. - **0x01A8 MagicRemoveSpell** — C→S is GameAction, S→C is GameEvent. - **0xF7DE TurbineChat** — bi-directional GameMessage. Blob layout includes a `ChatNetworkBlobType` discriminant (EVENT_BINARY=1, REQUEST_BINARY=3, RESPONSE_BINARY=5) + a `ChatNetworkBlobDispatchType` sub-discriminant (SendToRoomByID=1/2). Effectively two opcodes (event vs request/response) multiplexed on one wire code. Nested "bytes to follow" size prefixes must be back-patched after payload writes — non-trivial parser. - **0xF6EA ForceObjectDescSend** — bi-directional with identical `u32 guid` body. The side-effect differs: server takes it as "resend to me"; client takes it as "apply a fresh ObjDescEvent I'm about to receive." --- ## 11. Source of truth ranking (per opcode category) When the references disagree, use this priority: - **PacketHeader + Flags + encryption**: ACE and holtburger agree on all bits. No disputes. - **C→S GameAction opcodes**: holtburger's `opcodes.rs` is the most curated list of what a real client actually sends. ACE's `GameActionType.cs` is the full universe including things only legacy admin tools ever sent. Use holtburger when in doubt about which GAs are relevant; use ACE when you need the wire layout of a specific handler. - **S→C GameMessage opcodes**: ACE's `GameMessageOpcode.cs` is the canonical enum; Chorizite's XML adds field-level comments that are sometimes more accurate than ACE's implementation (remember "If it is 0, it defaults to 256*8"). Use ACE for structure; use Chorizite to cross-check specific field semantics. - **S→C GameEvent opcodes**: ACE's `GameEventType.cs` + the per-event classes in `Events/` are the wire ground truth. - **RawMotionState wire**: holtburger `types.rs` is the only source that documents every flag bit clearly; ACE has the same structure but embedded in `MovementData.Pack`. - **PositionPack (for UpdatePosition)**: ACE's `PositionPack.Write` is the canonical serializer; holtburger matches bit-for-bit. - **TurbineChat nested blob**: ACE's `GameMessageTurbineChat` has a large comment block documenting every field layout. Holtburger's implementation matches. --- ## 12. Quick-reference: what to add first if you have one afternoon Pick **four** opcodes from the P0/P1 tier and you have a meaningfully more useful client by end-of-day: 1. **0xF747 ObjectDelete** — stop leaking entities (20 lines). 2. **0xF7B0 GameEvent envelope skeleton** — unlocks the whole event category (50 lines dispatcher). 3. **0x02BB HearSpeech** + **0x02BC HearRangedSpeech** — see world chat (same parser, different trigger; 30 lines each). 4. **0xF7E0 ServerMessage** — see system chat (20 lines). That's ~150 lines of parser code and acdream graduates from "renders the world" to "reads the world's feedback."