acdream/docs/research/deepdives/r08-network-protocol-atlas.md
Erik 3f913f1999 docs+feat: 13 retail-AC deep-dives (R1-R13) + C# port scaffolds + roadmap E-H
78,000 words of grounded, citation-backed research across 13 major AC
subsystems, produced by 13 parallel Opus-4.7 high-effort agents. Plus
compact C# port scaffolds for the top-5 systems and a phase-E-through-H
roadmap update sequencing the work.

Research (docs/research/deepdives/):
- 00-master-synthesis.md          (navigation hub + dependency graph)
- r01-spell-system.md        5.4K words (fizzle sigmoid, 8 tabs, 0x004A wire)
- r02-combat-system.md       5.9K words (damage formula, crit, body table)
- r03-motion-animation.md    8.2K words (450+ commands, 27 hook types)
- r04-vfx-particles.md       5.8K words (13 ParticleType, PhysicsScript)
- r05-audio-sound.md         5.6K words (DirectSound 8, CPU falloff)
- r06-items-inventory.md     7.4K words (ItemType flags, EquipMask 31 slots)
- r07-character-creation.md  6.3K words (CharGen dat, 13 heritages)
- r08-network-protocol-atlas 9.7K words (63+149+94 opcodes mapped)
- r09-dungeon-portal-space.md 6.3K words (EnvCell, PlayerTeleport flow)
- r10-quest-dialogs.md       7.1K words (emote-script VM, 122 actions)
- r11-allegiance.md          5.4K words (tree + XP passup + 5 channels)
- r12-weather-daynight.md    4.5K words (deterministic client-side)
- r13-dynamic-lighting.md    4.9K words (8-light cap, hard Range cutoff)

Every claim cites a FUN_ address, ACE file path, DatReaderWriter type,
or holtburger/ACViewer reference. The master synthesis ties them into a
dependency graph and phase sequence.

Key architectural finding: of 94 GameEvents in the 0xF7B0 envelope,
ZERO are handled today — that's the largest network-protocol gap and
blocks F.2 (items) + F.5 (panels) + H.1 (chat).

C# scaffolds (src/AcDream.Core/):
- Items/ItemInstance.cs    — ItemType/EquipMask enums, ItemInstance,
                             Container, PropertyBundle, BurdenMath
- Spells/SpellModel.cs      — SpellDatEntry, SpellComponentEntry,
                             SpellCastStateMachine, ActiveBuff,
                             SpellMath (fizzle sigmoid + mana cost)
- Combat/CombatModel.cs     — CombatMode/AttackType/DamageType/BodyPart,
                             DamageEvent record, CombatMath (hit-chance
                             sigmoids, power/accuracy mods, damage formula),
                             ArmorBuild
- Audio/AudioModel.cs       — SoundId enum, SoundEntry, WaveData,
                             IAudioEngine / ISoundCache contracts,
                             AudioFalloff (inverse-square)
- Vfx/VfxModel.cs           — 13 ParticleType integrators, EmitterDesc,
                             PhysicsScript + hooks, Particle struct,
                             ParticleEmitter, IParticleSystem contract

All Core-layer data models; platform-backed engines live in AcDream.App.
Compiles clean; 470 tests still pass.

Roadmap (docs/plans/2026-04-11-roadmap.md):
- Phase E — "Feel alive": motion-hooks + audio + VFX
- Phase F — Fight + cast + gear: GameEvent dispatch, inventory,
            combat, spell, core panels
- Phase G — World systems: sky/weather, dynamic lighting, dungeons
- Phase H — Social + progression: chat, allegiance, quests, char creation
- Phase J — Long-tail (renumbered from old Phase E)

Quick-lookup table updated with 10+ new rows mapping observations to
new phase letters.
2026-04-18 10:32:44 +02:00

922 lines
58 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
<payload bytes> // 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. |
| 0x003B0x0042 | 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`. |
| 0x00AA0x00AE | 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 | — |
| 0x02540x0256 | 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 | — |
| 0x02690x026E | 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`. |
| 0x02A00x02A7, 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
<payload bytes> // 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<byte>`
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
│ └── <one file per opcode group>
└── WorldSession.cs // becomes thin, delegates to above
```
Each dispatcher exposes `register<T>(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."