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.
922 lines
58 KiB
Markdown
922 lines
58 KiB
Markdown
# 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. |
|
||
| 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
|
||
<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."
|