acdream/docs/research/2026-07-13-retail-client-command-routing-pseudocode.md
Erik 5a45a7ac7f 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>
2026-07-13 11:43:19 +02:00

3.5 KiB

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

ChatInterface.ProcessCommand():
    text = read chat input
    communicationSystem.OnChatCommand(text, currentCommandSource)
    append text to input history
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

register "lifestone" -> DoLifestone, HelpLifestone
register "lif"       -> DoLifestone, HelpLifestone
register "ls"        -> DoLifestone, HelpLifestone

The registry uses case-insensitive command keys.

Lifestone command execution

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
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.