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>
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 @ 0x00581320 →
ClientCommunicationSystem::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. |
PARTIAL — ChatCommandRouter.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." |
PARTIAL — ChatInputParser.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/DIVERGENT — ReplyAliases = {"/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. | PARTIAL — RetellAliases = {"/retell"}; rt MISSING. |
chat |
— | DoChatToggle @ 0x0056FAD0 |
on|off |
on → Event_ModifyGlobalSquelch(remove, 2); off → Event_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 | IMPLEMENTED — RetailClientCommandCatalog.cs:115, ClientCommandController.cs:165. |
filter / unfilter |
— | DoFilter @ 0x0057C860 / DoUnFilter @ 0x0057C880 |
-<type> |
IMPLEMENTED | IMPLEMENTED — ClientCommandController.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 @ 0x0057B130 → DoChannelCommand)
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 |
0x400 — explicitly 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. |
PARTIAL — RetailClientCommandCatalog.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 |
PARTIAL — pkl 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(). |
PARTIAL — ClientCommandController.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. |
IMPLEMENTED — RetailClientCommandCatalog.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)
/gis 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/rpis bound to Roleplay; retail binds it toreply.ChatInputParser.cs:78. Same class of bug (a private reply becomes a global broadcast). L/allegiance <text>is bound to the Allegiance channel; retail'sallegiance/allis the allegiance management command. Channel verbs area/ab/guild/gu. L/house <anything-but-recall>is swallowed locally.RetailClientCommandCatalog.cs:288-296returns a match withHasValidArguments:falsefor every unrecognized subcommand, so@house open,@house guest add X,@house abandonnever reach ACE. UntilDoHouseis ported, unrecognizedhousesubcommands must fall through toSendServerCommandCmd. L- Verb-trailing-comma trim.
@f, hi/@t Bob, hi— retail right-trims,off the verb inDoCommand. L /tellsplits 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
:/;emote prefixes (OnChatCommandcase 0x0B/0x0C). L@day— daylight toggle +SetPersistentAtDay. L@endurance— fixed help paragraph. L@speaker— fixed deprecation line. L@title <text>— chat window title. L@log [file]— chat-to-file logging. L@version— add the "Using Turbine Chat." line. L@render—SmartBox::HandleRenderOptionequivalent (acdream has no SmartBox; map to the existing quality/debug toggles or leave as a documented divergence). L@help <command>/@help <group>+ the 7 group nodes (commands,allegiances,channels,chatting,death,status,text). The exact retail help strings for all 57Help*functions are recoverable from the binary — see §4 for the extraction recipe. L- 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 inventionsgen,cv,lookingforgroup,tr,role,h. L
Tier 3 — wire messages acdream already has
@chat on|offand@notell on|off— both areModifyGlobalSquelchwith message types 2 and 3. acdream already wiresModifyGlobalSquelchfor/filter. W-have@join/@leave <room>— setPlayerModule::SetHear*Chat; these ride the existing character-options path. W-have
Tier 4 — new game actions (ACE-side handlers all exist)
@permit add|remove <name>— ACEAddPlayerPermission/RemovePlayerPermission. W-new@hslist <type>— ACEListAvailableHouses. W-new@index/@clist/@on/@off— ACEIndexChannels,ListChannels,AddChannel,RemoveChannel. W-new (four small parameterless/one-dword actions)@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@house <sub>— 15 subcommands; ACE handlers exist (SetOpenHouseStatus,ChangeStoragePermission,BootSpecificHouseGuest,HouseBootAll,AbandonHouse,HouseQuery,ModifyAllegianceGuestPermission, …). W-new@motd [set|clear]— allegiance MOTD. W-new@ab— allegiance broadcast. W-new@alh/@ah—RecallAllegianceHometown. W-new- Turbine-chat verbs as Turbine sends (
SendTurbineChat) rather than plainChannelBroadcast. 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 throughDoChannelCommand's fallback; the cheapest faithful port is to add theGetChannelIDtable and let the existing unknown-verb path consult it before falling back toSendServerCommandCmd. @loadfile— a client-side script player that re-entersOnChatCommandfor every line of an arbitrary file. Faithful, but it is a scripting surface; acdream already has a designed plugin API for that, so porting@loadfileshould 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 | 1 — house (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) | 3 — tell (space-split, not comma-split), reply (by name, not guid), retell |
ChatInputParser.cs |
| PARTIAL via local presentation | 2 — help, ? (flat blob; no per-command or group help) |
ChatCommandRouter.cs:96 |
| DIVERGENT — bound, wrong target | 3 — g→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 (DoCommand → Event_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.