acdream/docs/research/2026-08-09-chat-retail-command-registry.md
Erik e306c979ae docs: Campaign CH R1/R2/R4 research + ledger correction (CH1 = 172c6f9a)
Commits the command-registry, interface-text (SpewBox), and
side-channels-vs-ACE research docs (R3 color-table landed with CH1).
Corrects the CH1 ledger SHA the implementer recorded pre-amend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 15:25:29 +02:00

29 KiB

Retail client slash-command registry — complete enumeration + acdream audit

Date: 2026-08-09 Status: RESEARCH ONLY. No production code was changed.

Oracle: docs/research/named-retail/acclient_2013_pseudo_c.txt (Sept 2013 EoR build) plus byte-level decode of the PDB-paired binary C:\Users\erikn\Downloads\acclient.exe (v11.4186, CodeView GUID 9e847e2f-777c-4bd9-886c-22256bb87f32). Every verb string in this document was read out of .rdata at the exact push imm32 operand feeding PStringBase<char>::PStringBase<char> inside the registration loop — none were inferred from Binary Ninja's symbolic rendering, which mislabels several of the pooled single-character strings as wide-string slices (&*U"fvpca"[4]) and two as vtable fields.

Extends docs/research/2026-07-13-retail-client-command-families-pseudocode.md, which already proved out the families acdream ported (lifestone/marketplace/ arena/age/birth/framerate/lockui/die/loc/corpse/clear/UI-layout/friends/afk/ consent/squelch/filter/emote/fillcomps). Those pseudocode blocks are not repeated here.


1. Registry mechanism

There is one command table and two functions that populate it: ClientCommunicationSystem::m_hashCommands, an IntrusiveHashTable<CaseInsensitiveStringBase<PStringBase<char>>, CmdHashData*> sized to 100 buckets in the ctor at 0x0058555D. ClientCommunicationSystem::InitializeCommands @ 0x00581970 (spanning to 0x00585520) registers 116 entries; CmdHashData::CmdHashData @ 0x0056ED30 stores +0x00 verb, +0x08 func (uint8_t (*)(this, int argc, char** argv)), +0x0C unused-in-practice, +0x10 help (uint8_t (*)(this, HelpType, char const*, PStringBase<char>*)), +0x14 unused. ClientCommunicationSystem::StartupTurbineChatSystem @ 0x0057EFB0 runs only when the server enables Turbine Chat; it removes the a entry and adds 15 entries (a, guild, gu, general, cg, trade, ct, lfg, clfg, roleplay, crp, society, soc, olthoi, o), i.e. 14 net-new verbs. Nothing else ever calls IntrusiveHashTable<…CmdHashData*>::add — verified by grepping every call site (18 in 0x0057xxxx, all inside StartupTurbineChatSystem plus the two hash-table helpers themselves; 116 in 0x0058xxxx, all inside InitializeCommands).

Dispatch is ClientCommunicationSystem::OnChatCommand @ 0x00581320ClientCommunicationSystem::DoCommand @ 0x0057E2E0. OnChatCommand switches on firstChar - 0x2F: '/' (case 0) is rewritten in place to '@' and falls into DoCommand; '@' (case 0x11) enters DoCommand directly; ':' and ';' (cases 0x0B/0x0C) have their first character replaced with a space and the whole line prefixed with the literal "@emote" before DoCommand — that is retail's emote shorthand. Anything else routes to PublicChat / talk-focus. DoCommand splits the line on " \t" (PSUtils::FindAllWords, delimiter set at 0x007E0F5C), takes words[0].substring(1) as the verb, right-trims ',' from it (trim char set 0x0079452C, trim(left=0, right=1)) so @f, hello works, then looks the verb up. If the entry is absent or its func is NULL, it calls ClientCommunicationSystem::DoChannelCommand @ 0x005774A0, which tries ChannelSystem::GetChannelID @ 0x005CF1F0 on the verb and, on a hit, sends CM_Communication::Event_ChannelBroadcast(id, joinedArgs). Only if that also fails does the client fall back to CM_Communication::Event_Talk with the original @-prefixed line — which is exactly the passthrough ACE relies on (references/ACE/Source/ACE.Server/Network/GameAction/Actions/GameActionTalk.cs:21, if (message.StartsWith("@"))CommandManager). A registered handler that returns 0 raises HandleFailureEvent(0x26, "").


2. Complete retail command table

One row per handler. A in the Turbine column = the row exists only after StartupTurbineChatSystem. acdream paths are relative to the repo root.

2.1 Help / group nodes

Verb Aliases Handler @addr Args Behavior acdream status
help ? DoHelp @ 0x0057F9E0 [command|group] No args: prints the group index + "Note: You may substitute a forward slash (/) for the at symbol (@)." With an arg: looks the verb up and calls its help fn. PARTIALChatCommandRouter.TryHandleLocalPresentationCommand (src/AcDream.UI.Abstractions/Panels/Chat/ChatCommandRouter.cs:96) prints one flat blob for /help, /?, /h. No @help <command>, no group topics. /h is an acdream invention (not a retail verb).
commands (NULL func), help HelpAllGroup @ 0x0057E7F0 Help-topic node only. Typing it alone falls through to channel lookup then server. MISSING
allegiances (NULL func), HelpAllegiancesGroup @ 0x0057D4C0 Help topic. MISSING
channels (NULL func), HelpChannelsGroup @ 0x005773E0 Help topic. MISSING
chatting (NULL func), HelpChattingGroup @ 0x0057B1A0 Help topic. MISSING
death (NULL func), HelpDeathGroup @ 0x0057B8B0 Help topic. MISSING
status (NULL func), HelpStatusGroup @ 0x0057C410 Help topic. MISSING
text (NULL func), HelpTextGroup @ 0x0057C6C0 Help topic. MISSING

2.2 Local chat routing

Verb Aliases Handler @addr Args Behavior acdream status
say s DoSay @ 0x00581240 <text> Joins + trims args, PublicChat. Empty → "You must specify the text you wish to say." IMPLEMENTED-EQUIVALENT — ChatInputParser SayAliases = {"/say","/s"} (ChatInputParser.cs:41). Not a catalog command; routed as SendChatCmd(Say).
tell t, send, whisper, w DoTell @ 0x00577E40 <name>, <text> Splits on the first comma; name = left, text = right. Sets SetLastTelleeName, sends CM_Communication::Event_TalkDirectByName. Retail help is explicit: "you must put a comma after the character's name." PARTIALChatInputParser.TellAliases = {"/tell","/t"} only; send/whisper/w MISSING. acdream splits on whitespace first and merely trims a trailing comma off the target (ChatInputParser.cs:208-220), so a multi-word name (/t Lord Gnarly, hi) resolves the wrong target where retail resolves it correctly.
reply r, rp DoReply @ 0x00577910 <text> Sends Event_TalkDirect to gmCCommunicationSystem::GetLastTeller() (a guid, not a name). No teller → "Someone must @tell you first!" PARTIAL/DIVERGENTReplyAliases = {"/reply","/r"}; rp is MISSING and is bound to Roleplay in acdream (ChatInputParser.cs:78). acdream replies by name, retail by guid.
mr NULL func, help HelpReply @ 0x00577A50 <text> Documented as "reply to the last person who @m'd you (monarchs only)" but the 2013 build registers it with a null function pointer (verified at 0x00583041: xor edi,edi; xor ebp,ebp before the ctor call — arg3 is 0). It therefore falls through to channel lookup (miss) and is sent to the server as text. A retail defect, not an acdream one. MISSING (and do not implement client-side — retail does not)
pr NULL func, help HelpReply <text> Same as mr (verified at 0x005830C1). MISSING (same note)
retell rt DoReTell @ 0x00577BD0 <text> Re-sends to the last person you tell'd. PARTIALRetellAliases = {"/retell"}; rt MISSING.
chat DoChatToggle @ 0x0056FAD0 on|off onEvent_ModifyGlobalSquelch(remove, 2); offEvent_ModifyGlobalSquelch(add, 2) — i.e. it is a global Speech filter, not a separate flag. MISSING (cheap: acdream already has ModifyGlobalSquelch)
notell DoNoTell @ 0x0056FBD0 on|off Same mechanism with message type 3 (Tell). MISSING (same)
join DoJoinChat @ 0x0056F510 <Allegiance|General|Trade|LFG|Roleplay|Society|Soc> Sets the matching PlayerModule::SetHear*Chat(true) Turbine-room flag. MISSING
leave DoLeaveChat @ 0x0056F7F0 same tags Clears the matching flag. MISSING
index DoChannelIndex @ 0x0056E640 CM_Communication::Event_ChannelIndex(). ACE: GameActionType.IndexChannels. MISSING
clist DoChannelList @ 0x0057A9B0 <channel> GetChannelID then Event_ChannelList(id); bad tag → WeenieError 0x422; no arg → "Please specify the channel name." ACE: ListChannels. MISSING
on DoChannelOn @ 0x0057AA80 <channel> Event_AddToChannel(id). ACE: AddChannel. MISSING
off DoChannelOff @ 0x0057AB50 <channel> Event_RemoveFromChannel(id). ACE: RemoveChannel. MISSING
title DoTitle @ 0x0057A640 <text> Sets the popup chat window's title. Pure local UI. MISSING
log DoSetOutput @ 0x0057E4F0 [filename] Toggles chat-to-file logging; appends .txt; "Copying chat to %s. Run command again with no arguments to turn off logging." MISSING
clear DoClear @ 0x0056E600 [all] IMPLEMENTED IMPLEMENTEDRetailClientCommandCatalog.cs:115, ClientCommandController.cs:165.
filter / unfilter DoFilter @ 0x0057C860 / DoUnFilter @ 0x0057C880 -<type> IMPLEMENTED IMPLEMENTEDClientCommandController.ExecuteGlobalFilter.
messagetypes message_types, msgtypes, msg_types DoMessageTypes @ 0x0057A010 Lists squelch/filter categories. PARTIAL — only messagetypes registered (RetailClientCommandCatalog.cs:251); the three underscore/short aliases are MISSING.
loadfile DoLoadFile @ 0x00581870 / LoadFile @ 0x00581710 <file> fopen the file, then feed every line back through OnChatCommand after MakeLoadFileVariableSubstitutions. A script player. No arg → "You must provide a file name."; open failure → "Cannot open file %hs". MISSING (see §3 risk note)

2.3 Chat channels (DoStupidChannelHack @ 0x0057B130DoChannelCommand)

All of these are one handler: no args → "You must specify the text you wish to say."; otherwise ChannelSystem::GetChannelID(verb) then CM_Communication::Event_ChannelBroadcast(id, text).

Channel Registered verbs Channel id acdream status
Allegiance a, ab (also allegiance/all as a command, see §2.4) 0x02000000 /a IMPLEMENTED-EQUIVALENT (ChatInputParser.cs:63). ab MISSING. /allegiance is bound to the Allegiance channel in acdream but is a COMMAND in retail — divergence.
Co-vassals co-vassals, covassals, covassal, c 0x01000000 covassals OK; c, covassal, co-vassals MISSING. acdream's /cv is an invention.
Monarch monarch, m 0x4000 IMPLEMENTED-EQUIVALENT
Patron patron, p 0x2000 IMPLEMENTED-EQUIVALENT
Vassals vassals, vassal, v 0x1000 vassals, v OK; vassal MISSING.
Fellowship fellowship, fellows, fellow, f, group, g, party 0x800 DIVERGENT — retail's g and group and party are Fellowship. acdream maps /g to General (ChatInputParser.cs:57). fellows, group, party MISSING.

ChannelSystem::GetChannelID also resolves 22 tags that are not in the command table. Because DoCommand falls through to DoChannelCommand for any unregistered verb, these still work as channel broadcasts:

Tags Channel id
av, av1, advocate, advocate1 0x08
av2, advocate2 0x10
av3, advocate3 0x20
abuse 0x01
ad, admin 0x02
au, audit 0x04
sent, sentinel 0x200
celestialhand, celhan 0x08000000
eldrytchweb, eldweb 0x10000000
radiantblood, radblo 0x20000000
ol (and olthoi pre-Turbine) 0x40000000
help 0x400explicitly rejected by DoChannelCommand at 0x005774FD (`id == 0

All 22 are SERVER-PASSTHROUGH in acdream today (they reach ACE as @abuse … text rather than a channel broadcast).

2.4 Turbine Chat (registered only by StartupTurbineChatSystem @ 0x0057EFB0)

Each sends ClientCommunicationSystem::SendTurbineChat @ 0x0057DB10 with the matching ChatTypeEnum and the player's PlayerModule::Hear*Chat gate.

Verb Aliases Handler @addr acdream status
a guild, gu DoTurbineChat_Allegiance @ 0x0057EBA0 /a routed as a plain Allegiance channel message; guild/gu MISSING
general cg DoTurbineChat_General @ 0x0057EC50 general OK; cg MISSING. acdream's /gen is an invention.
trade ct DoTurbineChat_Trade @ 0x0057ECE0 trade OK; ct MISSING. /tr is an invention.
lfg clfg DoTurbineChat_LFG @ 0x0057ED70 lfg OK; clfg MISSING. /lookingforgroup is an invention.
roleplay crp DoTurbineChat_Roleplay @ 0x0057EE00 roleplay OK; crp MISSING. /role and /rp are inventions (/rp collides with retail's reply alias).
society soc DoTurbineChat_Society @ 0x0057EF20 society OK; soc MISSING
olthoi o DoTurbineChat_Olthoi @ 0x0057EE90 olthoi OK; o MISSING

2.5 Allegiance management

Verb Aliases Handler @addr Args Behavior acdream status
allegiance all DoAllegiance @ 0x0057D5A0 <sub> [args] Subcommand dispatcher. Verbs read out of the handler: boot (DoAllegianceBoot @ 0x0057AEF0), info (DoAllegianceInfo @ 0x00576560), chat/ch (DoAllegianceChat @ 0x00575CB0), broadcast/br (DoAllegianceBroadcast @ 0x005761F0), ban (DoAllegianceBan @ 0x005762A0), officer (DoAllegianceOfficer @ 0x00576650), title (DoAllegianceOfficerTitle @ 0x00576A10), hometown/ho (DoAllegianceHometown @ 0x0056EF10), motd (DoMotd @ 0x00577150), name (DoAllegianceName @ 0x00576C80), lock (DoAllegianceLock @ 0x00576E70), house (DoAllegianceHouse @ 0x0056EF70). MISSING (and /allegiance currently mis-bound as a chat channel)
ab DoAllegianceBroadcast @ 0x005761F0 <text> Monarch broadcast to the whole allegiance. MISSING
alh ah DoAllegianceHometown @ 0x0056EF10 ACE: GameActionType.RecallAllegianceHometown. MISSING
motd DoMotd @ 0x00577150 [set <text>|clear] Displays, sets (monarch-only), or clears the allegiance MOTD. MISSING
speaker DoSpeaker @ 0x0057DAB0 Prints exactly "This command is no longer in use, please see @allegiance officer.\n". Pure local. MISSING (trivial)

2.5b Housing

Verb Aliases Handler @addr Args Behavior acdream status
house hou DoHouse @ 0x00580860 <sub> [args] Subcommand verbs read out of the handler: open, close, recall/re, mansion_recall/alleg_recall/ma, storage (DoHouseStorage @ 0x00579560), remove, boot (DoHouseBoot @ 0x00579970), boot_all, remove_all, guest (DoHouseGuests @ 0x005791A0), abandon, available, hooks, on, off. PARTIALRetailClientCommandCatalog.TryMatch (RetailClientCommandCatalog.cs:279-301) recognizes only recall, mansion_recall, alleg_recall. hou, re, ma and all 12 other subcommands are MISSING, and the catalog swallows them locally with an invalid-args message instead of letting them reach ACE.
hor hr DoHouseRecall @ 0x00570450 IMPLEMENTED IMPLEMENTED
hom hoa DoMansionRecall @ 0x005704B0 IMPLEMENTED IMPLEMENTED
hslist DoHouseAvailableList @ 0x00570510 <apartment|cottage|villa|mansion> Lists available houses. ACE: GameActionType.ListAvailableHouses. MISSING

2.6 Death / recall / PK

Verb Aliases Handler @addr acdream status
lifestone lif, ls DoLifestone @ 0x0056FC70 IMPLEMENTED
marketplace mar, mp DoMarketplace @ 0x0056FCE0 IMPLEMENTED
pkarena pka DoPKArena @ 0x005788D0 IMPLEMENTED
pklarena pla DoPKLArena @ 0x005789D0 IMPLEMENTED
pklite pkl DoPKLite @ 0x0057A490 PARTIALpkl alias MISSING (RetailClientCommandCatalog.cs:218 registers only pklite)
die DoDie @ 0x00580050 IMPLEMENTED
corpse cor DoCorpse @ 0x00578220 IMPLEMENTED
consent DoConsent @ 0x0057DDA0 IMPLEMENTED
permit DoPermit @ 0x005785A0 add <name> / remove <name> — grants/revokes corpse-loot permission. ACE: AddPlayerPermission / RemovePlayerPermission. MISSING

2.7 Status / display

Verb Aliases Handler @addr Args Behavior acdream status
age DoAge @ 0x0057C5A0 IMPLEMENTED
birth DoBirth @ 0x0056E5F0 IMPLEMENTED
day DoDay @ 0x005706F0 Toggles LScape::m_fAlwaysDaylight via LScape::SetDay, persists with PlayerModule::SetPersistentAtDay, echoes "Let there be light!" when enabling. Pure local. MISSING
endurance DoEndurance @ 0x0057C5F0 Prints a fixed paragraph beginning "The endurance attribute has a nu…". Pure local text. MISSING (trivial)
framerate DoFrameRate @ 0x005707D0 IMPLEMENTED
loc DoLoc @ 0x0057A250 IMPLEMENTED
version DoVersion @ 0x0057E1B0 Prints "Client version %s\n"; additionally prints "Using Turbine Chat.\n" when IsUsingTurbineChat(), and extra lines when PlayerIsPSR(). PARTIALClientCommandController.cs:136 prints the version line only.
render DoRenderOption @ 0x0057E120 [options] Forwards to SmartBox::HandleRenderOption, prints its two out-strings. Pure local dev/render toggle. MISSING

2.8 Interface layout, emotes, social, components

Verb Aliases Handler @addr acdream status
saveui / loadui / saveautoui / loadautoui / lockui 0x0056FFF0 / 0x00570150 / 0x005702B0 / 0x00570330 / 0x005703B0 IMPLEMENTED
emote e, em, me DoEmote @ 0x00578AD0 IMPLEMENTED
emotes DoEmoteList @ 0x0057BB30 IMPLEMENTED
afk DoAFK @ 0x0057B3F0 IMPLEMENTED
friends / friends_add / friends_remove 0x0057BC00 / 0x00578FB0 / 0x00579080 IMPLEMENTED
squelch / unsquelch 0x0057BF50 / 0x0057C070 IMPLEMENTED
fillcomps DoFillComponents @ 0x0056FD50 IMPLEMENTED

2.9 Prefixes (not verbs)

Prefix Site Behavior acdream status
/ OnChatCommand @ 0x00581431 Rewritten to @, then dispatched. IMPLEMENTEDRetailClientCommandCatalog.TryMatch accepts both (RetailClientCommandCatalog.cs:267).
@ OnChatCommand @ 0x00581444 Dispatched directly. IMPLEMENTED
: , ; OnChatCommand @ 0x0058144D First char replaced with a space, line prefixed with "@emote", then dispatched — :waves@emote waves. MISSING
trailing , on the verb DoCommand @ 0x0057E3CD Right-trimmed, so @f, hi@f hi. MISSING — acdream's verb token keeps the comma, so /f, hi does not match any verb and is shipped to ACE as @f,.

3. Prioritized missing-command list

Effort key: L = pure-local (no wire message); W-have = wire path already exists in acdream; W-new = needs a new game action.

Tier 1 — correctness bugs in what already ships (do these first)

  1. /g is bound to General; retail binds it to Fellowship (0x800). ChatInputParser.cs:57. One-line fix, but it silently sends fellowship chatter to a global channel today. L
  2. /rp is bound to Roleplay; retail binds it to reply. ChatInputParser.cs:78. Same class of bug (a private reply becomes a global broadcast). L
  3. /allegiance <text> is bound to the Allegiance channel; retail's allegiance/all is the allegiance management command. Channel verbs are a/ab/guild/gu. L
  4. /house <anything-but-recall> is swallowed locally. RetailClientCommandCatalog.cs:288-296 returns a match with HasValidArguments:false for every unrecognized subcommand, so @house open, @house guest add X, @house abandon never reach ACE. Until DoHouse is ported, unrecognized house subcommands must fall through to SendServerCommandCmd. L
  5. Verb-trailing-comma trim. @f, hi / @t Bob, hi — retail right-trims , off the verb in DoCommand. L
  6. /tell splits on the first comma, not the first space. ChatInputParser.cs:208. Multi-word names break today. L

Tier 2 — pure-local commands, no wire work

  1. : / ; emote prefixes (OnChatCommand case 0x0B/0x0C). L
  2. @day — daylight toggle + SetPersistentAtDay. L
  3. @endurance — fixed help paragraph. L
  4. @speaker — fixed deprecation line. L
  5. @title <text> — chat window title. L
  6. @log [file] — chat-to-file logging. L
  7. @version — add the "Using Turbine Chat." line. L
  8. @renderSmartBox::HandleRenderOption equivalent (acdream has no SmartBox; map to the existing quality/debug toggles or leave as a documented divergence). L
  9. @help <command> / @help <group> + the 7 group nodes (commands, allegiances, channels, chatting, death, status, text). The exact retail help strings for all 57 Help* functions are recoverable from the binary — see §4 for the extraction recipe. L
  10. Missing aliases on already-implemented commands (one dictionary edit each): pkl, hou, message_types, msgtypes, msg_types, rt, send, whisper, w, vassal, covassal, co-vassals, c, fellows, group, party, guild, gu, cg, ct, clfg, crp, soc, o, ab. Also delete the non-retail inventions gen, cv, lookingforgroup, tr, role, h. L

Tier 3 — wire messages acdream already has

  1. @chat on|off and @notell on|off — both are ModifyGlobalSquelch with message types 2 and 3. acdream already wires ModifyGlobalSquelch for /filter. W-have
  2. @join / @leave <room> — set PlayerModule::SetHear*Chat; these ride the existing character-options path. W-have

Tier 4 — new game actions (ACE-side handlers all exist)

  1. @permit add|remove <name> — ACE AddPlayerPermission / RemovePlayerPermission. W-new
  2. @hslist <type> — ACE ListAvailableHouses. W-new
  3. @index / @clist / @on / @off — ACE IndexChannels, ListChannels, AddChannel, RemoveChannel. W-new (four small parameterless/one-dword actions)
  4. @allegiance <sub> — the 12-subcommand dispatcher. ACE handlers exist for every one (AllegianceInfoRequest, AllegianceChatBoot, AddAllegianceBan, SetAllegianceOfficer, SetAllegianceOfficerTitle, RecallAllegianceHometown, SetAllegianceName, DoAllegianceLockAction, DoAllegianceHouseAction, …). Largest single item; deserves its own slice. W-new
  5. @house <sub> — 15 subcommands; ACE handlers exist (SetOpenHouseStatus, ChangeStoragePermission, BootSpecificHouseGuest, HouseBootAll, AbandonHouse, HouseQuery, ModifyAllegianceGuestPermission, …). W-new
  6. @motd [set|clear] — allegiance MOTD. W-new
  7. @ab — allegiance broadcast. W-new
  8. @alh / @ahRecallAllegianceHometown. W-new
  9. Turbine-chat verbs as Turbine sends (SendTurbineChat) rather than plain ChannelBroadcast. acdream already ships the 0xF7DE TurbineChat path, so this is a routing decision, not new wire work. W-have

Explicitly do NOT implement

  • @mr, @pr — registered with a null function pointer in the 2013 build. Retail ships them as help text only; the command itself falls through to the server. Implementing them client-side would be a divergence.
  • The 22 fallback-only channel tags (abuse, admin, audit, advocate*, sentinel, celhan, eldweb, radblo, ol, …) are GM/faction channels. They are reachable in retail only through DoChannelCommand's fallback; the cheapest faithful port is to add the GetChannelID table and let the existing unknown-verb path consult it before falling back to SendServerCommandCmd.
  • @loadfile — a client-side script player that re-enters OnChatCommand for every line of an arbitrary file. Faithful, but it is a scripting surface; acdream already has a designed plugin API for that, so porting @loadfile should be an explicit product decision, not a parity checkbox.

4. Counts

Measure Count
Verbs registered by InitializeCommands @ 0x00581970 116
Verbs added by StartupTurbineChatSystem @ 0x0057EFB0 15 (14 net-new; a is replaced)
Total registered verbs 130
…of which have a null func (help-only nodes) 9 (commands, allegiances, channels, chatting, death, status, text, mr, pr)
Distinct handler functions behind those verbs 68 (61 base + 7 Turbine)
Additional verbs reachable via the GetChannelID fallback (unregistered) 22
Total client-parsed verbs 152
Prefixes with special parsing 4 (/, @, :, ;)

acdream, against the 130 registered verbs. Every verb is in exactly one row, so the column sums to 130.

Status Verbs Where
IMPLEMENTED — typed ExecuteClientCommandCmd 45 (33 ClientCommandId values) RetailClientCommandCatalog.cs + ClientCommandController.cs
PARTIAL via typed catalog 1house (3 of 15 subcommands, and it swallows the rest) RetailClientCommandCatalog.cs:279
IMPLEMENTED-EQUIVALENT — chat alias → SendChatCmd 21 ChatInputParser.cs
PARTIAL via chat alias (argument shape wrong) 3tell (space-split, not comma-split), reply (by name, not guid), retell ChatInputParser.cs
PARTIAL via local presentation 2help, ? (flat blob; no per-command or group help) ChatCommandRouter.cs:96
DIVERGENT — bound, wrong target 3g→General (retail Fellowship), rp→Roleplay (retail reply), allegiance→channel (retail command) ChatInputParser.cs
MISSING 55
Total registered 130

Plus 22 unregistered GetChannelID fallback tags, all MISSING → 77 client-parsed verbs unimplemented out of 152.

Reading note: the §2 tables mark messagetypes, pklite and version PARTIAL because an alias or an output line is missing. The counts above are per-verb, so those three verbs sit in IMPLEMENTED while their missing aliases (message_types, msgtypes, msg_types, pkl) are counted as four separate MISSING verbs.

Everything not in the table above reaches ACE correctly as SendServerCommandCmd → Talk (ChatCommandRouter.cs:62), which matches retail's own final fallback (DoCommandEvent_Talk) except that retail consults the channel-tag table first.

acdream verbs with no retail counterpart (candidates for removal): /h, /gen, /cv, /lookingforgroup, /tr, /role.


5. Reproduction recipes

Verb table (authoritative, resolves BN's mislabeled pooled strings):

# scan InitializeCommands for `push imm32` operands that point at short
# ASCII strings in .rdata — one per registered verb, in registration order
python pushes.py 581970 585520     # 116 verbs
python pushes.py 57efb0 57f9e0     # 15 Turbine verbs

Handler pairing: sed -n '396604,399712p' acclient_2013_pseudo_c.txt and read the CmdHashData::CmdHashData(ptr, &name, <func>, nullptr) call (later blocks) or the *(esi + 8) = <func> / *(esi + 0x10) = <help> stores (first ~22 blocks — BN renders the same code two different ways).

Exact retail help text for all 57 Help* functions: scan each ClientCommunicationSystem::Help* symbol's byte extent for push imm32 into .rdata and read the C string. This recovered, verbatim, every usage line and group listing quoted in §2 (e.g. the @tell comma requirement, the @afk msg 192-character limit, the @hslist house types).

ChannelSystem::GetChannelID @ 0x005CF1F0: alternate push imm32 (tag) and mov eax, imm32 (channel id) down the function; the id immediately precedes the next tag group.

Cross-checks performed: ACE GameActionTalk.cs:21 (the @-passthrough contract) and the GameActionType enumeration (confirms server-side handlers exist for every Tier-4 item). holtburger's commands.rs was consulted and rejected as an oracle for aliases — it binds /g to Allegiance and /p to Fellowship, which matches neither retail nor acdream.