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>
138 KiB
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.md — was never
committed and is not on disk.
git log --all -- "docs/research/2026-06-04-wire-message-catalog.md"→ emptygit log --all --diff-filter=A -- "*wire-message-catalog*"→ empty- filesystem sweep of
C:\Users\eriknfor*wire-message-catalog*→ no hits - the
research/subdirectory thatMEMORY.mdlinks 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:
- The row universe comes from ACE's three opcode enums rather than the catalog's 256. This is more complete, not less — see §1.
- 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 ifchain insrc/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 underACDREAM_DUMP_OPCODES=1, deduplicated to the first occurrence per opcode. - GameEvent:
Messages/GameEventDispatcher.csroutes by sub-opcode and counts misses in an_unhandledCountsbag. - GameAction: outbound builders across
Messages/*.cs, sent throughWorldSession.SendGameAction. Live callers live outsideCore.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 theRaiseAttribute/RaiseVital/RaiseSkilltrio (were BUILT+PARTIAL). Those four rows read PARSED / BUILT+WIRED as off57db35c. 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
_unhandledCountsbag. 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 ObjectCreate — SerializeUpdateObject 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 0x01–0x1F, 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 invertedOrientationHasNo*semantics and all four trailing u16 sequences, againstNetwork\Structure\PositionPack.cs.UpdateMotion(0xF74C) — including ACE'sAlign()being computed on the absolute stream length (Network\Extensions.cs:55), which yields exactly one pad byte at offset 15; and the non-sequentialMovementStateFlagbit→write-order mapping (0x01,0x02,0x08,0x20ushorts then0x04,0x10,0x40floats).- All
WeenieHeaderFlagbit values and field widths inCreateObjectagainstACE.Entity\Enum\WeenieHeaderFlags.csand theWorldObject_Properties.csproperty types (ItemCapacity/ContainerCapacityarebyte?→ 1 byte,Script/HookType/MaxStructureareushort?→ 2 bytes,Workmanshipisfloat?→ 4 bytes,CooldownDurationpromotes todouble→ 8 bytes). - The "sequence" prefix on every
0x02xxproperty message is a 1-byteByteSequence(Network\Sequence\SequenceManager.cs:179default 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.csalways passesASYNCMETHOD_SENDTOROOMBYNAME).DddInterrogationResponse(0xF7E6) empty-list encoding is accepted byte-exactly by ACE'sReadCAllIterationList.
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 —VendorInfoEventandApproachVendorshare0x0062) and...\GameEvent\Events\*.cs(+...\Network\Structure\*.csfor 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:1489→GameEventDispatcher.Dispatch(src\AcDream.Core.Net\Messages\GameEventDispatcher.cs:95). - Registrations: only two files —
src\AcDream.Core.Net\GameEventWiring.cs(40 owned) andsrc\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 32–127. 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.cs — never constructed anywhere in ACE.Server |
Zero risk vs ACE. |
| 0x0228 | UpdateRentPayment | yes — UpdateRentPayment |
STUB | none | GameEventHouseUpdateRentPayment.cs — never 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.cs — never 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:300 → Messages/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:308 → EnchantmentWireReader.cs:60 |
GameEventMagicUpdateMultipleEnchantments.cs — never 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.cs — never 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 = 0x0062has no acdream counterpart (acdream keeps onlyApproachVendor). Harmless — same value. - Name mismatches (5):
AllegianceAllegianceUpdateDone→AllegianceUpdateDone(0x01C8);MiscPortalStormBrewing→PortalStormBrewing(0x02C9);MiscPortalStormImminent→PortalStormImminent(0x02CA);MiscPortalStorm→PortalStorm(0x02CB);MiscPortalstormSubsided→PortalStormSubsided(0x02CC). All are cosmetic (values agree) but they defeat cross-repo grep. - Stale doc:
GameEventType.cs:4says "All 94GameEventTypesub-opcodes"; the enum has 103. It also citesreferences/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.cswrites onlyWriter.WriteString16L(message).WriteString16Lpads 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< 4guard fires every time and the handler atGameEventWiring.cs:108never callschat.OnSystemMessage. - Retail agrees with ACE.
CM_Communication::DispatchUI_TransientString @ 0x006A4FD0unpacks a singlePStringBase<char>and callsHandle_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 opcode0xF7E0(a top-level GameMessage, not a 0xF7B0 sub-opcode) and passes*(uint32_t*)arg2as 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-390synthesizesMakeString16L(...) + u32(5)and assertsChatType == 5. It is green against a payload ACE/retail never produce. (Same class as the "tests can pin wrong values" lesson infeedback_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-49 → LayeredSpell |
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-247OnDefenderNotification(name, **0u**, damageType, damage, hitQuadrant, critical)—attackerGuidis hardcoded0u(ACE genuinely does not send it, so the parameter is unsatisfiable from this message), andHealthPercent+AttackConditionsare dropped.:252-253OnAttackerNotification(name, damageType, damage, (float)HealthPercent)—CriticalandAttackConditionsare 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:
ParseTradeFailurereads one u32 and calls it the error code;GameEventTradeFailure.cswrites(objectGuid, reason)— the parser would return the guid.ParseAddToTradereads(itemGuid, slotIndex);GameEventAddToTrade.cswrites(objectGuid, tradeSide, 0 /*slot*/)— the second field isTradeSide, 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:39464 — AC1Legacy::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 toConsole.Errorwithout 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.0x0295is registered twice — legacy atWorldSession.cs:775and owned atGameEventWiring.cs:175. The dispatcher stores one handler per opcode (:63,:76), so the wiring handler wins for the whole session lifetime andWorldSession.TurbineChannelsReceived(declaredWorldSession.cs:459) never fires. It has zero subscribers insrc\, 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:339 → src\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:340 → src\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:305 → src\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:304 → src\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:308 → src\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:309 → src\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:345 → src\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:344 → src\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:346 → src\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:330 → src\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:11—var xpSpent = message.Payload.ReadUInt32();(identically inGameActionRaiseVital.cs:12andGameActionRaiseSkill.cs:11). - holtburger
crates\holtburger-core\src\client\types.rs:99-109—xp_spent: u32for 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 inf416c577.RaiseAttribute/RaiseVital/RaiseSkill— counted among the built actions; all three had the wrong field width. Fixed inf57db35c.
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.
-
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. -
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 atC:\Users\erikn\source\repos\client\ACEandC:\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. -
Fix the dangling
MEMORY.mdresearch 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. -
SetCharacterOptions(0x01A1) is wrong on the wire today. Needs a0x0005 SetSingleCharacterOptionbuilder (u32 option, u32 value) and a caller change. Confirmed by hand against ACE's handler. -
UpdateObject(0xF7DB) is dropped. ACE'sSerializeUpdateObjectjust callsSerializeCreateObject, so the body is byte-identical to one we already parse — 9 ACE send sites (tailoring, recipes, aetheria) are silently lost. A comment atWorldSession.cs:662claims coverage that does not exist. Cheapest MISSING row on the board. -
CharacterError(0xF659) is discarded. ACE terminates sessions with an explicit reason code, includingCharacterError.Logonfor account-in-use (AuthenticationHandler.cs:160/189/192). We recognize none of them and die on a genericTimeoutException("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 oneu32turns a three-minute guessing game into a log line. -
Narrow the
xpSpentchain touintend to end.f57db35cfixed the wire; the API still carriesulongfromCharacterSheetProviderthroughLiveSessionCommandRoutertoWorldSession. Cosmetic, but it is the surface that allowed the mismatch. -
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), andStackableSplitToWield(0x019B). The other three (FellowshipCreate0x00A2,AddChannel0x0145,RemoveChannel0x0146) have wrong payloads and tests pinning the wrong shape. -
GameEvent enum hygiene. One missing alias (
VendorInfoEvent = 0x0062), five name mismatches vs ACE (AllegianceAllegianceUpdateDone, and the fourMiscPortalStorm*), and a stale doc comment atGameEventType.cs:4claiming "All 94" for a 103-entry enum while citing the now-emptyreferences/ACE/...path. Values are all correct; only names and docs drift. -
Unverified by this audit, carried from the Phase-M matrix: inbound
Disconnect (0x8000)teardown,NetError/NetErrorDisconnect(0x100000 / 0x200000) parsing, and theEchoRequest (0x2000000)mirror-response builder. Flag values are confirmed correct; the behaviours were not re-checked. -
Open questions the agents raised but could not settle, carried for whoever picks them up:
AddShortcut(0x019C): the third dword is correct only ifShortcutEntry.SpellIdis already the packed spell+layer word. Unverified.InventoryActions.BuildTeleToPoitargets0x00B1, which is not in ACE'sGameActionTypeenum and has no handler. Where did that number come from?Jump(0xF61B): acdream matches retail'sJumpPackdecomp 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.SquelchInfofilters: acdream implements retail'svlongbit-vector correctly; ACE's 4x duplicate mask yields phantom message-type ids 32-127. Another oracle-vs-oracle disagreement where we currently follow retail.