feat(chat): route retail lifestone commands

Separate retail client actions, ACE server commands, and ordinary chat at the shared router. Port lifestone/lif/ls from the named retail registry through a typed App controller to game action 0x0063, keep unknown verbs on ACE Talk, and cover both UI backends plus exact outbound bytes.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 11:43:19 +02:00
parent 5090aa7217
commit 5a45a7ac7f
19 changed files with 604 additions and 78 deletions

View file

@ -0,0 +1,110 @@
# Retail client-command routing and lifestone recall
Date: 2026-07-13
## Scope
This note records the retail path used for chat-bar commands and the first
ported client command, `@lifestone` (`@lif`, `@ls`). It deliberately separates
commands the retail client executes from ACE administrator commands that the
server consumes as Talk text.
## Named-retail sources
- `ChatInterface::ProcessCommand @ 0x004F5100`
- `ClientCommunicationSystem::OnChatCommand @ 0x00581320`
- `ClientCommunicationSystem::DoLifestone @ 0x0056FC70`
- `ClientCommunicationSystem::HelpLifestone @ 0x00578500`
- `CM_Character::Event_TeleToLifestone @ 0x006A1B90`
- Command-table construction at `0x005838F7` through `0x00583A2B`
- Alias data at `0x007E17E4` (`ls`), `0x007E17E8` (`lif`), and
`0x007E17EC` (`lifestone`)
Source file:
`docs/research/named-retail/acclient_2013_pseudo_c.txt`.
## Cross-references
- ACE consumes server/admin commands such as `@ci` from the Talk game action.
That is a different path from retail client commands.
- acdream already has the exact lifestone action builder in
`AcDream.Core.Net.Messages.InteractRequests.BuildTeleToLifestone`.
- holtburger's chat parser is useful for chat aliases, but the named retail
command registry is authoritative for client-command ownership and aliases.
## Retail pseudocode
### Chat submission
```text
ChatInterface.ProcessCommand():
text = read chat input
communicationSystem.OnChatCommand(text, currentCommandSource)
append text to input history
```
```text
ClientCommunicationSystem.OnChatCommand(text, source):
allow plugin to inspect/replace the input
remember the current source and trimmed command line
if first character is '/':
replace it with '@'
return DoCommand()
if first character is '@':
return DoCommand()
if first character denotes an emote shortcut:
rewrite to '@emote ...'
return DoCommand()
otherwise:
route as ordinary speech according to the current talk focus
```
### Lifestone command registration
```text
register "lifestone" -> DoLifestone, HelpLifestone
register "lif" -> DoLifestone, HelpLifestone
register "ls" -> DoLifestone, HelpLifestone
```
The registry uses case-insensitive command keys.
### Lifestone command execution
```text
ClientCommunicationSystem.DoLifestone(argumentCount, arguments):
if argumentCount == 0:
CM_Character.Event_TeleToLifestone()
else:
add the command's usage/error text to the chat scroll
return handled
```
```text
CM_Character.Event_TeleToLifestone():
sequence = Proto_UI.GetNextUICounter()
packet = OrderHdr(gameActionEnvelope, sequence, payloadLength = 12)
packet.action = 0x0063
sent = Proto_UI.SendToWeenie(packet)
if not sent:
Proto_UI.UICounterFailedSend(sequence)
return sent
```
## Port contract
1. Both `/` and `@` prefixes are accepted for retail client commands.
2. `lifestone`, `lif`, and `ls` match case-insensitively.
3. The command accepts no arguments. Invalid arguments display usage locally
and send neither chat nor a game action.
4. A valid command publishes a typed client action. The App layer executes it
through `WorldSession`; UI code never touches the network session.
5. Unknown command verbs remain server commands: `/foo` is canonicalized to
`@foo` and sent through Talk so ACE remains authoritative for its command
set.
6. The wire body is the existing 12-byte game-action envelope with opcode
`0x0063`; no command text is sent for lifestone recall.