acdream/docs/research/2026-07-29-wire-stack-audit.md
Erik 081c2009d1 docs(net): the wire-stack audit, all three hundred forty-nine opcodes
Bucket B item 2 of the post-Campaign-V intake, delivered as a ledger. Every
entry in ACE's three opcode enums gets a row grounded in acdream source read
during the audit, with a file:line citation, compared field by field against
ACE's serializer or handler. The prior status columns were deliberately not
used as input; they are compared against the result instead.

Two blockers lead the document because they outlast it. The vendored reference
repos are empty directories in the main checkout, not just in worktrees, so
CLAUDE.md's instruction to cross-reference at least two of them is currently
unexecutable as written; working copies were recovered elsewhere on disk and
Chorizite.ACProtocol was not found anywhere. And the 2026-06-04 wire catalog
this audit was meant to diff against was never committed and does not exist on
disk, though MEMORY.md still links to it. The row universe therefore comes from
ACE's enums, which is more complete than the missing catalog's 256, and drift
is measured against the in-repo Phase-M matrix instead.

The corpus could not support the frequency ranking the mission asked for.
ACDREAM_DUMP_OPCODES was not set for any recorded session, so there are zero
unhandled-opcode lines across every log. That is reported rather than papered
over, and it is why no new parsers were written for unobserved gaps: the bar
was observed plus two oracles, and the first half was unsatisfiable. One
instrumented re-run would fix this, and it leads the open questions.

The most useful thing the audit found is a pattern rather than a single bug.
Three confirmed wire divergences each had a passing test built on the same
wrong assumption as the parser it was defending. The Phase-M matrix had marked
all three rows complete, and for ranged speech it even recorded the mistake as
a design note, "same parser as 0x02BB". A status column can be wrong on the day
it is written.

Counts: 130 parsed or built-and-wired, 11 partial, 75 stub or dead-builder, 128
missing, 5 server-side, across 349 rows. The stub block is concentrated in
fellowship, housing, trade, allegiance, and vendor, which is exactly the surface
the intake's bucket-C items will need.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 02:05:12 +02:00

138 KiB
Raw Blame History

Wire-stack audit — 2026-07-29

Bucket B item 2 of docs/plans/2026-07-29-post-vulkan-work-intake.md: "Verify networking stack — audit parsed-vs-stub-vs-missing, prioritize by what live servers actually send, holtburger remains the client-side oracle."

Base commit: b70b9832 (docs: capture the post-Campaign-V work intake, sorted), the newest reachable commit of claude/git-sync-status-5fb1d2. Baseline suite at base: 9,162 passed / 5 skipped / 0 failed (AcDream.Core.Net.Tests = 600).

This audit is report-first. The ledger is the deliverable; parser work is bonus and was held to a deliberately high bar (below).


0. Two blockers found before any auditing could start

Both are process findings that outlast this audit, so they lead.

0.1 The vendored reference repos are empty

references/ACE/, references/holtburger/, and references/Chorizite.ACProtocol/ are empty directories — not just inside agent worktrees, but in the main checkout at C:\Users\erikn\source\repos\acdream\references\. Only references/WorldBuilder is a real submodule (.gitmodules declares exactly one). Every CLAUDE.md instruction of the form "cross-reference at least two of them" is currently unexecutable as written.

Working copies were recovered elsewhere on disk and used for this audit:

Oracle Recovered path Extent
ACE C:\Users\erikn\source\repos\client\ACE 1,771 .cs — complete
holtburger C:\Users\erikn\source\repos\holtburger 124 .rs — complete
Chorizite.ACProtocol none found anywhere on disk absent

Three further identical ACE copies exist under %TEMP% (ACE-ref, ACE-k4-source, acdream-ace-reference) and one under C:\Users\erikn\source\repos\holtburger\ACE. The duplication is itself a symptom: previous sessions have each re-solved this problem privately.

Chorizite.ACProtocol was unavailable for this audit. Where CLAUDE.md's hierarchy names it as a cross-check for protocol field order, this audit used ACE plus holtburger, or ACE plus docs/research/named-retail/, instead.

0.2 The 2026-06-04 wire catalog does not exist

docs/research/2026-06-04-wire-message-catalog.md — cited by the intake doc as "256 opcodes catalogued, 114 parsed", and indexed in MEMORY.mdwas never committed and is not on disk.

  • git log --all -- "docs/research/2026-06-04-wire-message-catalog.md" → empty
  • git log --all --diff-filter=A -- "*wire-message-catalog*" → empty
  • filesystem sweep of C:\Users\erikn for *wire-message-catalog* → no hits
  • the research/ subdirectory that MEMORY.md links into (C:\Users\erikn\.claude\projects\C--Users-erikn-source-repos-acdream\memory\research\) does not exist

It was produced by the acdream-wire-message-catalog skill into the user-level memory tree and lost when that tree was pruned. MEMORY.md still carries the dangling link, as do the sibling links for the 2026-06-04 combat-math and property-enum research drops — those should be checked too.

Consequence for this audit. "Diff against the catalog's status column" is not possible. Two substitutions were made:

  1. The row universe comes from ACE's three opcode enums rather than the catalog's 256. This is more complete, not less — see §1.
  2. The drift comparison is against docs/research/2026-05-10-phase-m-opcode-matrix.md, which is in-repo, covers ~284 rows, and is the closest surviving prior status artifact. It is 11 weeks old rather than 7, and its own header warns that a "spot-check pass + intentional-divergence ratification" was still owed, so treat drift-vs-matrix as indicative rather than authoritative.

1. Row universe

ACE enumerates the protocol in three enums. Together they are the audit's universe:

Family Enum Rows
Top-level GameMessage Network/GameMessages/GameMessageOpcode.cs 82
GameEvent (inside 0xF7B0) Network/GameEvent/GameEventType.cs 104
GameAction (inside 0xF7B1) Network/GameAction/GameActionType.cs 163
Total 349

acdream's own src/AcDream.Core.Net/Messages/GameEventType.cs carries 103 entries against ACE's 104.

Where acdream's handling lives

  • Top-level: one if / else if chain in src/AcDream.Core.Net/WorldSession.cs, in the fragment loop that begins at line 1215. Each arm is a recognized opcode. The final arm (else if (DumpOpcodesEnabled), line 1572) is the unhandled fallback and only emits under ACDREAM_DUMP_OPCODES=1, deduplicated to the first occurrence per opcode.
  • GameEvent: Messages/GameEventDispatcher.cs routes by sub-opcode and counts misses in an _unhandledCounts bag.
  • GameAction: outbound builders across Messages/*.cs, sent through WorldSession.SendGameAction. Live callers live outside Core.Net.

2. Corpus reality check

The mission was to rank gaps by observed frequency. The corpus cannot support that ranking, and the reason is worth recording.

Logs examined (%TEMP%\claude\): coldeve-vk.log (3,226 lines, live production Coldeve server), coldeve-vk.log.err, user-vk-session.log, v4d-connected.log, v4d-connected2.log, prerevert-launch.log, prerevert-launch2.log, verify3.log, and the smaller Vulkan scene logs.

Zero unhandled-opcode lines across the entire corpus. The only diagnostic that would have produced them, ACDREAM_DUMP_OPCODES=1, was not set for any of those sessions. (0xF682 appears 15 times in coldeve-vk.log and is a red herring — it is the high half of cell id 0xF6820033 in [world-reveal] lines, not an opcode.)

So criterion (a) of the implementation bar — "observed in the corpus" — is satisfiable for essentially nothing in the MISSING/STUB set. Per the mission's own rule, that means no new parsers were written for unobserved gaps, and the ledger is the deliverable.

What the corpus does prove

It is positive evidence of which paths a live production server exercised:

Observation Count Wire path implicated
teleport queued / presentation started / arrival 14 each PlayerTeleport 0xF751
teleport materialized - snapped to ... 14 0xF751 + position follow-up
SetTurbineChatChannels parsed 4 (Coldeve) + 12 (local) 0xF7DE family
equipment: attached child=... parent=... 118 ParentEvent 0xF749
in world - CreateObject stream active 1 per session CreateObject 0xF745
entering world as 0x... Barris 1 CharacterList 0xF658 + EnterWorld
combat: melee attack / missile attack 3 outbound attack actions

Notably the Coldeve session logged in as Barris from a multi-character account, which the intake doc flags as evidence that the character-select path is half-built already.

The cheap fix

Re-running one connected session with ACDREAM_DUMP_OPCODES=1 would convert this section from "cannot rank" to a real frequency table, at the cost of one environment variable. That is the single highest-value follow-up from this audit and is listed first in §7. It needs a connected client, so it is user-gated and was out of scope here.


3. The ledger

Every row below is grounded in acdream source read during this audit, with a file:line citation, and compared field-by-field against ACE's serializer or handler for that opcode. The prior status columns in the Phase-M matrix were not used as input — they are compared against the result in section 4.

Verification depth. The three family tables were produced by parallel audit agents working to one contract. Five findings were then re-verified by hand against primary source before publication: the HearRangedSpeech range field, CommunicationTransientString's phantom chat type, the xpSpent width, the SetCharacterOptions payload, and CharacterError's absence. All five held. The remaining rows carry their evidence inline; treat them as audited-with-citation rather than hand-re-verified, and spot-check before acting on any single row — the same caveat the Phase-M matrix set for itself.

Status vocabulary. For inbound families: PARSED (recognized, every field ACE serializes is decoded), PARTIAL (decoded but fields skipped or assumed), STUB (recognized but body dropped / no state effect), MISSING (not recognized). For the outbound GameAction family the axis is different: BUILT+WIRED, BUILT+PARTIAL, BUILDER-ONLY (dead builder, no production caller), MISSING, N/A (server-side).

The tables below describe the state at base commit b70b9832. This audit then fixed three of those rows itself — 0x02BC HearRangedSpeech (was PARTIAL), 0x02EB CommunicationTransientString (was PARTIAL), and the RaiseAttribute/RaiseVital/RaiseSkill trio (were BUILT+PARTIAL). Those four rows read PARSED / BUILT+WIRED as of f57db35c. Every other row is unchanged. See sections 5 and 6.

Roll-up across all three families

Family Universe PARSED / BUILT+WIRED PARTIAL STUB / BUILDER-ONLY MISSING N/A
Top-level GameMessage 82 36 2 2 42
GameEvent (0xF7B0) 104 38 3 63 0
GameAction (0xF7B1) 163 56 6 10 86 5
Total 349 130 11 75 128 5

Two adjustments matter before reading those numbers as a grade:

  • Dead enum entries. 12 top-level opcodes and 13 GameEvent sub-opcodes are in ACE's enum but ACE never puts them on the wire. Excluding them, the top-level family is 36 PARSED / 2 PARTIAL / 2 STUB / 30 MISSING out of 70 live opcodes, and the real GameEvent gap is about 49 rows rather than 63.
  • GameEvent MISSING is zero, and that is not as good as it sounds. Every ACE sub-opcode value exists in acdream's enum, so nothing is literally unrecognized — but 62 of them have no registered handler and land in the dispatcher's _unhandledCounts bag. STUB is the honest status for those.

The concentration is worth naming: the GameEvent STUB block is almost entirely fellowship, housing, trade, allegiance, chess, books, and vendor — which is precisely the surface the intake doc's bucket-C items (fellowship/allegiance tabs, vendor management) will need.

3.1 Top-level GameMessage opcodes (82 rows)

Row universe: all 82 entries of ACE Source/ACE.Server/Network/GameMessages/GameMessageOpcode.cs (includes the two intentional duplicate names CharacterCreateResponse/CharacterRestoreResponse at 0xF643 and MovementEvent/Motion at 0xF74C).

acdream paths are repo-relative. The dispatch chain is the foreach (BorrowedMessageFragment frag ...) loop in src\AcDream.Core.Net\WorldSession.cs:1215; the unhandled fallback is WorldSession.cs:1572 (else if (DumpOpcodesEnabled)).

ACE serializer column names the file under C:\Users\erikn\source\repos\client\ACE\Source\ACE.Server\Network\GameMessages\Messages\ unless stated otherwise.

Opcode ACE name Dir acdream status acdream site (file:line) ACE serializer Field-coverage notes
0x0000 None MISSING none Sentinel only. GameMessage base skips writing the opcode word when Opcode == None, so it never appears on the wire. No coverage needed.
0x0024 InventoryRemoveObject S→C PARSED dispatch src\AcDream.Core.Net\WorldSession.cs:1484; parser src\AcDream.Core.Net\Messages\InventoryRemoveObject.cs:276 GameMessageInventoryRemoveObject.cs ACE writes only u32 guid (size 8). acdream reads guid and raises InventoryObjectRemoved. Wired to ClientObjectTable at src\AcDream.Core.Net\ObjectTableWiring.cs:87. Complete.
0x0197 SetStackSize S→C PARSED dispatch WorldSession.cs:1477; parser Messages\SetStackSize.cs:241 GameMessageSetStackSize.cs ACE order: byte seq (ByteSequence), u32 guid, i32 stackSize, i32 value. acdream reads all four; the sequence byte is deliberately not honored (latest-wins, cited as divergence DR-4 at SetStackSize.cs:138/:246). Sequence widths confirmed via Network\Sequence\SequenceManager.cs:179 (default = ByteSequence).
0x019E PlayerKilled S→C PARSED dispatch WorldSession.cs:1414; parser Messages\PlayerKilled.cs:341 GameMessagePlayerKilled.cs ACE: String16L deathMessage, u32 victimGuid, u32 killerGuid. acdream reads all three in order. Complete.
0x01E0 EmoteText S→C PARSED dispatch WorldSession.cs:1388; parser Messages\EmoteText.cs:126 GameMessageEmoteText.cs ACE: u32 senderId, String16L senderName, String16L emoteText. acdream matches exactly. Complete.
0x01E2 SoulEmote S→C PARSED dispatch WorldSession.cs:1397; parser Messages\SoulEmote.cs:177 GameMessageSoulEmote.cs Same layout as 0x01E0; acdream matches. Complete.
0x02BB HearSpeech S→C PARSED dispatch WorldSession.cs:1379; parser Messages\HearSpeech.cs:49 GameMessageHearSpeech.cs ACE: String16L text, String16L senderName, u32 senderId, u32 chatType. acdream matches. Complete for the local variant only.
0x02BC HearRangedSpeech S→C PARTIAL dispatch WorldSession.cs:1379; parser Messages\HearSpeech.cs:49 GameMessageHearRangedSpeech.cs Missing field: f32 range. ACE writes text, senderName, senderId, range(f32), chatType; acdream's shared parser reads chatType from offset senderId+4, i.e. from the range float's bits. See Finding 1.
0x02CD PrivateUpdatePropertyInt S→C PARSED dispatch WorldSession.cs:1461; parser Messages\PrivateUpdatePropertyInt.cs:133 GameMessagePrivateUpdatePropertyInt.cs ACE: byte seq, u32 property, i32 value. acdream reads property + value; sequence deliberately skipped with cited reason (DR-4, PrivateUpdatePropertyInt.cs:121). 92 ACE construction sites — the highest-volume property message.
0x02CE PublicUpdatePropertyInt S→C PARSED dispatch WorldSession.cs:1454; parser Messages\PublicUpdatePropertyInt.cs:176 GameMessagePublicUpdatePropertyInt.cs ACE: byte seq, u32 guid, u32 property, i32 value. acdream reads guid/property/value; sequence documented-skipped (DR-4).
0x02CF PrivateUpdatePropertyInt64 S→C PARSED dispatch WorldSession.cs:1468; parser Messages\PrivateUpdatePropertyInt64.cs:212 GameMessagePrivateUpdatePropertyInt64.cs ACE: byte seq, u32 property, i64 value. acdream reads property at +5 and i64 at +9. Retail anchor cited (CM_Qualities::DispatchUI_PrivateUpdateInt64 @ 0x006AEAD0).
0x02D0 PublicUpdatePropertyInt64 S→C MISSING — (falls to WorldSession.cs:1572) GameMessagePublicUpdatePropertyInt64.cs ACE constructs it twice (Enlightenment / Player_Properties). Layout byte seq, u32 guid, u32 property, i64 value. Not recognized anywhere in src\.
0x02D1 PrivateUpdatePropertyBool S→C MISSING GameMessagePrivateUpdatePropertyBool.cs 7 ACE construction sites. Layout byte seq, u32 property, u32 value. Not recognized. Local-player bool qualities never update live after the 0xF7B0/0x0013 PlayerDescription dump.
0x02D2 PublicUpdatePropertyBool S→C MISSING GameMessagePublicUpdatePropertyBool.cs 9 ACE construction sites incl. WorldObjects\Door.cs:220 (PropertyBool.Locked), Lock.cs:275/310, Hook.cs:259 (UiHidden). Layout byte seq, u32 guid, u32 property, u32 value. See Finding 4.
0x02D3 PrivateUpdatePropertyFloat S→C MISSING GameMessagePrivateUpdatePropertyFloat.cs Class exists but ACE has zero construction sites — never sent in normal play. Layout byte seq, u32 property, f64 value.
0x02D4 PublicUpdatePropertyFloat S→C MISSING GameMessagePublicUpdatePropertyFloat.cs 2 ACE sites (EmoteManager.cs:1159, Player_Properties.cs:1153). Layout byte seq, u32 guid, u32 property, f64 value. Not recognized.
0x02D5 PrivateUpdatePropertyString S→C MISSING GameMessagePrivateUpdatePropertyString.cs Class exists but ACE has zero construction sites — never sent. Layout byte seq, u32 property, align, String16L.
0x02D6 PublicUpdatePropertyString S→C MISSING GameMessagePublicUpdatePropertyString.cs 2 ACE sites (Player_Properties.cs:1200, SlumLord.cs:230 — renames a house sign). Note ACE's field order here is seq, property, guid, align, String16L, i.e. property BEFORE guid, unlike every other Public* message. See Finding 6.
0x02D7 PrivateUpdatePropertyDataID S→C MISSING GameMessagePrivateUpdateDataID.cs 4 ACE sites. Layout byte seq, u32 property, u32 value. Not recognized.
0x02D8 PublicUpdatePropertyDataID S→C MISSING GameMessagePublicUpdateDataID.cs (class GameMessagePublicUpdatePropertyDataID) 1 ACE site. Layout byte seq, u32 guid, u32 property, u32 value. Not recognized.
0x02D9 PrivateUpdatePropertyInstanceID S→C MISSING GameMessagePrivateUpdateInstanceID.cs 1 ACE site. Layout byte seq, u32 property, u32 value. Not recognized.
0x02DA PublicUpdateInstanceId S→C MISSING GameMessagePublicUpdateInstanceID.cs 12 ACE construction sites — the live container/wielder re-parent broadcast. Layout byte seq, u32 guid, u32 property, u32 instanceGuid. Not recognized. See Finding 5.
0x02DB PrivateUpdatePosition S→C MISSING GameMessagePrivateUpdatePosition.cs 1 ACE site. Layout byte seq, u32 positionType, Position.Serialize. Carries lifestone/marketplace/last-outside anchors. Not recognized.
0x02DC PublicUpdatePosition S→C MISSING GameMessagePublicUpdatePosition.cs Class exists; zero ACE construction sites — never sent in normal play.
0x02DD PrivateUpdateSkill S→C MISSING GameMessagePrivateUpdateSkill.cs 10 ACE construction sites (Player_Skills.cs, SkillAlterationDevice.cs). Layout byte seq, u32 skill, u32 ranks, u16 adjustPP, u32 advancementClass, u32 xpSpent, u32 initLevel, u32 resistanceAtLastCheck, f64 lastUsedTime. Not recognized — skills never refresh live.
0x02DE PublicUpdateSkill S→C MISSING none No ACE serializer class and no handler — enum entry only; ACE never sends it.
0x02DF PrivateUpdateSkillLevel S→C MISSING none No ACE serializer class — enum entry only; never sent.
0x02E0 PublicUpdateSkillLevel S→C MISSING none No ACE serializer class — enum entry only; never sent.
0x02E3 PrivateUpdateAttribute S→C MISSING GameMessagePrivateUpdateAttribute.cs 8 ACE construction sites (Player_Attributes.cs, AttributeTransferDevice.cs, Enlightenment.cs). Layout byte seq, u32 attribute, u32 ranks, u32 startingValue, u32 xpSpent. Not recognized — attributes never refresh live.
0x02E4 PublicUpdateAttribute S→C MISSING none No ACE serializer class — enum entry only; never sent.
0x02E7 PrivateUpdateVital S→C PARSED dispatch WorldSession.cs:1433; parser Messages\PrivateUpdateVital.cs:71 GameMessagePrivateUpdateVital.cs ACE: byte seq, u32 vital, u32 ranks, u32 startingValue, u32 xpSpent, u32 current. acdream reads all six including the sequence byte. Complete.
0x02E8 PublicUpdateVital S→C MISSING GameMessagePublicUpdateVital.cs Class exists; zero ACE construction sites — never sent in normal play. Layout would be byte seq, u32 guid, u32 attribute, u32 ranks, u32 base, u32 totalInvestment, u32 current.
0x02E9 PrivateUpdateAttribute2ndLevel S→C PARSED dispatch WorldSession.cs:1444; parser Messages\PrivateUpdateVital.cs:92 GameMessagePrivateUpdateAttribute2ndLevel.cs ACE: byte seq, u32 vital, u32 current. acdream reads all three. 11 ACE construction sites (regen/drain ticks). Complete.
0xEA60 AdminEnvirons S→C PARSED dispatch (inline) WorldSession.cs:1499-1510 GameMessageAdminEnvirons.cs ACE writes exactly one field, u32 environChangeType (size hint 8). acdream reads it at offset 4 and raises EnvironChanged. Complete.
0xF619 PositionAndMovement MISSING none No ACE serializer, no ACE handler; only appears in the enum and in a commented-out field at Physics\Common\SmartBox.cs:35. Never on the wire.
0xF625 ObjDescEvent S→C PARSED dispatch WorldSession.cs:1542; parser Messages\ObjDescEvent.cs:51 GameMessageObjDescEvent.cs → WorldObjects\WorldObject_Networking.cs:48 SerializeUpdateModelData ACE: u32 guid, ModelData block, u16 instanceSeq, u16 visualDescSeq. acdream reads all of it via the shared CreateObject.ReadModelData, and additionally hard-rejects a non-4-byte tail (ObjDescEvent.cs:70) so cursor errors cannot produce a silently-wrong appearance. Complete.
0xF643 CharacterCreateResponse S→C MISSING GameMessageCharacterCreateResponse.cs 1 ACE site. Layout u32 response + (when Ok) u32 guid, String16L name, u32 0. acdream never creates characters, so this never arrives in normal acdream play, but it is unrecognized.
0xF643 CharacterRestoreResponse S→C MISSING GameMessageCharacterRestore.cs Duplicate opcode value with CharacterCreateResponse. 1 ACE site. Layout u32 1, u32 guid, String16L name, u32 secondsDisabled. Unrecognized; acdream never sends the 0xF7D9 request that triggers it.
0xF653 CharacterLogOff both PARSED inbound WorldSession.cs:1236 (CharacterLogOff.IsConfirmation); outbound builder Messages\CharacterLogOff.cs:186 GameMessageCharacterLogOff.cs (S→C, opcode-only) + Network\Handlers\CharacterHandler.cs:266 (C→S) ACE's S→C confirmation is opcode-only (size 4) — acdream's IsConfirmation requires body.Length == 4 and matches the opcode. The C→S request builder writes opcode + u32 characterId per Proto_UI::LogOffCharacter @ 0x00546A20. Both directions complete. Note: the confirmation check runs before the dispatchWorldEvents gate so it still lands during Dispose drain.
0xF655 CharacterDelete both MISSING GameMessageCharacterDelete.cs (S→C, opcode-only) + CharacterHandler.cs:273 (C→S) acdream neither sends the C→S delete request nor recognizes the S→C ack. Not reachable in acdream's flow today.
0xF656 CharacterCreate C→S MISSING CharacterHandler.cs:26 acdream has no character-creation path; it always selects an existing identity from CharacterList.
0xF657 CharacterEnterWorld C→S PARSED builder Messages\CharacterEnterWorld.cs:49; send WorldSession.cs:926 CharacterHandler.cs:198 acdream writes opcode, u32 characterGuid, String16L accountName — matching what ACE's handler reads. Uses the account name returned by CharacterSet::UnPack (not the login-form spelling) per CPlayerSystem::LogOnCharacter @ 0x0055F890, which is what ACE validates.
0xF658 CharacterList S→C PARSED dispatch WorldSession.cs:1245; parser Messages\CharacterList.cs:57 GameMessageCharacterList.cs ACE: u32 status(0), i32 activeCount, N × (u32 id, String16L name, u32 secondsGreyedOut), u32 deletedCount(0), u32 slotCount, String16L account, u32 useTurbineChat, u32 hasThroneOfDestiny. acdream reads every field including both collections, with a payload-derived count bound (CharacterList.cs:123). Complete.
0xF659 CharacterError S→C MISSING GameMessageCharacterError.cs 12 ACE construction sites, including every login-rejection path (AuthenticationHandler.cs:145/152/160/189/192, Session.cs:339, Player_Tick.cs:45/59). Layout is a single u32 CharacterError. Unrecognized — see Finding 3.
0xF6EA ForceObjectDescSend C→S MISSING Network\Handlers\ControlHandler.cs:8 Client-to-server only (u32 itemGuid); also duplicated in GameActionType.cs:159. acdream never sends it, so it can never request a fresh ObjDesc for a stale item.
0xF745 ObjectCreate S→C PARTIAL dispatch WorldSession.cs:1271; parser Messages\CreateObject.cs:~540-1140 GameMessageCreateObject.cs → WorldObject_Networking.cs:56 SerializeCreateObject Physics block: fully covered (flags, state, Movement/AnimationFrame, Position, MTable, STable, PeTable, CSetup, Parent, Children, ObjScale, Friction, Elasticity, Translucency, Velocity, Acceleration, Omega, DefaultScript(+Intensity), all 9 u16 timestamps, align). Weenie block: correct field ORDER and bit map (verified against ACE.Entity\Enum\WeenieHeaderFlags.cs). Skipped-but-cursor-consumed (value discarded, no typed output): PScript (0x08000000, u16), HouseOwner (0x02000000, u32), HouseRestrictions (0x04000000, RestrictionDB), Monarch (0x00000040, u32). Also Workmanship is captured but CombatUse is read as unsigned where ACE writes sbyte (CreateObject.cs:973 vs. the unchecked((sbyte)…) used for ItemsCapacity at :929) — benign for real enum values.
0xF746 PlayerCreate S→C STUB dispatch WorldSession.cs:1260-1270 GameMessagePlayerCreate.cs ACE writes u32 guid (size 8). acdream matches the opcode only as the LoginComplete trigger and never reads the guid; the local identity comes from the CharacterList selection (WorldSession.cs:907). Also the arm is gated on && !_loginCompleteSent, so any later 0xF746 falls through to the unhandled branch.
0xF747 ObjectDelete S→C PARSED dispatch WorldSession.cs:1279; parser Messages\DeleteObject.cs:29 GameMessageDeleteObject.cs ACE: u32 guid, u16 instanceSeq, Align(). acdream reads guid + instanceSeq (min length 10; ACE emits 12 after align). Retail anchor CM_Physics::DispatchSB_DeleteObject @ 0x006AC6A0. Complete.
0xF748 UpdatePosition S→C PARSED dispatch WorldSession.cs:1319; parser Messages\UpdatePosition.cs:163 GameMessageUpdatePosition.cs → Network\Structure\PositionPack.cs ACE: u32 guid, u32 flags, Origin(u32 cell + 3×f32), conditional rotation W/X/Y/Z (present when the matching OrientationHasNo* bit is CLEAR), conditional Velocity(3×f32), conditional PlacementID(u32), then 4×u16 sequences. acdream reproduces every branch, in order, including the inverted-flag rotation semantics and all four sequences. Complete.
0xF749 ParentEvent S→C PARSED dispatch WorldSession.cs:1293; parser Messages\ParentEvent.cs:116 GameMessageParentEvent.cs ACE: u32 creatureGuid, u32 childGuid, i32 parentLocation, i32 placement, u16 parentInstanceSeq, u16 childPositionSeq (24 bytes). acdream reads all six. Complete.
0xF74A PickupEvent S→C PARSED dispatch WorldSession.cs:1285; parser Messages\PickupEvent.cs:76 GameMessagePickupEvent.cs ACE: u32 guid, u16 instanceSeq, u16 positionSeq. acdream reads all three and routes to a distinct EntityPickedUp event (deliberately NOT collapsed into 0xF747, since pickup advances POSITION_TS and retains the logical object). Complete.
0xF74B SetState S→C PARSED dispatch WorldSession.cs:1355; parser Messages\SetState.cs:66 GameMessageSetState.cs ACE: u32 guid, u32 physicsState, u16 instanceSeq, u16 stateSeq (16 bytes). acdream reads all four. Complete.
0xF74C MovementEvent S→C PARSED dispatch WorldSession.cs:1299; parser Messages\UpdateMotion.cs:94 GameMessageUpdateMotion.cs → Network\Motion\MovementData.cs:184 Same opcode as Motion (next row). See that row for the field analysis. Also duplicated as GameActionType.MovementEvent = 0xF74C (C→S), which acdream does not send.
0xF74C Motion S→C PARSED dispatch WorldSession.cs:1299; parser Messages\UpdateMotion.cs:94 GameMessageUpdateMotion.cs ACE: u32 guid, u16 instanceSeq, then MovementData.Write(header:true) = u16 movementSeq, u16 serverControlSeq, byte isAutonomous, Align(), byte movementType, byte motionFlags, u16 currentStyle, then the type-selected body. acdream covers movementType 0 (InterpretedMotionState with the correct non-sequential flag-bit → write-order mapping, the Commands list, and the MotionFlags.StickToObject trailing guid), 6/7 (MoveTo*: optional target guid, Origin, 7-dword MovementParameters, runRate) and 8/9 (TurnTo*: optional target guid + standalone wire heading, 3-dword TurnToParameters). The Align() pad is computed correctly against ACE's ABSOLUTE-stream CalculatePadMultiple (Network\Extensions.cs:55) — one pad byte at stream length 15. Complete.
0xF74E VectorUpdate S→C PARSED dispatch WorldSession.cs:1341; parser Messages\VectorUpdate.cs:179 GameMessageVectorUpdate.cs ACE: u32 guid, Vector3 velocity, Vector3 omega, u16 instanceSeq, u16 vectorSeq (36 bytes). acdream reads all four. Complete.
0xF750 Sound S→C MISSING GameMessageSound.cs 63 ACE construction sites — the highest-volume unhandled opcode. Layout u32 guid, u32 soundId, f32 volume (16 bytes). No 0xF750 reference anywhere in src\. See Finding 7.
0xF751 PlayerTeleport S→C PARSED dispatch (inline) WorldSession.cs:1524-1540 GameMessagePlayerTeleport.cs ACE writes exactly one field, u16 teleportSequence, then Align() (total 8 bytes). acdream reads the u16 at offset 4 and raises TeleportStarted. Complete.
0xF753 AutonomousPosition C→S PARSED builder Messages\AutonomousPosition.cs:32 (GameAction sub-opcode inside the 0xF7B1 envelope) Network\GameAction\GameActionType.cs:165; the S→C GameMessageAutonomousPosition.cs exists but its only send site is commented out (WorldObjects\Player.cs:683) ACE never sends 0xF753 as a top-level GameMessage. acdream correctly treats it as a client→server GameAction (u32 0xF7B1, u32 sequence, u32 0xF753, …), matching retail.
0xF754 PlayScriptId S→C PARSED dispatch WorldSession.cs:1512; parser Messages\PlayPhysicsScript.cs:216 none — ACE has no serializer or handler for PlayScriptId; the enum entry is unused acdream parses u32 guid, u32 scriptDid (12 bytes exact) per retail SmartBox::HandlePlayScriptID @ 0x00452020. Complete against retail, but ACE never emits it, so it is dead on an ACE server.
0xF755 PlayEffect S→C PARSED dispatch WorldSession.cs:1518; parser Messages\PlayPhysicsScriptType.cs:246 GameMessageScript.cs ACE: u32 guid, u32 scriptId, f32 speed (16 bytes). acdream reads all three (RawScriptType, Intensity) with an exact-length gate. 27 ACE construction sites. Complete.
0xF7B0 GameEvent S→C PARSED dispatch WorldSession.cs:1489; envelope parser Messages\GameEventEnvelope.cs:53 Network\GameEvent\GameEventMessage.cs Envelope only: u32 playerGuid, u32 gameEventSequence, u32 eventType, then a borrowed payload view. All four header words are read and the payload is dispatched by sub-opcode via Messages\GameEventDispatcher.cs. Per-sub-opcode coverage is a separate family and is out of scope for this ledger.
0xF7B1 GameAction C→S PARSED builders across Messages\*Requests.cs / *Actions.cs; send seam WorldSession.cs:1589 Network\GameAction\GameActionPacket.cs:9 Outbound-only envelope (u32 0xF7B1, u32 sequence, u32 actionType, …). acdream implements ~50 sub-actions (chat, movement, inventory, combat, casting, social, recalls). Per-sub-opcode coverage is a separate family.
0xF7C1 AccountBanned S→C MISSING GameMessageAccountBanned.cs Sent from AuthenticationHandler.cs:220 on a banned account. Layout u32 secondsUntilExpiry + optional String16L reason. Unrecognized — the ban reason is silently dropped and the client just times out.
0xF7C8 CharacterEnterWorldRequest C→S PARSED builder Messages\CharacterEnterWorld.cs:36; send WorldSession.cs:910 CharacterHandler.cs:183 Opcode-only body (4 bytes), matching what ACE's handler expects. Complete.
0xF7CC GetServerVersion C→S MISSING Network\Handlers\GetServerVersionHandler.cs:10 acdream never sends the @version query. ACE would reply with a 0xF7E0 SystemChat line (which acdream does parse). No inbound work needed.
0xF7CD FriendsOld C→S PARSED builder Messages\ClientCommandRequests.cs:40 (LegacyFriendsOpcode); send WorldSession.cs:1778 Network\Handlers\FriendsOldHandler.cs:9 Client-to-server legacy friends command; ACE replies with a 0xF7E0 SystemChat ("not used in the emulator"). acdream builds and sends it as a control message. Complete for the direction that exists.
0xF7D9 CharacterRestore C→S MISSING CharacterHandler.cs:331 acdream has no character-restore path.
0xF7DB UpdateObject S→C MISSING — (only a comment mentions it: WorldSession.cs:662) GameMessageUpdateObject.cs → WorldObject_Networking.cs:28 SerializeUpdateObject 9 ACE construction sites (Tailoring ×2, RecipeManager, Aetheria, DeveloperCommands…). The payload is byte-identical to 0xF745 ObjectCreateSerializeUpdateObject simply calls SerializeCreateObject. acdream's parser hard-rejects it because CreateObject.TryParse compares the opcode word against 0xF745. See Finding 2.
0xF7DC AccountBoot S→C MISSING GameMessageBootAccount.cs 13 ACE construction sites (Sentinel @boot, session termination paths). Layout: optional String16L reason (may be a bare opcode). Unrecognized — a booted client shows no reason and just falls off the wire.
0xF7DE TurbineChat both PARSED dispatch WorldSession.cs:1421; parser Messages\TurbineChat.cs:145, builder in the same file GameMessageTurbineChat.cs + Network\Handlers\TurbineChatHandler.cs:19 9-u32 header (sizeFirst, blobType, dispatchType, targetType, targetId, transportType, transportId, cookie, sizeSecond) then a variant payload. acdream handles (EventBinary, SendToRoomByName) and (ResponseBinary, *), which are exactly the two combinations ACE emits (TurbineChatHandler.cs:112 and the NETBLOB_RESPONSE_BINARY sites all pass ASYNCMETHOD_SENDTOROOMBYNAME). Turbine 1-or-2-byte-prefix UTF-16LE strings handled separately from CP1252 String16L. sizeFirst is read but intentionally not validated (TurbineChat.cs:~275). Complete.
0xF7DF CharacterEnterWorldServerReady S→C PARSED WorldSession.cs:919 (handshake loop) GameMessageCharacterEnterWorldServerReady.cs Opcode-only body (4 bytes) — there are no fields to decode. acdream gates EnterWorld on seeing it. Note it is only matched in the handshake drain; after login it falls to the unhandled branch, which is harmless because ACE sends it once.
0xF7E0 ServerMessage S→C PARSED dispatch WorldSession.cs:1406; parser Messages\ServerMessage.cs:292 GameMessageSystemChat.cs ACE: String16L message, i32 chatMessageType. acdream reads both. 724 ACE construction sites — by far the most-sent message on the server. Complete.
0xF7E1 ServerName S→C MISSING GameMessageServerName.cs Sent twice at login (AuthenticationHandler.cs:258, Session.cs:274). Layout i32 currentConnections, i32 maxConnections, String16L serverName. Unrecognized — acdream never learns the world name or population.
0xF7E2 DDD_DataMessage S→C MISSING GameMessageDDDDataMessage.cs 7 ACE construction sites, but only reachable when the client claims stale dat iterations. acdream's interrogation response claims zero lists (see 0xF7E5/0xF7E6), so ACE never enters the DDD patch flow in practice.
0xF7E3 DDD_RequestDataMessage C→S MISSING Network\Handlers\DDDHandler.cs:195 Client-to-server dat pull request; acdream never sends it (no DDD patching).
0xF7E4 DDD_ErrorMessage S→C MISSING GameMessageDDDErrorMessage.cs 1 ACE construction site, inside the DDD patch flow acdream never enters. Layout u32 resourceType, u32 dataId, u32 errorType.
0xF7E5 DDD_Interrogation S→C STUB dispatch WorldSession.cs:1250-1259 GameMessageDDDInterrogation.cs ACE writes u32 serversRegion, u32 nameRuleLanguage, u32 productId, u32 supportedLanguagesCount(2), u32 0, u32 1 (28 bytes). acdream recognizes the opcode and immediately sends a canned 0xF7E6 reply but decodes zero body fields — region, language, product id and the supported-language list are all dropped.
0xF7E6 DDD_InterrogationResponse C→S PARSED builder Messages\DddInterrogationResponse.cs:34; send WorldSession.cs:1258 DDDHandler.cs:24 acdream writes opcode, u32 language(1=English), u32 count(0). ACE's handler reads ReadUInt32() then ReadCAllIterationList() which starts with an i32 numElements — so a count of 0 terminates the read cleanly (Network\Structure\CAllIterationList.cs:19). Byte-exact for the empty case.
0xF7E7 DDD_BeginDDD S→C MISSING GameMessageDDDBeginDDD.cs 1 ACE construction site, inside the DDD patch flow acdream never enters.
0xF7E8 DDD_BeginPullDDD MISSING none No ACE serializer and no ACE handler — enum entry only; never on the wire.
0xF7E9 DDD_IterationData MISSING none No ACE serializer and no ACE handler — enum entry only; never on the wire.
0xF7EA DDD_EndDDD both MISSING GameMessageDDDEndDDD.cs (S→C, opcode-only) + DDDHandler.cs:176 (C→S) 2 ACE construction sites, inside the DDD patch flow acdream never enters. Unrecognized in either direction.

Roll-up

Status Count
PARSED 36
PARTIAL 2
STUB 2
MISSING 42
Total rows 82

Restricting to opcodes ACE actually constructs/handles in normal play (i.e. excluding the 12 enum-only or dead entries: None, PositionAndMovement, PublicUpdateSkill, PrivateUpdateSkillLevel, PublicUpdateSkillLevel, PublicUpdateAttribute, PublicUpdateVital, PublicUpdatePosition, PrivateUpdatePropertyFloat, PrivateUpdatePropertyString, DDD_BeginPullDDD, DDD_IterationData): 36 PARSED / 2 PARTIAL / 2 STUB / 30 MISSING out of 70 live opcodes.

Findings

Finding 1 — HearRangedSpeech (0x02BC) decodes chatType from the range float's bits

src\AcDream.Core.Net\Messages\HearSpeech.cs:49 uses one parser for both 0x02BB (HearSpeech) and 0x02BC (HearRangedSpeech), reading:

string16L text; string16L senderName; u32 senderGuid; u32 chatType;

But ACE's GameMessageHearRangedSpeech.cs writes an extra float range between the sender id and the chat type:

Writer.WriteString16L(messageText);
Writer.WriteString16L(senderName);
Writer.Write(senderID);
Writer.Write(range);                 // <-- acdream never reads this
Writer.Write((uint)chatMessageType);

Independently confirmed by holtburger (crates\holtburger-protocol\src\messages\chat\types.rs:50-67, HearRangedSpeechData::unpack advances 12 bytes: sender, range, chat_type) versus HearSpeechData::unpack (8 bytes).

Consequence: for every ranged/shout message, Parsed.ChatType is the IEEE-754 bit pattern of the range float (e.g. a 20 m range → 0x41A00000 = 1 101 004 800) instead of a ChatMessageType in 0x010x1F, and the real chat type is never read at all. The IsRanged flag on HearSpeech.Parsed is derived from the opcode, so the parser knows which variant it is — it just does not branch on it. Evidence that the field genuinely diverges: the min-length gate is also wrong for the ranged case (body.Length < 16 at HearSpeech.cs:51 vs. a 20-byte minimum). ACE has 1 GameMessageHearRangedSpeech construction site, so the blast radius is narrow but the decode is definitively wrong.

Finding 2 — UpdateObject (0xF7DB) is dropped despite being byte-identical to ObjectCreate

ACE.Server\WorldObjects\WorldObject_Networking.cs:28:

public virtual void SerializeUpdateObject(BinaryWriter writer, bool adminvision = false, bool changenodraw = false)
{
    // content of these 2 is the same? TODO: Validate that?
    SerializeCreateObject(writer, false, adminvision, changenodraw);
}

so 0xF7DB and 0xF745 carry the same ModelData + PhysicsData + WeenieHeader payload. acdream already has a full parser for that payload, but CreateObject.TryParse (src\AcDream.Core.Net\Messages\CreateObject.cs:~545) rejects anything whose opcode word is not 0xF745, and no dispatch arm matches 0xF7DB, so the message falls to the unhandled branch at WorldSession.cs:1572. ACE has 9 construction sites — Entity\Tailoring.cs:504, Tailoring.cs:563, Managers\RecipeManager.cs:397, Entity\Aetheria.cs:241, plus dev commands — i.e. every "this item's whole description changed" refresh is silently discarded. A stale ClientObjectTable row is the visible symptom (retailored armor keeps its old name/icon/model until the object is re-spawned). Note also that WorldSession.cs:662 documents ACDREAM_DUMP_APPEARANCE=1 as logging "every 0xF625 ObjDescEvent + 0xF7DB UpdateObject" — the 0xF7DB half of that comment describes behavior that does not exist.

Finding 3 — Login/session rejection reasons (CharacterError 0xF659) are silently discarded

ACE terminates a session with an explicit GameMessageCharacterError carrying a single u32 CharacterError code. Send sites include Network\Handlers\AuthenticationHandler.cs:145 (AccountInvalid), :152 (AccountDoesntExist), :160 and :189/:192 (Logon — the "account already logged in" case), :248 (LogonServerFull), Network\Session.cs:339, and WorldObjects\Player_Tick.cs:45/59 (save failures). acdream recognizes none of them (grep 0xF659 over src\ returns nothing), so the message hits the unhandled branch and the session instead dies on a generic timeout: throw new TimeoutException("CharacterList not received") at WorldSession.cs:889 or "ServerReady not received" at :921.

This is the exact mechanism behind the project's documented "exit 29 after a hard kill" mystery (CLAUDE.md, "Logout-before-reconnect"): ACE is telling the client CharacterError.Logon — acdream just throws the reason away and reports a timeout. Decoding one u32 would turn a 3-minute guessing game into a one-line diagnostic.

Finding 4 — Live PropertyBool broadcasts (0x02D2) are dropped, including door lock state

GameMessagePublicUpdatePropertyBool has 9 ACE construction sites, notably WorldObjects\Door.cs:220 and WorldObjects\Lock.cs:275/310 (both PropertyBool.Locked) and WorldObjects\Hook.cs:259 (PropertyBool.UiHidden). acdream has no parser and no dispatch arm. Consequence: after the initial CreateObject, a door's locked/unlocked state and a hook's hidden state never change client-side. This sits adjacent to the door work already tracked in the render/physics digests, so it is worth flagging even though it is a gap rather than a decode error. The sibling private form (0x02D1, 7 sites) is likewise unhandled.

Finding 5 — PublicUpdateInstanceId (0x02DA) unhandled — 12 send sites, container/wielder re-parenting

GameMessagePublicUpdateInstanceID (byte seq, u32 guid, u32 property, u32 instanceGuid) is ACE's live re-parent broadcast (PropertyInstanceId.Container / .Wielder), with 12 construction sites. acdream tracks parent relations from 0xF749 ParentEvent and the CreateObject Parent block only, so any instance-id change delivered through this opcode is lost. Same for the private form 0x02D9 (1 site).

Finding 6 — ACE's PublicUpdatePropertyString (0x02D6) has anomalous field order (documentation hazard)

Every other Public* property message writes seq, guid, property, value. But GameMessagePublicUpdatePropertyString.cs writes seq, property, guid, Align(), String16L — property BEFORE guid, plus an Align() that the int/bool/float variants do not have. acdream does not parse this opcode today, so there is no live bug; recording it here so a future implementation does not copy the layout from the neighbouring Public* classes and get it wrong. (Its private sibling 0x02D5 has zero ACE send sites and can be skipped.)

Finding 7 — Sound (0xF750) is the single highest-volume unhandled opcode

63 ACE construction sites — locks, chests, drops, trait raises, UI feedback — all writing u32 guid, u32 soundId, f32 volume. Nothing in src\ references 0xF750. This is a pure coverage gap (no decode error), but by send frequency it is the largest one in the family after the already-handled 0xF7E0.

Non-findings verified clean (checked field-by-field, no divergence)
  • UpdatePosition (0xF748) — including the inverted OrientationHasNo* semantics and all four trailing u16 sequences, against Network\Structure\PositionPack.cs.
  • UpdateMotion (0xF74C) — including ACE's Align() being computed on the absolute stream length (Network\Extensions.cs:55), which yields exactly one pad byte at offset 15; and the non-sequential MovementStateFlag bit→write-order mapping (0x01,0x02,0x08,0x20 ushorts then 0x04,0x10,0x40 floats).
  • All WeenieHeaderFlag bit values and field widths in CreateObject against ACE.Entity\Enum\WeenieHeaderFlags.cs and the WorldObject_Properties.cs property types (ItemCapacity/ContainerCapacity are byte? → 1 byte, Script/HookType/MaxStructure are ushort? → 2 bytes, Workmanship is float? → 4 bytes, CooldownDuration promotes to double → 8 bytes).
  • The "sequence" prefix on every 0x02xx property message is a 1-byte ByteSequence (Network\Sequence\SequenceManager.cs:179 default arm), which is what acdream assumes everywhere it parses one.
  • TurbineChat (0xF7DE) blob/dispatch pairs — acdream's two implemented cases are exactly the two ACE emits (TurbineChatHandler.cs always passes ASYNCMETHOD_SENDTOROOMBYNAME).
  • DddInterrogationResponse (0xF7E6) empty-list encoding is accepted byte-exactly by ACE's ReadCAllIterationList.

3.2 GameEvent sub-opcodes, inside 0xF7B0 (104 rows)

Audit date: 2026-07-29. Read-only.

Oracles

  • Row universe + field layout: C:\Users\erikn\source\repos\client\ACE\Source\ACE.Server\Network\GameEvent\GameEventType.cs (104 entries, 103 distinct values — VendorInfoEvent and ApproachVendor share 0x0062) and ...\GameEvent\Events\*.cs (+ ...\Network\Structure\*.cs for the shared blobs).
  • Retail cross-check: docs\research\named-retail\acclient_2013_pseudo_c.txt, acclient.h.

acdream sides

  • Envelope: src\AcDream.Core.Net\Messages\GameEventEnvelope.cs:53 (16-byte header, payload borrowed).
  • Dispatch: src\AcDream.Core.Net\WorldSession.cs:1489GameEventDispatcher.Dispatch (src\AcDream.Core.Net\Messages\GameEventDispatcher.cs:95).
  • Registrations: only two filessrc\AcDream.Core.Net\GameEventWiring.cs (40 owned) and src\AcDream.Core.Net\WorldSession.cs:775,780 (2 legacy). 42 registrations total; every other sub-opcode falls into _unhandledCounts (GameEventDispatcher.cs:114).
  • Production call site supplies every optional collaborator (src\AcDream.Runtime\Session\LiveSessionEventRouter.cs:146-175), so all conditionally-registered handlers are live in production.

All file:line below are repo-relative.


Sub-opcode ACE name acdream enum? acdream status acdream site (file:line) ACE serializer Field-coverage notes
0x0003 AllegianceUpdateAborted yes — AllegianceUpdateAborted STUB none (falls to GameEventDispatcher.cs:114) (no serializer class in ACE) ACE has no GameEventAllegianceUpdateAborted and never sends it. Zero risk vs ACE.
0x0004 PopupString yes — PopupString PARSED reg GameEventWiring.cs:113; parse Messages/GameEvents.cs:87 GameEventPopupString.cs ACE writes one String16L. acdream reads it (CP-1252 + 4-byte pad, GameEvents.cs:535). Complete.
0x0013 PlayerDescription yes — PlayerDescription PARSED reg GameEventWiring.cs:477; parse Messages/PlayerDescriptionParser.cs:268 GameEventPlayerDescription.cs Every ACE-written section is walked in ACE's exact order: propertyFlags/weenieType → Int32/Int64/Bool/Double/String/Did/Iid/Position tables → vectorFlags+hasHealth → attributes → skills → spells → enchantment registry → optionFlags/options1 → shortcuts → SpellLists8 → DesiredComps → SpellbookFilters → Options2 → GameplayOptions → inventory → equipped. Enum bit values verified identical (DescriptionPropertyFlag, AttributeCache bit=(id-1), CharacterOptionDataFlag, EnchantmentMask). Deliberate documented skips: skill u16 const_one (PlayerDescriptionParser.cs:637) and hash-bucket hints. Two caveats: (a) GameplayOptions is an unlength-prefixed raw byte[] in ACE (GameEventPlayerDescription.cs:571) so acdream recovers its end by a 4-byte-aligned forward scan (PlayerDescriptionParser.cs:755) and stores it opaque; (b) the shortcut Spell field is ACE's LayeredSpell (u16+u16) read as one u32 (PlayerDescriptionParser.cs:357) — see Finding 2.
0x0020 AllegianceUpdate yes — AllegianceUpdate STUB none GameEventAllegianceUpdate.cs (rank u32 + AllegianceProfile) ACE constructs it 5×; sent whenever allegiance data changes. Body dropped; AllegianceTree (src\AcDream.Core\Allegiance\AllegianceTree.cs:28) documents it as the intended source but nothing feeds it.
0x0021 FriendsListUpdate yes — FriendsListUpdate PARSED reg GameEventWiring.cs:149; parse Messages/SocialStateMessages.cs:13 GameEventFriendsListUpdate.cs ACE writes count, then per friend friendId, isOnline, appearOffline, name(String16L), numFriends(0), numFriendsOf(0), then trailing updateType. acdream reads all of them including both empty u32 lists and the trailing type. Minor: Enum.IsDefined gate (SocialStateMessages.cs:37) rejects any value outside FriendsUpdateType{0..4} — ACE's FriendsUpdateTypeFlag is [Flags], so a combined value would drop the whole update (ACE only sends singletons today).
0x0022 InventoryPutObjInContainer yes — InventoryPutObjInContainer PARSED reg GameEventWiring.cs:346; parse Messages/GameEvents.cs:387 GameEventItemServerSaysContainId.cs 4×u32: itemGuid, containerGuid, placement, containerType. All read.
0x0023 WieldObject yes — WieldObject PARSED reg GameEventWiring.cs:335; parse Messages/GameEvents.cs:369 GameEventWieldItem.cs 2×u32: objectId, (int)EquipMask. Both read (EquipLoc widened to uint — ACE writes it as signed int, harmless for a bitmask).
0x0029 CharacterTitle yes — CharacterTitle STUB none GameEventCharacterTitle.cs (1u, currentTitleId, count, titleId[]) Sent once per title query/login path. Body dropped; no title UI consumes it.
0x002B UpdateTitle yes — UpdateTitle STUB none GameEventUpdateTitle.cs (title u32, setAsDisplayTitle u32) ACE sends on title grant. Dropped.
0x0052 CloseGroundContainer yes — CloseGroundContainer PARSED reg GameEventWiring.cs:425; parse Messages/GameEvents.cs:450 GameEventCloseGroundContainer.cs Single u32 container guid. Complete.
0x0062 VendorInfoEvent no (alias absent; value present as ApproachVendor) STUB none (alias of GameEventApproachVendor.cs) Name-only divergence — same wire value as the row below. See Finding 5.
0x0062 ApproachVendor yes — ApproachVendor STUB none GameEventApproachVendor.cs ACE sends on every vendor Use: vendorGuid, merchandiseItemTypes, min/max value, dealMagical, buy/sellPrice, altCurrency, altCurrencyCount, altCurrencyPluralName(String16L), numItems, then per item `stackSize
0x0075 StartBarber yes — StartBarber STUB none GameEventStartBarber.cs (16 u32 appearance fields) Only on barber Use. Dropped.
0x00A0 InventoryServerSaveFailed yes — InventoryServerSaveFailed PARSED reg GameEventWiring.cs:394; parse Messages/GameEvents.cs:441 GameEventInventoryServerSaveFailed.cs 2×u32: itemGuid, WeenieError. Both read and both used (RejectMove). ACE's 2nd-most-constructed event (179 sites).
0x00A3 FellowshipQuit yes — FellowshipQuit STUB none GameEventFellowshipQuit.cs (playerId u32) ACE sends on fellow quit. Dropped (no fellowship subsystem).
0x00A4 FellowshipDismiss yes — FellowshipDismiss STUB none GameEventFellowshipDismiss.cs (guid u32) Same.
0x00B4 BookDataResponse yes — BookDataResponse STUB none GameEventBookDataResponse.cs Sent on book Use. Dropped; no book UI.
0x00B5 BookModifyPageResponse yes — BookModifyPageResponse STUB none GameEventBookModifyPageResponse.cs (bookGuid, page, success) Dropped.
0x00B6 BookAddPageResponse yes — BookAddPageResponse STUB none GameEventBookAddPageResponse.cs (bookGuid, page, success) Dropped.
0x00B7 BookDeletePageResponse yes — BookDeletePageResponse STUB none GameEventBookDeletePageResponse.cs (bookGuid, page, success) Dropped.
0x00B8 BookPageDataResponse yes — BookPageDataResponse STUB none GameEventBookPageDataResponse.cs Dropped.
0x00C3 GetInscriptionResponse yes — GetInscriptionResponse STUB none GameEventInscriptionResponse.cs (guid, inscription, playerGuid, scribeName, scribeAccount, Align) ACE sends on inscription query. Dropped — note the pre-M4 "inscription transaction" work is accepted, so this is likely a real gap to confirm against the outbound path.
0x00C9 IdentifyObjectResponse yes — IdentifyObjectResponse PARSED reg GameEventWiring.cs:433; parse Messages/AppraiseInfoParser.cs:154 GameEventIdentifyObjectResponse.cs + Structure/AppraiseInfo.cs:735 Flag bit values match ACE's IdentifyResponseFlags exactly. Read order matches ACE's write order (Int, Int64, Bool, Float, String, Did, SpellBook, ArmorProfile, CreatureProfile, WeaponProfile, HookProfile, Armor/Weapon/Resist enchantment bitfields, ArmorLevels). Sub-blob layouts verified field-for-field against ArmorProfile.cs, CreatureProfile.cs (incl. the 0x8 ShowAttributes and 0x1 HasBuffsDebuffs gates), WeaponProfile.cs (10 fields), HookProfile.cs (3 u32), ArmorLevel.cs (9 u32). Complete.
0x0147 ChannelBroadcast yes — ChannelBroadcast PARSED reg GameEventWiring.cs:98; parse Messages/GameEvents.cs:31 GameEventChannelBroadcast.cs channel u32 + senderName + messageText. All read.
0x0148 ChannelList yes — ChannelList STUB none GameEventChannelList.cs (count + names) ACE sends only in response to a channel-list request. Dropped.
0x0149 ChannelIndex yes — ChannelIndex STUB none GameEventChannelIndex.cs (count + admin channel names) Admin/advocate only. Dropped.
0x0196 ViewContents yes — ViewContents PARSED reg GameEventWiring.cs:363; parse Messages/GameEvents.cs:403 GameEventViewContents.cs containerGuid, count, count×(guid, containerType). All read with a 64-bit-safe bounds check (GameEvents.cs:409).
0x019A InventoryPutObjectIn3D yes — InventoryPutObjectIn3D PARSED reg GameEventWiring.cs:379; parse Messages/GameEvents.cs:430 GameEventItemServerSaysMoveItem.cs Single u32 guid. Complete.
0x01A7 AttackDone yes — AttackDone PARSED reg GameEventWiring.cs:265; parse Messages/GameEvents.cs:261 GameEventAttackDone.cs ACE writes exactly one u32 (WeenieError); acdream reads it. The record's AttackSequence is fabricated as 0 (GameEvents.cs:264) — nothing on the wire, so no coverage gap vs ACE, but it is not a decoded field. See Finding 4.
0x01A8 MagicRemoveSpell yes — MagicRemoveSpell PARTIAL reg GameEventWiring.cs:287; parse Messages/GameEvents.cs:288 GameEventMagicRemoveSpell.cs Missing field: u16 layer. ACE writes (ushort spellId, ushort layer); acdream reads a single little-endian u32 and treats the whole thing as the spell id. Correct only while layer == 0 (ACE's default). A non-zero layer silently corrupts the spell id by layer << 16.
0x01AC VictimNotification yes — VictimNotification PARSED reg GameEventWiring.cs:237; parse Messages/GameEvents.cs:170 GameEventVictimNotification.cs One String16L death message. Complete.
0x01AD KillerNotification yes — KillerNotification PARSED reg GameEventWiring.cs:275; parse Messages/GameEvents.cs:180 GameEventKillerNotification.cs One String16L. Complete.
0x01B1 AttackerNotification yes — AttackerNotification PARSED reg GameEventWiring.cs:249; parse Messages/GameEvents.cs:196 GameEventAttackerNotification.cs All 6 fields decoded (name, damageType u32, percent f64, damage u32, critical u32, attackConditions u64). Parser is complete; the wiring forwards only name/damageType/damage/percent — Critical and AttackConditions never reach CombatState (GameEventWiring.cs:252-253). See Finding 6.
0x01B2 DefenderNotification yes — DefenderNotification PARSED reg GameEventWiring.cs:242; parse Messages/GameEvents.cs:223 GameEventDefenderNotification.cs All 7 fields decoded (name, damageType, f64 percent, damage, damageLocation, critical, u64 attackConditions). Parser complete. Wiring drops HealthPercent + AttackConditions and passes a hardcoded attackerGuid = 0u (GameEventWiring.cs:245-247). See Finding 6.
0x01B3 EvasionAttackerNotification yes — EvasionAttackerNotification PARSED reg GameEventWiring.cs:255; parse Messages/GameEvents.cs:242 GameEventEvasionAttackerNotification.cs One String16L. Complete.
0x01B4 EvasionDefenderNotification yes — EvasionDefenderNotification PARSED reg GameEventWiring.cs:260; parse Messages/GameEvents.cs:249 GameEventEvasionDefenderNotification.cs One String16L. Complete.
0x01B8 CombatCommenceAttack yes — CombatCommenceAttack PARSED reg GameEventWiring.cs:270; parse Messages/GameEvents.cs:256 GameEventCombatCommenceAttack.cs Zero-length body (ACE's 4 ctor arg is a MemoryStream capacity hint, not content — GameEventMessage.cs:34). acdream requires payload.Length == 0. Complete.
0x01C0 UpdateHealth yes — UpdateHealth PARSED reg GameEventWiring.cs:222; parse Messages/GameEvents.cs:142 GameEventUpdateHealth.cs objectId u32 + health f32. Both read at the right widths.
0x01C3 QueryAgeResponse yes — QueryAgeResponse PARSED reg GameEventWiring.cs:118; parse Messages/GameEvents.cs:100 GameEventQueryAgeResponse.cs targetName + age, both String16L. Complete.
0x01C7 UseDone yes — UseDone PARSED reg GameEventWiring.cs:412; parse Messages/GameEvents.cs:423 GameEventUseDone.cs One u32 WeenieError. Complete.
0x01C8 AllegianceAllegianceUpdateDone yes — name mismatch: AllegianceUpdateDone STUB none GameEventAllegianceAllegianceUpdateDone.cs (WeenieError u32) ACE constructs it 5×. Dropped.
0x01C9 FellowshipFellowUpdateDone yes — FellowshipFellowUpdateDone STUB none GameEventFellowshipFellowUpdateDone.cs ACE writes no payload (the errorType ctor arg is never serialized — ACE bug/oversight). Dropped.
0x01CA FellowshipFellowStatsDone yes — FellowshipFellowStatsDone STUB none (no serializer class in ACE) ACE never sends it. Zero risk vs ACE.
0x01CB ItemAppraiseDone yes — ItemAppraiseDone STUB none (no serializer class in ACE) ACE never sends it. Retail uses it as the appraise-complete signal; acdream instead keys off 0x00C9 arrival.
0x01E2 Emote yes — Emote STUB none (no serializer class in ACE) ACE never sends it (emotes ride HearEmote/SoulEmote top-level opcodes, handled at Messages/SoulEmote.cs). Zero risk vs ACE.
0x01EA PingResponse yes — PingResponse PARSED reg WorldSession.cs:780; parse Messages/GameEvents.cs:153 GameEventPingResponse.cs Zero-length body; acdream asserts payload.IsEmpty and records the RTT. Complete.
0x01F4 SetSquelchDB yes — SetSquelchDB PARSED reg GameEventWiring.cs:158; parse Messages/SocialStateMessages.cs:45 GameEventCommunicationSetSquelch.cs + Structure/SquelchDB.cs, SquelchInfo.cs Reads accounts PackableHashTable (u16 count / u16 buckets — matches PackableHashTable.WriteHeader), characters table, then the global SquelchInfo. SquelchInfo order (filters → name → accountWide) matches both ACE and retail SquelchInfo::UnPack @ 0x006B11C0. All bytes consumed. Semantic hazard, not a coverage gap: acdream models _squelch_msgs as retail's AC1Legacy::vlong bit-vector (bit id = word*32+bit), but ACE writes the same SquelchMask 4× (SquelchInfo.cs:27), so acdream synthesizes phantom message-type ids 32127. See Finding 8.
0x01FD RegisterTrade yes — RegisterTrade STUB none GameEventRegisterTrade.cs (initiator, partner, 0L) ACE sends on trade open. Dropped — no trade subsystem.
0x01FE OpenTrade yes — OpenTrade STUB none (no serializer class in ACE) ACE never sends it.
0x01FF CloseTrade yes — CloseTrade STUB none GameEventCloseTrade.cs (EndTradeReason u32) Dropped.
0x0200 AddToTrade yes — AddToTrade STUB none — dead parser at Messages/GameEvents.cs:466 GameEventAddToTrade.cs (objectGuid, tradeSide, 0) Parser ParseAddToTrade exists but reads only 2 of ACE's 3 u32 (misses the trailing slot/location) and is never registered. See Finding 7.
0x0201 RemoveFromTrade yes — RemoveFromTrade STUB none (no serializer class in ACE) ACE never sends it.
0x0202 AcceptTrade yes — AcceptTrade STUB none — dead parser at Messages/GameEvents.cs:475 GameEventAcceptTrade.cs (whoAccepted guid) Parser exists, never registered.
0x0203 DeclineTrade yes — DeclineTrade STUB none GameEventDeclineTrade.cs (whoDeclined guid) Dropped.
0x0205 ResetTrade yes — ResetTrade STUB none GameEventResetTrade.cs (whoReset guid) Dropped.
0x0207 TradeFailure yes — TradeFailure STUB none — dead parser at Messages/GameEvents.cs:457 GameEventTradeFailure.cs (objectGuid, WeenieError) Parser reads only 1 of ACE's 2 u32 (misses objectGuid, actually reads it as the reason) and is never registered. See Finding 7.
0x0208 ClearTradeAcceptance yes — ClearTradeAcceptance STUB none GameEventClearTradeAcceptance.cs (empty body) Dropped.
0x021D HouseProfile yes — HouseProfile STUB none GameEventHouseProfile.cs (crystal guid + HouseProfile) Dropped — no housing subsystem.
0x0225 HouseData yes — HouseData STUB none GameEventHouseData.cs (HouseData blob) Dropped.
0x0226 HouseStatus yes — HouseStatus STUB none GameEventHouseStatus.cs (WeenieError u32) Dropped.
0x0227 UpdateRentTime yes — UpdateRentTime STUB none GameEventHouseUpdateRentTime.csnever constructed anywhere in ACE.Server Zero risk vs ACE.
0x0228 UpdateRentPayment yes — UpdateRentPayment STUB none GameEventHouseUpdateRentPayment.csnever constructed in ACE.Server Zero risk vs ACE.
0x0248 HouseUpdateRestrictions yes — HouseUpdateRestrictions STUB none GameEventHouseUpdateRestrictions.cs (sequence, guid, RestrictionDB) Dropped.
0x0257 UpdateHAR yes — UpdateHAR STUB none GameEventHouseUpdateHAR.cs (HouseAccess) Dropped.
0x0259 HouseTransaction yes — HouseTransaction STUB none GameEventHouseTransaction.csnever constructed in ACE.Server Zero risk vs ACE.
0x0264 QueryItemManaResponse yes — QueryItemManaResponse PARSED reg GameEventWiring.cs:229; parse Messages/GameEvents.cs:488 GameEventQueryItemManaResponse.cs 3 fields: target u32, mana f32, success u32. All read at the right widths.
0x0271 AvailableHouses yes — AvailableHouses STUB none GameEventHouseAvailableHouses.cs (type, List<uint>, totalAvailable) Dropped.
0x0274 CharacterConfirmationRequest yes — CharacterConfirmationRequest PARSED reg GameEventWiring.cs:129; parse Messages/GameEvents.cs:503 GameEventConfirmationRequest.cs confirmationType u32, context u32, text String16L. All read.
0x0276 CharacterConfirmationDone yes — CharacterConfirmationDone PARSED reg GameEventWiring.cs:139; parse Messages/GameEvents.cs:524 GameEventConfirmationDone.cs confirmationType u32, contextId u32. Both read.
0x027A AllegianceLoginNotification yes — AllegianceLoginNotification STUB none GameEventAllegianceLoginNotification.cs (playerGuid, isLoggedIn) ACE sends on allegiance-member login/logout. Dropped.
0x027C AllegianceInfoResponse yes — AllegianceInfoResponse STUB none GameEventAllegianceInfoResponse.cs (playerGuid + AllegianceProfile) Dropped.
0x0281 JoinGameResponse yes — JoinGameResponse STUB none GameEventJoinGameResponse.cs (boardGuid, color i32) Chess only. Dropped.
0x0282 StartGame yes — StartGame STUB none GameEventStartGame.cs (boardGuid, color i32) Chess only. Dropped.
0x0283 MoveResponse yes — MoveResponse STUB none GameEventMoveResponse.cs (boardGuid, result i32) Chess only. Dropped.
0x0284 OpponentTurn yes — OpponentTurn STUB none GameEventOpponentTurn.cs (boardGuid, color, ChessMoveData) Chess only. Dropped.
0x0285 OpponentStalemate yes — OpponentStalemate STUB none GameEventOpponentStalemate.cs (boardGuid, color, stalemate) Chess only. Dropped.
0x028A WeenieError yes — WeenieError PARSED reg GameEventWiring.cs:210; parse Messages/GameEvents.cs:115 GameEventWeenieError.cs One u32. Complete. ACE's single most-constructed event (239 sites).
0x028B WeenieErrorWithString yes — WeenieErrorWithString PARSED reg GameEventWiring.cs:215; parse Messages/GameEvents.cs:124 GameEventWeenieErrorWithString.cs errorType u32 + message String16L. Both read; order matches retail CM_Communication::DispatchUI_WeenieErrorWithString @ 0x006A5070 (u32 then string).
0x028C GameOver yes — GameOver STUB none GameEventGameOver.cs (boardGuid, teamWinner i32) Chess only. Dropped.
0x0295 SetTurbineChatChannels yes — SetTurbineChatChannels PARSED reg GameEventWiring.cs:175 and WorldSession.cs:775; parse Messages/SetTurbineChatChannels.cs:67 GameEventSetTurbineChatChannels.cs 10 sequential u32 read in ACE's exact order (allegiance, General, Trade, LFG, Roleplay, Olthoi, society, SocietyCelestialHand, SocietyEldrytchWeb, SocietyRadiantBlood). Complete. Double-registered: the dispatcher is single-handler-per-opcode, so the owned GameEventWiring handler shadows the WorldSession one while a session is live; WorldSession.TurbineChannelsReceived has zero subscribers anyway. See Finding 10.
0x02AE AdminQueryPluginList yes — AdminQueryPluginList STUB none (no serializer class in ACE) ACE never sends it.
0x02B1 AdminQueryPlugin yes — AdminQueryPlugin STUB none (no serializer class in ACE) ACE never sends it.
0x02B3 AdminQueryPluginResponse yes — AdminQueryPluginResponse STUB none (no serializer class in ACE) ACE never sends it.
0x02B4 SalvageOperationsResult yes — SalvageOperationsResult STUB none GameEventSalvageOperationsResult.cs (skill, 0, count, SalvageResult[], augBonus) ACE sends on salvage. Dropped.
0x02BD Tell yes — Tell PARTIAL reg GameEventWiring.cs:103; parse Messages/GameEvents.cs:54 GameEventTell.cs Missing field: the trailing u32 after chatMessageType (GameEventTell.cs:831/:841 — "not documented in the xml's, but is found in the pcaps"). acdream stops after chatType. Benign today (constant 0, nothing follows in the envelope), but it is an ACE-serialized field that is neither read nor documented as skipped; the Tell record's XML comment lists only 5 fields.
0x02BE FellowshipFullUpdate yes — FellowshipFullUpdate STUB none GameEventFellowshipFullUpdate.cs (PackableHashTable of fellows + name/leader/flags) ACE constructs it 6×; sent on any fellowship change. Dropped.
0x02BF FellowshipDisband yes — FellowshipDisband STUB none GameEventFellowshipDisband.cs (empty body) Dropped.
0x02C0 FellowshipUpdateFellow yes — FellowshipUpdateFellow STUB none GameEventFellowshipUpdateFellow.cs (guid, cp, lum, level, 3 max vitals, 3 cur vitals, shareLoot, name, updateType) Dropped.
0x02C1 MagicUpdateSpell yes — MagicUpdateSpell PARTIAL reg GameEventWiring.cs:282; parse Messages/GameEvents.cs:159 GameEventMagicUpdateSpell.cs Missing field: u16 layer. ACE writes (ushort spellId, ushort layer); acdream reads one u32 as the spell id. Same latent corruption as 0x01A8.
0x02C2 MagicUpdateEnchantment yes — MagicUpdateEnchantment PARSED reg GameEventWiring.cs:292; parse Messages/GameEvents.cs:300Messages/EnchantmentWireReader.cs:19 GameEventMagicUpdateEnchantment.cs + Structure/Enchantment.cs:150 All 14 base fields + the conditional spellSetId decoded in ACE's exact order and widths (u16 spellId, u16 layer, u16 category, u16 hasSpellSetId, u32 power, f64 startTime, f64 duration, u32 casterGuid, f32 degradeMod, f32 degradeLimit, f64 lastTimeDegraded, u32 statModType, u32 statModKey, f32 statModValue, [u32 spellSetId]) = 60 (+4). Complete.
0x02C3 MagicRemoveEnchantment yes — MagicRemoveEnchantment PARSED reg GameEventWiring.cs:307; parse Messages/GameEvents.cs:279 GameEventMagicRemoveEnchantment.cs Correctly reads (u16 spellId, u16 layer) in ACE's order and hands them to Spellbook.OnEnchantmentRemoved(layer, spellId) matching that method's (layerId, spellId) signature. Complete. (The XML comment at GameEvents.cs:270 says "(layerId, spellId)" — comment order is wrong, code order is right.)
0x02C4 MagicUpdateMultipleEnchantments yes — MagicUpdateMultipleEnchantments PARSED reg GameEventWiring.cs:297; parse Messages/GameEvents.cs:308EnchantmentWireReader.cs:60 GameEventMagicUpdateMultipleEnchantments.csnever constructed in ACE.Server i32 count + N × the 0x02C2 record. Complete; unexercised against ACE.
0x02C5 MagicRemoveMultipleEnchantments yes — MagicRemoveMultipleEnchantments PARSED reg GameEventWiring.cs:312; parse Messages/GameEvents.cs:323 GameEventMagicRemoveMultipleEnchantments.csnever constructed in ACE.Server i32 count + N × LayeredSpell(u16 spellId, u16 layer); order matches Structure/LayeredSpell.cs:39. Complete; unexercised against ACE.
0x02C6 MagicPurgeEnchantments yes — MagicPurgeEnchantments PARSED reg GameEventWiring.cs:329 GameEventMagicPurgeEnchantments.cs (empty body) No fields to decode; handler calls Spellbook.OnPurgeAll(). Complete.
0x02C7 MagicDispelEnchantment yes — MagicDispelEnchantment PARSED reg GameEventWiring.cs:318; parse Messages/GameEvents.cs:320 GameEventMagicDispelEnchantment.cs Same (u16 spellId, u16 layer) shape as 0x02C3. Complete.
0x02C8 MagicDispelMultipleEnchantments yes — MagicDispelMultipleEnchantments PARSED reg GameEventWiring.cs:323; parse Messages/GameEvents.cs:323 GameEventMagicDispelMultipleEnchantments.cs i32 count + N × LayeredSpell. Both ACE ctor overloads produce the same wire shape. Complete.
0x02C9 MiscPortalStormBrewing yes — name mismatch: PortalStormBrewing STUB none GameEventPortalStormBrewing.cs (f32 extent, default 0.4) ACE writes one float; acdream drops it. Portal-storm UI cue absent.
0x02CA MiscPortalStormImminent yes — name mismatch: PortalStormImminent STUB none GameEventPortalStormImminent.cs (f32 extent, default 0.6) Same.
0x02CB MiscPortalStorm yes — name mismatch: PortalStorm STUB none GameEventPortalStorm.cs (empty body) Dropped.
0x02CC MiscPortalstormSubsided yes — name mismatch: PortalStormSubsided STUB none GameEventPortalStormSubsided.cs (empty body) Dropped.
0x02EB CommunicationTransientString yes — CommunicationTransientString STUB (broken parser) reg GameEventWiring.cs:108; parse Messages/GameEvents.cs:73 GameEventCommunicationTransientString.cs A handler IS registered, but it produces zero state effect on every real payload. ACE writes exactly one String16L and nothing else; acdream's ParseTransient then requires 4 more bytes for a ChatType u32 and returns null when they are absent (GameEvents.cs:79). WriteString16L pads to a 4-byte multiple, so the remainder after the string is always exactly 0 bytes → 100% drop. ACE constructs this event at 134 sites — it is the third-most-common GameEvent on the wire. See Finding 1.
0x0312 MagicPurgeBadEnchantments yes — MagicPurgeBadEnchantments PARSED reg GameEventWiring.cs:331 GameEventMagicPurgeBadEnchantments.cs (empty body) No fields; handler calls Spellbook.OnPurgeBadEnchantments(). Complete.
0x0314 SendClientContractTrackerTable yes — SendClientContractTrackerTable STUB none GameEventSendClientContractTrackerTable.cs (ContractManager) ACE sends at login when contracts exist. Dropped.
0x0315 SendClientContractTracker yes — SendClientContractTracker STUB none GameEventSendClientContractTracker.cs (ContractTracker, deleteContract, setAsDisplay) Dropped.

Roll-up

Rows written: 104 (one per ACE enum entry; 0x0062 appears twice because ACE declares VendorInfoEvent and ApproachVendor at the same value).

Status Rows Notes
PARSED 38 Every ACE-serialized field decoded into typed output.
PARTIAL 3 0x01A8 MagicRemoveSpell, 0x02C1 MagicUpdateSpell (both drop u16 layer), 0x02BD Tell (drops ACE's trailing u32).
STUB 63 62 rows with no registered handler (→ _unhandledCounts) + 1 registered-but-always-null (0x02EB).
MISSING 0 acdream's enum covers all 103 distinct ACE values; nothing is absent from the enum.

Registered handlers: 42 (40 in GameEventWiring.cs, 2 in WorldSession.cs). Of the 62 unregistered rows, 13 correspond to sub-opcodes ACE never emits at all (0x0003, 0x0062-alias, 0x01CA, 0x01CB, 0x01E2, 0x01FE, 0x0201, 0x0227, 0x0228, 0x0259, 0x02AE, 0x02B1, 0x02B3), so the real ACE-facing gap is ~49 rows, dominated by fellowship (6), housing (8), chess (6), trade (8), allegiance (4), and books (5).

acdream-vs-ACE enum delta

  • Value coverage: identical. All 103 distinct ACE values are present in src\AcDream.Core.Net\Messages\GameEventType.cs; acdream declares no value ACE does not.
  • Missing alias (1): ACE's VendorInfoEvent = 0x0062 has no acdream counterpart (acdream keeps only ApproachVendor). Harmless — same value.
  • Name mismatches (5): AllegianceAllegianceUpdateDoneAllegianceUpdateDone (0x01C8); MiscPortalStormBrewingPortalStormBrewing (0x02C9); MiscPortalStormImminentPortalStormImminent (0x02CA); MiscPortalStormPortalStorm (0x02CB); MiscPortalstormSubsidedPortalStormSubsided (0x02CC). All are cosmetic (values agree) but they defeat cross-repo grep.
  • Stale doc: GameEventType.cs:4 says "All 94 GameEventType sub-opcodes"; the enum has 103. It also cites references/ACE/..., a path that is now empty in this tree.

Findings

1. CommunicationTransientString (0x02EB) is dropped 100% of the time — a phantom trailing ChatType field

GameEvents.ParseTransient (src\AcDream.Core.Net\Messages\GameEvents.cs:73-84):

string message = ReadString16L(payload, ref pos);
if (payload.Length - pos < 4) return null;          // <-- always taken
uint chatType = BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(pos));
  • ACE GameEventCommunicationTransientString.cs writes only Writer.WriteString16L(message). WriteString16L pads to a multiple of 4 including the 2 length bytes (ACE.Server\Network\Extensions.cs:12-21), and the 16-byte GameEvent header is already 4-aligned, so after the string there are exactly 0 bytes left. The < 4 guard fires every time and the handler at GameEventWiring.cs:108 never calls chat.OnSystemMessage.
  • Retail agrees with ACE. CM_Communication::DispatchUI_TransientString @ 0x006A4FD0 unpacks a single PStringBase<char> and calls Handle_Communication__TransientString(system, &string) — no second field. The "string + u32 type" shape acdream implemented belongs to a different message: CM_Communication::DispatchUI_TextboxString @ 0x006A4F30, which gates on opcode 0xF7E0 (a top-level GameMessage, not a 0xF7B0 sub-opcode) and passes *(uint32_t*)arg2 as the type.
  • Impact: ACE constructs this event at 134 call sites — it is the standard "system says something to you" channel (skill-up notices, quest text, transient status). All of it is silently discarded.
  • The unit test pins the wrong shape: tests\AcDream.Core.Net.Tests\Messages\GameEventDispatcherTests.cs:377-390 synthesizes MakeString16L(...) + u32(5) and asserts ChatType == 5. It is green against a payload ACE/retail never produce. (Same class as the "tests can pin wrong values" lesson in feedback_phase_a1_hotfix_saga.md.)
2. Three sites read ACE's (u16 spellId, u16 layer) pair as a single u32
Site ACE writer
Messages\GameEvents.cs:159 ParseMagicUpdateSpell GameEventMagicUpdateSpell.cs:225-227
Messages\GameEvents.cs:288 ParseMagicRemoveSpell GameEventMagicRemoveSpell.cs:189-190
Messages\PlayerDescriptionParser.cs:357 (shortcut spellId) Structure\Shortcut.cs:44-49LayeredSpell

Each returns spellId | (layer << 16). All three are correct only because ACE currently passes layer = 0 / a default LayeredSpell. The neighbouring 0x02C3/0x02C7 parsers get the same pair right (GameEvents.cs:279), so this is an inconsistency inside acdream, not a missing spec. Spell ids are ushort on the wire; the uint return type hides the truncation.

3. Tell (0x02BD) does not consume ACE's trailing u32

GameEventTell.cs:831 and :841 both end with Writer.Write(0u). ParseTell (GameEvents.cs:54-68) stops after chatType. No desync results (nothing follows in the envelope) but the field is neither read nor documented as skipped, and the Tell record's XML doc (GameEvents.cs:46-52) presents the message as 5 fields.

4. AttackDone fabricates an AttackSequence that is not on the wire

GameEvents.cs:264 returns new AttackDone(0u, <weenieError>) and GameEventWiring.cs:268 forwards that constant into CombatState.OnAttackDone(attackSequence, weenieError). ACE's GameEventAttackDone.cs writes a single u32. Any consumer that treats AttackSequence as meaningful is reading a hardcoded zero.

5. Enum divergences (see the delta section above)

Five renamed members, one missing alias, and a stale "All 94" doc comment at GameEventType.cs:4. Values are all correct, so this is a maintainability/grep risk rather than a wire risk.

6. Wiring-level field loss on the two damage notifications

Both parsers are complete, but GameEventWiring.cs discards decoded fields before they reach CombatState:

  • :245-247 OnDefenderNotification(name, **0u**, damageType, damage, hitQuadrant, critical)attackerGuid is hardcoded 0u (ACE genuinely does not send it, so the parameter is unsatisfiable from this message), and HealthPercent + AttackConditions are dropped.
  • :252-253 OnAttackerNotification(name, damageType, damage, (float)HealthPercent)Critical and AttackConditions are dropped. Critical-hit presentation therefore cannot be driven from 0x01B1.
7. Four parsers exist with no registration (dead code that also has bugs)

ParseTradeFailure (GameEvents.cs:457), ParseAddToTrade (:466), ParseAcceptTrade (:475), and ParseIdentifyResponseHeader (:355) are never wired into the dispatcher. Two of them are also wrong against ACE:

  • ParseTradeFailure reads one u32 and calls it the error code; GameEventTradeFailure.cs writes (objectGuid, reason) — the parser would return the guid.
  • ParseAddToTrade reads (itemGuid, slotIndex); GameEventAddToTrade.cs writes (objectGuid, tradeSide, 0 /*slot*/) — the second field is TradeSide, not a slot, and the real slot u32 is never read.
8. SquelchInfo filter semantics: acdream is retail-correct, ACE is not — expect phantom message-type ids

SocialStateMessages.ReadSquelchInfo (SocialStateMessages.cs:95-113) decodes the filter block as retail's AC1Legacy::vlong bit-vector (acclient.h:39464AC1Legacy::vlong _squelch_msgs), i.e. word w bit b ⇒ message type w*32+b. ACE writes the same SquelchMask four times (Structure\SquelchInfo.cs:27, with the comment "not sure why this is sent 4x"). Against ACE, acdream therefore materialises the same mask at bit offsets 0-31, 32-63, 64-95 and 96-127 — i.e. up to 3× phantom message-type ids per squelch entry. Byte consumption stays correct (no desync); only SquelchInfo.MessageTypes set membership is polluted. Worth a divergence-register row if SquelchState ever gates on specific ids.

9. PlayerDescription trailer boundary is recovered heuristically, and failure is silent

ACE writes Character.GameplayOptions as a raw byte[] with no length prefix (GameEventPlayerDescription.cs:571). PlayerDescriptionParser.TryHeuristicInventoryStart (:755-786) brute-forces 4-byte-aligned offsets until TryUnpackInventoryStrict happens to consume exactly to end-of-buffer. When no offset qualifies (or the trailer throws), the catch at :425-435 sets TrailerTruncated = true and returns empty inventory + equipment lists — and GameEventWiring.cs:628-662 then calls InitializeInventoryManifest / InitializeEquipmentManifest with those empty arrays without ever checking TrailerTruncated. A trailer mis-scan therefore presents as "logged in with no inventory and nothing equipped", with no error surfaced outside ACDREAM_DUMP_VITALS=1.

10. Dispatcher blind spots: swallowed handler exceptions and a shadowed double registration
  • GameEventDispatcher.Dispatch (GameEventDispatcher.cs:99-110) catches every handler exception and writes to Console.Error without incrementing _unhandledCounts. A parser that throws is invisible to the "which server events are firing that we don't parse?" diagnostic the class advertises at :11-13.
  • 0x0295 is registered twice — legacy at WorldSession.cs:775 and owned at GameEventWiring.cs:175. The dispatcher stores one handler per opcode (:63, :76), so the wiring handler wins for the whole session lifetime and WorldSession.TurbineChannelsReceived (declared WorldSession.cs:459) never fires. It has zero subscribers in src\, so this is dead wiring rather than a live bug, but it is the only opcode with competing owners.
11. Sub-opcodes ACE cannot emit (context for the STUB column)

No serializer class exists for 0x0003, 0x01CA, 0x01CB, 0x01E2, 0x01FE, 0x0201, 0x02AE, 0x02B1, 0x02B3. Serializer files exist but are never constructed anywhere in ACE.Server for 0x0227, 0x0228, 0x0259, 0x02C4, 0x02C5. The last two matter in the opposite direction: acdream's 0x02C4/0x02C5 parsers are complete but have never been exercised against a real ACE payload, so they are untested-in-practice PARSED rows.


3.3 GameAction sub-opcodes, inside 0xF7B1 (163 rows)

Row universe: all 163 entries of C:\Users\erikn\source\repos\client\ACE\Source\ACE.Server\Network\GameAction\GameActionType.cs.

acdream paths: all src\... / tests\... paths below are repo-relative.

ACE handler root: C:\Users\erikn\source\repos\client\ACE\Source\ACE.Server\Network\GameAction\Actions\ — the ACE handler column gives the bare filename under that folder.

Classification rule used: a builder counts as wired only when a call site outside src\AcDream.Core.Net\ reaches it. Nearly every builder is fronted by a WorldSession.SendXxx() wrapper (src\AcDream.Core.Net\WorldSession.cs:1589-2097); the "Live caller" column names the first out-of-Core.Net site that invokes that wrapper (or a delegate bound to it). Builders reachable only from tests\AcDream.Core.Net.Tests\ are BUILDER-ONLY.

Envelope note: every acdream builder writes the same 12-byte header (u32 0xF7B1, u32 gameActionSequence, u32 subOpcode) before its payload; that is verified once here and not repeated per row.

Sub-opcode ACE name acdream status Builder (file:line) Live caller (file:line) ACE handler Field-coverage notes
0x0005 SetSingleCharacterOption MISSING GameActionSetSingleCharacterOption.cs ACE reads u32 option, u32 value. No acdream builder — see Finding F1: acdream uses 0x01A1 instead and gets it wrong.
0x0008 TargetedMeleeAttack BUILT+WIRED src\AcDream.Core.Net\Messages\AttackTargetRequest.cs:39 src\AcDream.App\Combat\LiveCombatAttackOperations.cs:212; src\AcDream.Headless\Hosting\HeadlessGameplayOperations.cs:106 GameActionTargetedMeleeAttack.cs ACE reads u32 targetGuid, u32 attackHeight, f32 powerLevel. Builder writes exactly that order/width.
0x000A TargetedMissileAttack BUILT+WIRED src\AcDream.Core.Net\Messages\AttackTargetRequest.cs:56 src\AcDream.App\Combat\LiveCombatAttackOperations.cs:206; src\AcDream.Headless\Hosting\HeadlessGameplayOperations.cs:104 GameActionTargetedMissileAttack.cs ACE reads u32 targetGuid, u32 attackHeight, f32 accuracyLevel. Match.
0x000F SetAfkMode BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:94 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:339src\AcDream.App\UI\ClientCommandController.cs:247 GameActionSetAFKMode.cs ACE reads u32 afk. Builder writes u32 (away?1:0). Match.
0x0010 SetAfkMessage BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:98 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:340src\AcDream.App\UI\ClientCommandController.cs:262 GameActionSetAFKMessage.cs ACE reads String16L. Builder writes CP-1252 String16L padded to 4. Match.
0x0015 Talk BUILT+WIRED src\AcDream.Core.Net\Messages\ChatRequests.cs:43 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:143; src\AcDream.App\Net\LiveSessionCommandRouter.cs:109 GameActionTalk.cs ACE reads one String16L. Match.
0x0017 RemoveFriend BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:110 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:684 GameActionRemoveFriend.cs ACE reads u32 friendGuid. Match.
0x0018 AddFriend BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:107 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:680 GameActionAddFriend.cs ACE reads String16L name. Match.
0x0019 PutItemInContainer BUILT+WIRED src\AcDream.Core.Net\Messages\InteractRequests.cs:97 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:344; src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:802 GameActionPutItemInContainer.cs ACE reads u32 item, u32 container, i32 placement. Builder writes u32/u32/i32. Match.
0x001A GetAndWieldItem BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:155 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:314 GameActionGetAndWieldItem.cs ACE reads u32 itemGuid, i32 EquipMask. Builder writes u32/u32 — same 4-byte width, same bits. Match.
0x001B DropItem BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:143 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:315 GameActionDropItem.cs ACE reads u32 itemGuid. Match.
0x001D SwearAllegiance BUILDER-ONLY src\AcDream.Core.Net\Messages\AllegianceRequests.cs:34 no caller found (only tests\AcDream.Core.Net.Tests\Messages\AllegianceRequestsTests.cs:13) GameActionAllegianceSwearAllegiance.cs Payload correct (u32 patronGuid) but nothing in src\ calls it — no WorldSession wrapper exists either. Dead builder.
0x001E BreakAllegiance BUILDER-ONLY src\AcDream.Core.Net\Messages\AllegianceRequests.cs:43 no caller found (only tests\AcDream.Core.Net.Tests\Messages\AllegianceRequestsTests.cs:29) GameActionAllegianceBreakAllegiance.cs Payload correct (u32 targetGuid). Dead builder.
0x001F AllegianceUpdateRequest MISSING GameActionAllegianceUpdateRequest.cs ACE reads u32 uiPanel (bool).
0x0025 RemoveAllFriends BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:113 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:687 GameActionRemoveAllFriends.cs Parameterless on both sides. Match.
0x0026 TeleToPklArena BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:54 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:305src\AcDream.App\UI\ClientCommandController.cs:95 GameActionTeleToPklArena.cs Parameterless. Match.
0x0027 TeleToPkArena BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:51 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:304src\AcDream.App\UI\ClientCommandController.cs:88 GameActionTeleToPkArena.cs Parameterless. Match.
0x002C TitleSet MISSING GameActionSetTitle.cs ACE reads u32 title.
0x0030 QueryAllegianceName MISSING GameActionQueryAllegianceName.cs Parameterless.
0x0031 ClearAllegianceName MISSING GameActionClearAllegianceName.cs Parameterless.
0x0032 TalkDirect MISSING GameActionTalkDirect.cs ACE reads String16L message, u32 targetGuid. acdream only has name-addressed Tell (0x005D).
0x0033 SetAllegianceName MISSING GameActionSetAllegianceName.cs ACE reads String16L.
0x0035 UseWithTarget BUILT+WIRED src\AcDream.Core.Net\Messages\InteractRequests.cs:52 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:312 GameActionUseWithTarget.cs ACE reads u32 sourceObjectGuid, u32 targetObjectGuid. Builder writes source then target. Match.
0x0036 Use BUILT+WIRED src\AcDream.Core.Net\Messages\InteractRequests.cs:38 src\AcDream.App\Interaction\SelectionInteractionTransport.cs:27; src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:778 GameActionUseItem.cs ACE reads u32 itemGuid. Match.
0x003B SetAllegianceOfficer MISSING GameActionSetAllegianceOfficer.cs ACE reads String16L name, u32 officerLevel.
0x003C SetAllegianceOfficerTitle MISSING GameActionSetAllegianceOfficerTitle.cs ACE reads u32 level, String16L title.
0x003D ListAllegianceOfficerTitles MISSING GameActionListAllegianceOfficerTitles.cs Parameterless.
0x003E ClearAllegianceOfficerTitles MISSING GameActionClearAllegianceOfficerTitles.cs Parameterless.
0x003F DoAllegianceLockAction MISSING GameActionDoAllegianceLockAction.cs ACE reads u32 AllegianceLockAction.
0x0040 SetAllegianceApprovedVassal MISSING GameActionSetAllegianceApprovedVassal.cs ACE reads String16L.
0x0041 AllegianceChatGag MISSING GameActionAllegianceChatGag.cs ACE reads String16L name, u32 enabled.
0x0042 DoAllegianceHouseAction MISSING GameActionDoAllegianceHouseAction.cs ACE reads u32 AllegianceHouseAction.
0x0044 RaiseVital BUILT+PARTIAL src\AcDream.Core.Net\Messages\CharacterActions.cs:43 (→ :77) src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:625 GameActionRaiseVital.cs Width bug (F2). ACE reads u32 vital, u32 xpSpent; builder writes u32 id + u64 xp (WriteUInt64LittleEndian, CharacterActions.cs:84), a 24-byte body where ACE expects 20.
0x0045 RaiseAttribute BUILT+PARTIAL src\AcDream.Core.Net\Messages\CharacterActions.cs:39 (→ :77) src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:620 GameActionRaiseAttribute.cs Same u64 vs u32 xpSpent bug (F2).
0x0046 RaiseSkill BUILT+PARTIAL src\AcDream.Core.Net\Messages\CharacterActions.cs:47 (→ :77) src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:630 GameActionRaiseSkill.cs Same u64 vs u32 xpSpent bug (F2).
0x0047 TrainSkill BUILT+WIRED src\AcDream.Core.Net\Messages\CharacterActions.cs:51 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:636 GameActionTrainSkill.cs ACE reads u32 skill, i32 creditsSpent. Builder writes u32/u32 — same width. Match.
0x0048 CastUntargetedSpell BUILT+WIRED src\AcDream.Core.Net\Messages\CastSpellRequest.cs:41 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:376; src\AcDream.Headless\Hosting\HeadlessGameplayOperations.cs:217 GameActionMagicCastUntargetedSpell.cs ACE reads u32 spellId. Match.
0x004A CastTargetedSpell BUILT+WIRED src\AcDream.Core.Net\Messages\CastSpellRequest.cs:54 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:378; src\AcDream.Headless\Hosting\HeadlessGameplayOperations.cs:223 GameActionMagicCastTargetedSpell.cs ACE reads u32 targetGuid, u32 spellId. Match.
0x0053 ChangeCombatMode BUILT+WIRED src\AcDream.Core.Net\Messages\CharacterActions.cs:67 src\AcDream.Runtime\Gameplay\RuntimeCombatModeState.cs:88; src\AcDream.App\Combat\LiveCombatModeCommandController.cs:35 GameActionChangeCombatMode.cs ACE reads u32 newCombatMode. Match.
0x0054 StackableMerge BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:36 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:676 GameActionStackableMerge.cs ACE reads u32 from, u32 to, i32 amount. Match (amount written unsigned, same bits).
0x0055 StackableSplitToContainer BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:49 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:349, :670, :696 GameActionStackableSplitToContainer.cs ACE reads u32 stack, u32 container, i32 place, i32 amount. Match.
0x0056 StackableSplitTo3D BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:64 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:337 GameActionStackableSplitTo3D.cs ACE reads u32 stackId, i32 amount. Match.
0x0058 ModifyCharacterSquelch BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:129 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:718 GameActionModifyCharacterSquelch.cs ACE reads u32 squelch, u32 playerGuid, String16L name, u32 messageType. Builder writes exactly that order. Match.
0x0059 ModifyAccountSquelch BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:146 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:726 GameActionModifyAccountSquelch.cs ACE reads u32 squelch, String16L name. Match.
0x005B ModifyGlobalSquelch BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:156 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:731 GameActionModifyGlobalSquelch.cs ACE reads u32 squelch, u32 messageType. Match.
0x005D Tell BUILT+WIRED src\AcDream.Core.Net\Messages\ChatRequests.cs:73 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:147; src\AcDream.App\Net\LiveSessionCommandRouter.cs:238 GameActionTell.cs ACE reads String16L message then String16L target. Builder writes message-first (ChatRequests.cs:83-84) — a documented 2026-04-25 fix. Match.
0x005F Buy MISSING GameActionBuyItems.cs ACE reads u32 vendorGuid, u32 numItems, then per item i32 amount + u32 objectID. Slice-5 vendor work is still pending.
0x0060 Sell MISSING GameActionSellItems.cs ACE reads u32 vendorGuid, u32 numItems, then per item i32 amount + u32 objectGuid.
0x0063 TeleToLifestone BUILT+WIRED src\AcDream.Core.Net\Messages\InteractRequests.cs:69 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:179 GameActionTeleToLifestone.cs Parameterless. Match.
0x00A1 LoginComplete BUILT+WIRED src\AcDream.Core.Net\Messages\GameActionLoginComplete.cs:45 src\AcDream.Runtime\Session\RuntimeLiveEntitySessionController.cs:327; src\AcDream.App\Streaming\LocalPlayerTeleportController.cs:306 GameActionLoginComplete.cs Parameterless on both sides. Builder hardcodes sequence 0 (:49) with a comment that ACE ignores it — true for this handler, but it means LoginComplete does not advance the shared GameAction sequence.
0x00A2 FellowshipCreate BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:88 no caller found (only tests\...\Messages\SocialActionsTests.cs:53) GameActionFellowshipCreate.cs Dead and wrong (F3): ACE reads String16L name then one u32 shareXp; builder writes name + two loose bytes (openness, shareXp) at SocialActions.cs:100-101, so ACE would decode shareXp = openness | shareXp<<8.
0x00A3 FellowshipQuit BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:106 no caller found (tests only) GameActionFellowshipQuit.cs ACE reads u32 disband; builder writes 1 byte into a 4-byte zeroed slot → same u32 value. Layout OK, but dead.
0x00A4 FellowshipDismiss BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:117 no caller found (tests only) GameActionFellowshipDismiss.cs ACE reads u32 guid. Layout OK, dead.
0x00A5 FellowshipRecruit BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:121 no caller found (tests only) GameActionFellowshipRecruit.cs ACE reads u32 guid. Layout OK, dead.
0x00A6 FellowshipUpdateRequest BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:125 no caller found (tests only) GameActionFellowshipUpdateRequest.cs ACE reads i32 panelOpen; builder writes 1 byte into a zeroed 4-byte slot. Layout OK, dead.
0x00AA BookData MISSING GameActionBookData.cs ACE reads u32 bookGuid.
0x00AB BookModifyPage MISSING GameActionBookModifyPage.cs ACE reads u32 bookGuid, i32 page, String16L text.
0x00AC BookAddPage MISSING GameActionBookAddPage.cs ACE reads u32 bookGuid.
0x00AD BookDeletePage MISSING GameActionBookDeletePage.cs ACE reads u32 bookGuid, i32 page.
0x00AE BookPageData MISSING GameActionBookPageData.cs ACE reads u32 bookGuid, i32 pageNum.
0x00BF SetInscription BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:182 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:712 GameActionSetInscription.cs ACE reads u32 objectGuid, String16L text. Builder writes guid + CP-1252 String16L padded to 4 (:194). Match.
0x00C8 IdentifyObject BUILT+WIRED src\AcDream.Core.Net\Messages\AppraiseRequest.cs:33 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:310 GameActionIdentifyObject.cs ACE reads u32 objectGuid. Match.
0x00CD GiveObjectRequest BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:90 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:317 GameActionGiveObjectRequest.cs ACE reads u32 targetGuid, u32 objectGuid, i32 amount. Match.
0x00D6 AdvocateTeleport MISSING GameActionAdvocateTeleport.cs ACE reads String16L target + a Position.
0x0140 AbuseLogRequest MISSING (none — ACE has no handler) ACE declares the enum value but ships no [GameAction] handler; sending it is a no-op server-side.
0x0145 AddChannel BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:147 no caller found (only tests\...\Messages\SocialActionsTests.cs:115) GameActionAddChannel.cs Dead and wrong (F4): ACE reads u32 Channel id; builder writes a String16L channel name.
0x0146 RemoveChannel BUILDER-ONLY src\AcDream.Core.Net\Messages\SocialActions.cs:151 no caller found (tests only) GameActionRemoveChannel.cs Same u32 vs String16L mismatch as 0x0145 (F4).
0x0147 ChatChannel BUILT+WIRED src\AcDream.Core.Net\Messages\ChatRequests.cs:89 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:987; src\AcDream.App\Net\LiveSessionCommandRouter.cs:281 GameActionChatChannel.cs ACE reads u32 Channel, String16L message. Match.
0x0148 ListChannels MISSING GameActionChannelList.cs ACE reads u32 Channel.
0x0149 IndexChannels MISSING GameActionChannelIndex.cs Parameterless.
0x0195 NoLongerViewingContents BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:167 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:296 GameActionNoLongerViewingContents.cs ACE reads u32 objectGuid. Match.
0x019B StackableSplitToWield BUILDER-ONLY src\AcDream.Core.Net\Messages\InventoryActions.cs:76 no caller found (only tests\...\Messages\InventoryActionsTests.cs:56) GameActionStackableSplitToWield.cs Layout correct (u32 stack, i32 EquipMask, i32 amount) but no WorldSession wrapper and no production caller. Dead builder.
0x019C AddShortCut BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:108 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:442 GameActionAddShortcut.cs ACE ReadShortcut() = u32 Index, u32 ObjectId, then LayeredSpell = u16 SpellId + u16 Layer. Builder writes i32 Index, u32 ObjectId, u32 SpellId — 12 bytes either way; the third dword is only correct if ShortcutEntry.SpellId really is the packed spell+layer word its comment claims (F7).
0x019D RemoveShortCut BUILT+WIRED src\AcDream.Core.Net\Messages\InventoryActions.cs:121 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:463 GameActionRemoveShortcut.cs ACE reads u32 index. Match.
0x01A1 SetCharacterOptions BUILT+PARTIAL src\AcDream.Core.Net\Messages\SocialActions.cs:136 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:660 GameActionSetCharacterOptions.cs Severe (F1). ACE reads a whole PlayerModule: u32 flags, i32 characterOptions1, optional shortcut list, u32 numTab1Spells + list, optional spell tabs, desired components, u32 spellbookFilters, i32 characterOptions2, String16L, generic qualities, gameplay-options blob (handler lines 45-189). Builder writes one u32, which ACE consumes as flags — not as the options bitmap — then under-runs the payload on the very next ReadInt32().
0x01A8 RemoveSpellC2S BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:210 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:554 GameActionMagicRemoveSpellId.cs ACE reads u32 spellId. Match.
0x01B7 CancelAttack BUILT+WIRED src\AcDream.Core.Net\Messages\AttackTargetRequest.cs:73 src\AcDream.Runtime\Gameplay\RuntimeCombatAttackState.cs:267; src\AcDream.Headless\Hosting\HeadlessGameplayOperations.cs:113 GameActionCancelAttack.cs Parameterless on both sides. Match.
0x01BF QueryHealth BUILT+WIRED src\AcDream.Core.Net\Messages\SocialActions.cs:50 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:646 GameActionQueryHealth.cs ACE reads u32 objectGuid. Match.
0x01C2 QueryAge BUILT+PARTIAL src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:66 (→ :259) src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:308src\AcDream.App\UI\ClientCommandController.cs:104 GameActionQueryAge.cs Latent type bug (F5). ACE reads a String16L target; builder writes u32 objectId. Works today only because every caller passes the default 0, whose 4 zero bytes are byte-identical to an empty String16L. Any non-zero id becomes a bogus 16-bit string length.
0x01C4 QueryBirth BUILT+PARTIAL src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:69 (→ :259) src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:309src\AcDream.App\UI\ClientCommandController.cs:107 GameActionQueryBirth.cs Same u32-instead-of-String16L issue as 0x01C2 (F5).
0x01DF Emote BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:102 src\AcDream.App\Net\LiveSessionCommandRouter.cs:326; src\AcDream.App\UI\ClientCommandController.cs:174 GameActionEmote.cs ACE reads String16L. Match.
0x01E1 SoulEmote MISSING GameActionSoulEmote.cs ACE reads String16L. acdream's SoulEmote.cs is the inbound 0x01E2 event parser, not this outbound action.
0x01E3 AddSpellFavorite BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:185 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:487 GameActionAddSpellFavorite.cs ACE reads u32 spellId, u32 spellBarPositionId, u32 spellBarId. Builder writes spellId/position/tabIndex as u32/i32/i32 — same widths and order. Match.
0x01E4 RemoveSpellFavorite BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:196 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:513 GameActionRemoveSpellFavorite.cs ACE reads u32 spellId, u32 spellBarId. Match.
0x01E9 PingRequest BUILT+WIRED src\AcDream.Core.Net\Messages\SocialActions.cs:78 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:623 GameActionPingRequest.cs Parameterless. Match.
0x01F6 OpenTradeNegotiations MISSING GameActionOpenTradeNegotiations.cs ACE reads u32 tradePartnerGuid.
0x01F7 CloseTradeNegotiations MISSING GameActionCloseTradeNegotiations.cs Parameterless.
0x01F8 AddToTrade MISSING GameActionAddToTrade.cs ACE reads u32 itemGuid, u32 tradeSlot.
0x01FA AcceptTrade MISSING GameActionAcceptTrade.cs ACE reads u32 partner, f64 tradeStamp, u32 status, u32 initiator, u32 initiatorAccepts, u32 partnerAccepts.
0x01FB DeclineTrade MISSING GameActionDeclineTrade.cs Parameterless.
0x0204 ResetTrade MISSING GameActionResetTrade.cs Parameterless.
0x0216 ClearPlayerConsentList BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:165 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:345src\AcDream.App\UI\ClientCommandController.cs:293 GameActionClearPlayerConsentList.cs Parameterless. Match.
0x0217 DisplayPlayerConsentList BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:168 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:344src\AcDream.App\UI\ClientCommandController.cs:289 GameActionDisplayPlayerConsentList.cs Parameterless. Match.
0x0218 RemoveFromPlayerConsentList BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:171 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:346src\AcDream.App\UI\ClientCommandController.cs:302 GameActionRemoveFromPlayerConsentList.cs ACE reads String16L playerName. Match.
0x0219 AddPlayerPermission MISSING GameActionAddPlayerPermission.cs ACE reads String16L playerName.
0x021A RemovePlayerPermission MISSING GameActionRemovePlayerPermission.cs ACE reads String16L playerName.
0x021C BuyHouse MISSING GameActionHouseBuyHouse.cs ACE reads u32 slumlord, ListUInt32 items.
0x021E HouseQuery MISSING GameActionHouseQuery.cs Parameterless.
0x021F AbandonHouse MISSING GameActionHouseAbandon.cs Parameterless.
0x0221 RentHouse MISSING GameActionHouseRentHouse.cs ACE reads u32 slumlord, ListUInt32 items.
0x0224 SetDesiredComponentLevel BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:175 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:575; src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:361 GameActionSetDesiredComponentLevel.cs ACE reads u32 component_wcid, u32 amount. Match.
0x0245 AddPermanentGuest MISSING GameActionHouseAddPermanentGuest.cs ACE reads String16L guestName.
0x0246 RemovePermanentGuest MISSING GameActionHouseRemovePermanentGuest.cs ACE reads String16L guestName.
0x0247 SetOpenHouseStatus MISSING GameActionHouseSetOpenStatus.cs ACE reads u32 openHouse.
0x0249 ChangeStoragePermission MISSING GameActionHouseChangeStoragePermission.cs ACE reads String16L guestName, u32 hasPermission.
0x024A BootSpecificHouseGuest MISSING GameActionHouseBootSpecificGuest.cs ACE reads String16L playerName.
0x024C RemoveAllStoragePermission MISSING GameActionHouseRemoveAllStoragePermission.cs Parameterless.
0x024D RequestFullGuestList MISSING GameActionHouseRequestFullGuestList.cs Parameterless.
0x0254 SetMotd MISSING GameActionSetMotd.cs ACE reads String16L motd.
0x0255 QueryMotd MISSING GameActionQueryMotd.cs Parameterless.
0x0256 ClearMotd MISSING GameActionClearMotd.cs Parameterless.
0x0258 QueryLord MISSING GameActionHouseQueryLord.cs ACE reads u32 lord (slumlord id).
0x025C AddAllStoragePermission MISSING GameActionHouseAddAllStoragePermission.cs Parameterless.
0x025E RemoveAllPermanentGuests MISSING GameActionHouseRemoveAllPermanentGuests.cs Parameterless.
0x025F BootEveryone MISSING GameActionHouseBootEveryone.cs Parameterless.
0x0262 TeleToHouse BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:57 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:185 GameActionTeleToHouse.cs Parameterless. Match.
0x0263 QueryItemMana BUILT+WIRED src\AcDream.Core.Net\Messages\SocialActions.cs:64 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:647 GameActionQueryItemMana.cs ACE reads u32 objectGuid. Match (guid 0 cancels).
0x0266 SetHooksVisibility MISSING GameActionHouseSetHooksVisibility.cs ACE reads u32 visible.
0x0267 ModifyAllegianceGuestPermission MISSING GameActionHouseModifyAllegianceGuestPermission.cs ACE reads u32 add.
0x0268 ModifyAllegianceStoragePermission MISSING GameActionHouseModifyAllegianceStoragePermission.cs ACE reads u32 add.
0x0269 ChessJoin MISSING GameActionChessJoin.cs ACE reads u32 boardGuid, i32 color.
0x026A ChessQuit MISSING GameActionChessQuit.cs Parameterless.
0x026B ChessMove MISSING GameActionChessMove.cs ACE reads two ChessPieceCoord.
0x026D ChessMovePass MISSING GameActionChessMovePass.cs Parameterless.
0x026E ChessStalemate MISSING GameActionChessStalemate.cs ACE reads i32 stalemate.
0x0270 ListAvailableHouses MISSING GameActionHouseListAvailable.cs ACE reads u32 HouseType.
0x0275 ConfirmationResponse BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:73 src\AcDream.App\Composition\InteractionRetainedUiComposition.cs:705 GameActionConfirmationResponse.cs ACE reads i32 confirmType, u32 context, i32 response. Builder writes u32/u32/u32 — same widths, same order. Match.
0x0277 BreakAllegianceBoot MISSING GameActionBreakAllegianceBoot.cs ACE reads String16L playerName, u32 accountBoot.
0x0278 TeleToMansion BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:60 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:188 GameActionTeleToMansion.cs Parameterless. Match.
0x0279 Suicide BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:90 src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:330src\AcDream.App\UI\ClientCommandController.cs:145 GameActionDie.cs Parameterless. Match.
0x027B AllegianceInfoRequest MISSING GameActionAllegianceInfoRequest.cs ACE reads String16L playerName.
0x027D CreateTinkeringTool MISSING GameActionCreateTinkeringTool.cs ACE reads u32 toolGuid, u32 itemCount, then N u32 item guids.
0x0286 SpellbookFilter BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:206 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:535 GameActionSpellbookFilter.cs ACE reads u32 filters. Match.
0x028D TeleToMarketPlace BUILT+WIRED src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:48 src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:182 GameActionTeleToMarketplace.cs Parameterless. Match.
0x028F EnterPkLite MISSING GameActionEnterPkLite.cs Parameterless.
0x0290 FellowshipAssignNewLeader MISSING GameActionFellowshipAssignNewLeader.cs ACE reads u32 newLeaderID.
0x0291 FellowshipChangeOpenness MISSING GameActionFellowshipChangeOpenness.cs ACE reads u32 isOpen.
0x02A0 AllegianceChatBoot MISSING GameActionAllegianceChatBoot.cs ACE reads String16L playerName, String16L reason.
0x02A1 AddAllegianceBan MISSING GameActionAddAllegianceBan.cs ACE reads String16L playerName.
0x02A2 RemoveAllegianceBan MISSING GameActionRemoveAllegianceBan.cs ACE reads String16L playerName.
0x02A3 ListAllegianceBans MISSING GameActionListAllegianceBans.cs Parameterless.
0x02A5 RemoveAllegianceOfficer MISSING GameActionRemoveAllegianceOfficer.cs ACE reads String16L officerName.
0x02A6 ListAllegianceOfficers MISSING GameActionListAllegianceOfficers.cs Parameterless.
0x02A7 ClearAllegianceOfficers MISSING GameActionClearAllegianceOfficers.cs Parameterless.
0x02AB RecallAllegianceHometown MISSING GameActionRecallAllegianceHometown.cs Parameterless.
0x02AF QueryPluginListResponse MISSING (none — ACE has no handler) Client-side plugin-enumeration reply; ACE declares the enum value but ships no handler.
0x02B2 QueryPluginResponse MISSING (none — ACE has no handler) Same as 0x02AF.
0x0311 FinishBarber MISSING GameActionFinishBarber.cs ACE forwards the raw payload to Player.HandleActionFinishBarber(message) (appearance/palette blob).
0x0316 AbandonContract MISSING GameActionAbandonContract.cs ACE reads u32 contractId.
0xF61B Jump BUILT+WIRED src\AcDream.Core.Net\Messages\JumpAction.cs:39 src\AcDream.Runtime\Gameplay\LocalPlayerOutboundController.cs:73 GameActionJump.cs Oracle conflict, benign in practice (F6). Builder writes retail's JumpPack verbatim — f32 extent, vec3 velocity, Position (32 B), 4×u16 timestamps — confirmed by docs\research\named-retail\acclient.h:54020 and JumpPack::Pack/UnPack @ 0x00516d10/0x00516df0. ACE's reader omits the Position and then reads u32 objectGuid + u32 spellId we never write, so ACE's four sequence values and both trailing dwords are garbage; harmless only because Player.HandleActionJump (Player.cs:866) uses just Extent and Velocity.
0xF61C MoveToState BUILT+WIRED src\AcDream.Core.Net\Messages\MoveToState.cs:59 src\AcDream.Runtime\Gameplay\LocalPlayerOutboundController.cs:206 GameActionMoveToState.cs ACE's Network\Motion\MoveToState.cs reads RawMotionState, Position (cell + xyz + wxyz = 32 B), 4×u16, u8 contactLongJump, then aligns. Builder emits exactly this, including the 0x02|0x01 trailing byte and AlignTo4. Match.
0xF61E DoMovementCommand MISSING (none — ACE has no handler) ACE declares the value only; movement rides 0xF61C / 0xF753.
0xF649 TurnTo MISSING (none — ACE has no handler) Enum-only in ACE.
0xF661 StopMovementCommand MISSING (none — ACE has no handler) Enum-only in ACE.
0xF6EA ForceObjectDescSend MISSING (none — ACE has no handler) Enum-only in ACE.
0xF745 ObjectCreate N/A (server-side) (none) Server→client object creation (acdream parses it at src\AcDream.Core.Net\Messages\CreateObject.cs:44). Never sent by a client.
0xF747 ObjectDelete N/A (server-side) (none) Server→client despawn (parsed at src\AcDream.Core.Net\Messages\DeleteObject.cs:18).
0xF74C MovementEvent N/A (server-side) (none) Server→client UpdateMotion broadcast (parsed at src\AcDream.Core.Net\Messages\UpdateMotion.cs:65).
0xF750 ApplySoundEffect N/A (server-side) (none) Server→client sound broadcast.
0xF752 AutonomyLevel MISSING (none — ACE has no handler) Enum-only in ACE; retail sends it around server-control handoff.
0xF753 AutonomousPosition BUILT+WIRED src\AcDream.Core.Net\Messages\AutonomousPosition.cs:47 src\AcDream.Runtime\Gameplay\LocalPlayerOutboundController.cs:122, :170 GameActionAutonomousPosition.cs ACE reads Position (32 B), 4×u16, u8 lastContact, then aligns. Builder emits exactly that. Match.
0xF755 ApplyVisualEffect N/A (server-side) (none) Server→client effect broadcast (parsed at src\AcDream.Core.Net\Messages\PlayPhysicsScriptType.cs:17).
0xF7C9 JumpNonAutonomous MISSING (none — ACE has no handler) Enum-only in ACE; retail's server-controlled jump variant.

Roll-up

Status Count
BUILT+WIRED 56
BUILT+PARTIAL 6
BUILDER-ONLY 10
MISSING 86
N/A (server-side) 5
Total rows 163

Of the 86 MISSING rows, 9 are also unhandled by ACE (0x0140, 0x02AF, 0x02B2, 0xF61E, 0xF649, 0xF661, 0xF6EA, 0xF752, 0xF7C9), so 77 represent server-reachable actions acdream cannot currently send.

Coverage of the ACE-handled, client-originated subset (149 rows after removing the 5 server-side and 9 ACE-unhandled entries): 56 wired + 6 partial + 10 dead = 72 with builders, 77 with none — ~48.3% builder coverage, ~37.6% fully wired.

Findings

F1 — SetCharacterOptions (0x01A1) sends 1 dword where ACE parses a whole PlayerModule

SocialActions.BuildSetCharacterOptions (src\AcDream.Core.Net\Messages\SocialActions.cs:136-144) writes a 16-byte body: envelope + one u32 optionsBitmap. GameActionSetCharacterOptions.Handle (...\Actions\GameActionSetCharacterOptions.cs:45-189) reads, in order: u32 flags, i32 characterOptions1Flag (immediately applied via session.Player.SetCharacterOptions1), conditional shortcut array, u32 numTab1Spells + array, conditional 4-tab spell lists, desired-components map, u32 spellbookFilters, i32 characterOptions2Flag, a String16L, a generic-qualities block, and a trailing gameplay-options byte blob.

Two consequences: (a) our single dword lands in flags, i.e. it is interpreted as the presence bitmask, never as the options value; (b) the very next ReadInt32() runs off the end of a 4-byte payload. The correct single-toggle message is 0x0005 SetSingleCharacterOption (u32 option, u32 value), for which acdream has no builder at all. Live caller: src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:660.

F2 — RaiseAttribute / RaiseVital / RaiseSkill write a 64-bit xpSpent; the wire field is 32-bit

CharacterActions.BuildAttrOrVital (src\AcDream.Core.Net\Messages\CharacterActions.cs:77-86) allocates 24 bytes and does BinaryPrimitives.WriteUInt64LittleEndian(body.AsSpan(16), xp). Both independent client-side/server-side oracles use 32 bits:

  • ACE GameActionRaiseAttribute.cs:11var xpSpent = message.Payload.ReadUInt32(); (identically in GameActionRaiseVital.cs:12 and GameActionRaiseSkill.cs:11).
  • holtburger crates\holtburger-core\src\client\types.rs:99-109xp_spent: u32 for all three commands.

Effect: the server reads only the low dword (correct by luck for values under 4 G) and leaves 4 stray bytes at the tail of the fragment. The header comments at CharacterActions.cs:19-21 document the intended layout as u64, so the comment is wrong too. All three are live-wired (src\AcDream.Runtime\Session\DirectGameRuntimeCommandAdapter.cs:620/625/630), so this ships on the wire today whenever a player spends XP.

F3 — FellowshipCreate (0x00A2) packs two loose bools where ACE reads one dword

SocialActions.BuildFellowshipCreate (src\AcDream.Core.Net\Messages\SocialActions.cs:88-103) writes String16L name, then body[12+len] = openness, body[13+len] = shareXp, padded to 4. GameActionFellowshipCreate.cs:11-12 reads String16L fellowshipName then a single u32 shareXp. ACE therefore computes shareXp = openness | (shareXp << 8) — i.e. an unshared fellowship created with openness=true still reads as "share XP on". Currently harmless only because the builder is dead (F8).

F4 — AddChannel / RemoveChannel (0x0145 / 0x0146) send a channel name string; ACE reads a channel id dword

SocialActions.BuildAddChannel / BuildRemoveChannel (src\AcDream.Core.Net\Messages\SocialActions.cs:147-152, via SingleString at :166) emit a String16L. ACE reads (Channel)message.Payload.ReadUInt32() (GameActionAddChannel.cs:10, GameActionRemoveChannel.cs:10). The declaring comments at SocialActions.cs:46-47 assert "string16L channelName", which is the source of the error. Both builders are dead (F8), so nothing ships today — but these are exactly the two the tests lock in (tests\AcDream.Core.Net.Tests\Messages\SocialActionsTests.cs:115,126), so the tests currently pin the wrong shape.

F5 — QueryAge / QueryBirth (0x01C2 / 0x01C4) write a u32; ACE reads a String16L

ClientCommandRequests.BuildObjectQuery (src\AcDream.Core.Net\Messages\ClientCommandRequests.cs:259-267) appends u32 objectId. ACE reads String16L target (GameActionQueryAge.cs:13, GameActionQueryBirth.cs:13). This only works today because both live call sites take the objectId = 0 default (ClientCommandRequests.cs:66,69; wired at src\AcDream.App\Net\LiveSessionRuntimeFactory.cs:308-309), and four zero bytes happen to decode as an empty String16L + pad. The moment anyone passes a real object id the low 16 bits become a string length and the parse desynchronises.

F6 — Jump (0xF61B): acdream is retail-correct, ACE's reader is not — record the divergence

JumpAction.Build (src\AcDream.Core.Net\Messages\JumpAction.cs:39-81) writes f32 extent, vec3 velocity, Position (cell + xyz + quaternion = 32 B), 4×u16 timestamps, aligned — a 56-byte body. That matches the retail truth in docs\research\named-retail\acclient.h:54020-54029 (struct JumpPack : PackObj { float extent; Vector3 velocity; Position position; u16 instance_timestamp; u16 server_control_timestamp; u16 teleport_timestamp; u16 force_position_ts; }) and the JumpPack::Pack / JumpPack::UnPack bodies at 0x00516d10 / 0x00516df0, both of which call position.vtable->Pack/UnPack between the velocity and the timestamps and size the buffer as position_size + 0x18.

ACE's Network\Structure\JumpPack.cs reads extent + velocity + 4×u16 and then GameActionJump.cs:12-13 reads u32 objectGuid and u32 spellId — a 32-byte layout with no Position. Against acdream (and against retail) ACE's four sequence numbers are actually halves of cellId/position.X, and its objectGuid/ spellId are position.Y/position.Z bit patterns. This is currently harmless because Player.HandleActionJump (...\ACE.Server\WorldObjects\Player.cs:866-896) only consumes Extent and Velocity and never touches the sequences. holtburger's JumpData (crates\holtburger-protocol\src\messages\movement\actions.rs:73-139) agrees with ACE, not with the retail header — worth noting so nobody "fixes" acdream toward it.

No change recommended; this belongs in the divergence register as a server-oracle-vs-retail note, not as an acdream defect.

F7 — AddShortcut (0x019C) third dword is only correct if ShortcutEntry.SpellId is the packed spell word

InventoryActions.BuildAddShortcut (src\AcDream.Core.Net\Messages\InventoryActions.cs:108-118) writes i32 Index, u32 ObjectId, u32 SpellId. ACE's ReadShortcut (...\Network\Structure\Shortcut.cs:33-42) reads u32 Index, u32 ObjectId, then ReadLayeredSpell() = u16 SpellId + u16 Layer (...\Network\Structure\LayeredSpell.cs:30-37). Total width matches, so this is only a hazard if ShortcutEntry.SpellId ever holds a plain spell id in all 32 bits: ACE would then read the high half as a spell layer. The builder comment claims a "raw 32-bit spell word", which would be correct — flagging it as unverified rather than as a confirmed bug.

F8 — Dead builders (BUILDER-ONLY): 10 sub-opcodes with a builder and no production caller

Each of these has a builder in src\AcDream.Core.Net\Messages\ reachable only from tests\AcDream.Core.Net.Tests\; none has a WorldSession.SendXxx wrapper except where noted, and no site in src\AcDream.Runtime\, src\AcDream.App\, or src\AcDream.Headless\ invokes them.

Sub-opcode Builder Payload correct vs ACE?
0x001D SwearAllegiance AllegianceRequests.cs:34 yes
0x001E BreakAllegiance AllegianceRequests.cs:43 yes
0x00A2 FellowshipCreate SocialActions.cs:88 no — see F3
0x00A3 FellowshipQuit SocialActions.cs:106 yes
0x00A4 FellowshipDismiss SocialActions.cs:117 yes
0x00A5 FellowshipRecruit SocialActions.cs:121 yes
0x00A6 FellowshipUpdateRequest SocialActions.cs:125 yes
0x0145 AddChannel SocialActions.cs:147 no — see F4
0x0146 RemoveChannel SocialActions.cs:151 no — see F4
0x019B StackableSplitToWield InventoryActions.cs:76 yes

Seven of the ten are byte-correct and need only a wrapper plus a UI/command call site to become BUILT+WIRED — the cheapest coverage wins in this family.

F9 — One builder targets a sub-opcode that is not in ACE's enum at all

InventoryActions.BuildTeleToPoi (src\AcDream.Core.Net\Messages\InventoryActions.cs:132) declares TeleToPoiOpcode = 0x00B1 (:25). 0x00B1 does not appear in ACE's 163-entry GameActionType, and there is no [GameAction(0x00B1)] handler. It is also dead (only tests\AcDream.Core.Net.Tests\Messages\InventoryActionsTests.cs:111 touches it). Not a row above — recorded here so it is not mistaken for coverage.

F10 — LoginComplete (0x00A1) hardcodes GameAction sequence 0

GameActionLoginComplete.Build (src\AcDream.Core.Net\Messages\GameActionLoginComplete.cs:45-52) writes 0u for the sequence rather than calling WorldSession.NextGameActionSequence(). ACE's handler ignores the sequence, so this is safe against ACE, but it is the one GameAction acdream emits that does not participate in the outbound sequence counter — worth knowing if a stricter server or a wire-capture comparison ever looks at monotonicity. It is sent from three sites (src\AcDream.Core.Net\WorldSession.cs:1269, src\AcDream.Runtime\Session\RuntimeLiveEntitySessionController.cs:327, src\AcDream.App\Streaming\LocalPlayerTeleportController.cs:306).


4. Drift vs the Phase-M matrix

The 2026-06-04 catalog this audit was meant to diff against does not exist (section 0.2). The comparison below is therefore against docs/research/2026-05-10-phase-m-opcode-matrix.md, 11 weeks older than the missing catalog.

The universes are not the same size, so raw counts are not directly comparable. Phase-M scoped an "in-scope" subset per family; this audit takes ACE's full enums. Both are given below.

Family Phase-M universe Phase-M "acdream today" This audit's universe This audit's recognized
Transport flags 22 14 parse / 5 build 23 23/23 values match ACE
Optional-header fields 12 10 partial 12 all parsed in ACE's order
Top-level GameMessage 51 21 implemented 82 40 (36 parsed + 2 partial + 2 stub)
GameEvent (0xF7B0) 103 27 parsed / 26 wired 104 41 parsed-or-partial, 42 handlers
GameAction (0xF7B1) 96 24 built / 8 live callers 163 72 builders, 56 wired

Drift in our favour

Coverage roughly doubled in every family since 2026-05-10, which is what you would expect after M3 and M4 shipped casting, inventory, combat, and the world-interaction slices. GameAction live callers moved the most: 8 to 56.

Drift against us — the part worth reading

The matrix recorded a bug as correct. Its row for 0x02BC HearRangedSpeech reads acdream today = P+W, Phase M target = P+W, with the note "Shouts; same parser as 0x02BB". That note is the bug. Ranged speech carries an extra f32 range that local speech does not, so sharing the parser meant reading the range float's bits as the chat type. The matrix marked the row complete and moved on. Fixed in 7e95c45e.

This is the strongest argument for the mission's ground-in-source rule. A status column is a claim about code at a moment in time; it decays, and worse, it can be wrong on the day it is written. Three of this audit's confirmed bugs sat in rows the matrix had already marked done.

Two further rows the matrix over-credits:

  • 0x02EB CommunicationTransientString — the matrix lists the GameEvent as covered; it was dropping 100% of messages. Fixed in f416c577.
  • RaiseAttribute / RaiseVital / RaiseSkill — counted among the built actions; all three had the wrong field width. Fixed in f57db35c.

Unresolved from the matrix. Its section 1 transport rows still list four inbound parse gaps that remain open: Disconnect (0x8000) inbound teardown, NetError (0x100000) / NetErrorDisconnect (0x200000), and the EchoRequest (0x2000000) mirror-response builder. This audit confirms the flag values are correct but did not re-verify those four behaviours; they are carried in section 7.


5. Tests added

All test work landed in AcDream.Core.Net.Tests, which goes 600 to 659 (+59), zero skips.

Commit What Tests
d5b0765e AceWireWriter harness + VectorUpdate (0xF74E) and PlayScriptId/PlayEffect (0xF754/0xF755) golden tests +17
7e95c45e HearSpeech/HearRangedSpeech (0x02BB/0x02BC) golden tests +13
61193643 PacketHeaderFlags exhaustive value conformance vs ACE +24
f416c577 CommunicationTransientString corrected round-trip + padding-residue sweep +1
f57db35c RaiseAttribute corrected width + RaiseVital/RaiseSkill theory +4

The harness is the durable part

tests/AcDream.Core.Net.Tests/Messages/AceWireWriter.cs is a line-for-line mirror of ACE's Source/ACE.Server/Network/Extensions.cs writers — WriteString16L (including its pad-to-a-multiple-of-4-counting-the-length-bytes rule), WritePackedDword, WriteGuid, Align, Pad — with each primitive citing the ACE line it ports. It also exposes GameMessage(opcode) and GameEvent(guid, seq, type) constructors, the latter mirroring GameEvent/GameEventMessage.cs:21-25.

This matters more than the individual tests. A hand-typed hex fixture proves only that a parser agrees with whoever typed the hex; if the author misread the oracle, the test cements the misreading. That is exactly how the HearRangedSpeech, TransientString, and xpSpent bugs each survived — every one of them had a passing test built on the same wrong assumption as the parser. Generating fixtures from the server's own algorithm removes that failure mode. Future message families should extend this harness rather than hand-rolling bytes.

Three pre-existing tests were corrected, not deleted, each with a comment explaining what it used to assert and why that was wrong.


6. Parser changes

No new parsers were written for MISSING or STUB opcodes. The mission's bar was (a) observed in the corpus AND (b) fully specified by two oracles. Criterion (a) is unsatisfiable against this corpus (section 2), so the bar was not met by anything. That is the correct outcome, not a shortfall — and it is why section 7's first item is the instrumented re-run.

What did change is three confirmed decode/encode bugs in already-covered messages, each specified by at least two oracles and each verified by hand against primary source before the fix:

Commit Message Bug Oracles
7e95c45e HearRangedSpeech 0x02BC Shared 0x02BB's layout, so the f32 range field was read as chatType and the real chat type was never read ACE GameMessageHearRangedSpeech.cs; holtburger chat/types.rs HearRangedSpeechData
f416c577 CommunicationTransientString 0x02EB Required a trailing u32 chatType the server never sends; because the string is 4-byte padded, the guard tripped and every transient string was dropped ACE GameEventCommunicationTransientString.cs; retail Handle_Communication__TransientString at 0x0057d460; holtburger
f57db35c RaiseAttribute/Vital/Skill 0x0045/0x0044/0x0046 Wrote a 64-bit xpSpent, making a 24-byte action where the server expects 20 ACE GameActionRaiseAttribute.cs:11; holtburger RaiseAttributeData.xp_spent: u32

Severity honestly stated: only the transient-string bug had continuous user-visible effect. The ranged-speech chat type is not consumed downstream today, and ACE reads the low dword of xpSpent and ignores the tail, so the raise actions worked by luck about value ranges. All three were nonetheless wire divergences with passing tests defending them.

Deliberately not fixed

SetCharacterOptions (0x01A1) is a fourth confirmed live bug — acdream sends one dword where ACE parses an entire PlayerModule, and the correct single-toggle message is 0x0005 SetSingleCharacterOption, for which there is no builder. It is left open because the fix is a new builder plus a caller change in DirectGameRuntimeCommandAdapter, not a field-width correction, and it deserves its own slice. See section 7.


7. Open questions

Ordered by leverage.

  1. Re-run one connected session with ACDREAM_DUMP_OPCODES=1. This is the cheapest high-value action available and it unblocks the ranking this audit could not produce (section 2). One environment variable on an otherwise normal Coldeve session converts "128 MISSING rows, unknown priority" into a real work order. User-gated: it needs a connected client.

  2. Restore references/ (section 0.1). Three of the six documented oracles are empty directories in the main checkout, and CLAUDE.md's "cross-reference at least two of them" is currently unexecutable as written. Working copies exist at C:\Users\erikn\source\repos\client\ACE and C:\Users\erikn\source\repos\holtburger; Chorizite.ACProtocol was not found anywhere on disk and may need re-cloning. Until this is fixed every session re-solves it privately — four duplicate ACE copies under %TEMP% are the evidence.

  3. Fix the dangling MEMORY.md research links (section 0.2). The wire catalog, and by the same pattern the 2026-06-04 combat-math and property-enum drops, are indexed but absent. Either restore them or mark the index entries as lost — a link that silently resolves to nothing is worse than no link, because a future session will plan around a document that does not exist.

  4. SetCharacterOptions (0x01A1) is wrong on the wire today. Needs a 0x0005 SetSingleCharacterOption builder (u32 option, u32 value) and a caller change. Confirmed by hand against ACE's handler.

  5. UpdateObject (0xF7DB) is dropped. ACE's SerializeUpdateObject just calls SerializeCreateObject, so the body is byte-identical to one we already parse — 9 ACE send sites (tailoring, recipes, aetheria) are silently lost. A comment at WorldSession.cs:662 claims coverage that does not exist. Cheapest MISSING row on the board.

  6. CharacterError (0xF659) is discarded. ACE terminates sessions with an explicit reason code, including CharacterError.Logon for account-in-use (AuthenticationHandler.cs:160/189/192). We recognize none of them and die on a generic TimeoutException("CharacterList not received"). This does not change the root cause of the documented "exit 29 after a hard kill" wait — that is still ACE's session-cleanup delay — but it means the server is telling us why and we throw it away. Decoding one u32 turns a three-minute guessing game into a log line.

  7. Narrow the xpSpent chain to uint end to end. f57db35c fixed the wire; the API still carries ulong from CharacterSheetProvider through LiveSessionCommandRouter to WorldSession. Cosmetic, but it is the surface that allowed the mismatch.

  8. Ten dead GameAction builders, seven of which are already byte-correct and need only a wrapper plus a call site: SwearAllegiance (0x001D), BreakAllegiance (0x001E), the four Fellowship Quit/Dismiss/Recruit/Update (0x00A3-0x00A6), and StackableSplitToWield (0x019B). The other three (FellowshipCreate 0x00A2, AddChannel 0x0145, RemoveChannel 0x0146) have wrong payloads and tests pinning the wrong shape.

  9. GameEvent enum hygiene. One missing alias (VendorInfoEvent = 0x0062), five name mismatches vs ACE (AllegianceAllegianceUpdateDone, and the four MiscPortalStorm*), and a stale doc comment at GameEventType.cs:4 claiming "All 94" for a 103-entry enum while citing the now-empty references/ACE/... path. Values are all correct; only names and docs drift.

  10. Unverified by this audit, carried from the Phase-M matrix: inbound Disconnect (0x8000) teardown, NetError / NetErrorDisconnect (0x100000 / 0x200000) parsing, and the EchoRequest (0x2000000) mirror-response builder. Flag values are confirmed correct; the behaviours were not re-checked.

  11. Open questions the agents raised but could not settle, carried for whoever picks them up:

    • AddShortcut (0x019C): the third dword is correct only if ShortcutEntry.SpellId is already the packed spell+layer word. Unverified.
    • InventoryActions.BuildTeleToPoi targets 0x00B1, which is not in ACE's GameActionType enum and has no handler. Where did that number come from?
    • Jump (0xF61B): acdream matches retail's JumpPack decomp exactly, but ACE's reader omits the Position and reads two phantom dwords. Benign today because ACE only consumes Extent/Velocity — but it is a divergence between our two oracles and belongs in the divergence register rather than being silently tolerated.
    • SquelchInfo filters: acdream implements retail's vlong bit-vector correctly; ACE's 4x duplicate mask yields phantom message-type ids 32-127. Another oracle-vs-oracle disagreement where we currently follow retail.