fix(chat): / and @ are equivalent command prefixes (retail parity)
Retail treats / and @ interchangeably for commands, but two client-side
layers broke the / spelling for server commands:
- ChatCommandRouter refused ANY unknown /verb with a local "Unknown
command" guess — so /tele, /ci, /acehelp never reached ACE even
though ACE supports them. The guard is gone; the server is now the
single authority on what's a valid command (ACE replies "Unknown
command: x" itself).
- ChatInputParser passed unknown /verbs through as literal speech.
ACE's GameActionTalk only intercepts the @ form on the wire (the /
acceptance in CommandManager is the server CONSOLE path), so the
parser now rewrites unknown /xyz -> @xyz.
Both command pass-throughs (@ and rewritten /) now also force the Say
channel: GameActionTalk (0x0015) is the only wire action ACE parses
commands on — previously a command typed with a chat channel active
would broadcast as channel speech.
Phase J Tier 4 ("/-text must never broadcast as speech") still holds:
letter-verbed input goes out as an @command (never speech), and
command-shaped-but-verbless input ("/", "//shrug") is refused locally
by a narrow router guard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
59ea3252cd
commit
d3cab1ab10
5 changed files with 141 additions and 51 deletions
|
|
@ -113,18 +113,43 @@ public sealed class ChatPanelInputTests
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/foo")]
|
||||
[InlineData("/ls")]
|
||||
[InlineData("/mp /tools/script.py")]
|
||||
[InlineData("/genio public")]
|
||||
[InlineData("/")]
|
||||
public void Submit_UnknownSlashCommand_ShowsUnknownAndDoesNotPublish(string raw)
|
||||
[InlineData("/foo", "@foo")]
|
||||
[InlineData("/ls", "@ls")]
|
||||
[InlineData("/mp /tools/script.py", "@mp /tools/script.py")]
|
||||
[InlineData("/genio public", "@genio public")]
|
||||
public void Submit_UnknownSlashCommand_RoutesToServerAsAtCommand(string raw, string expectedText)
|
||||
{
|
||||
// Phase J Tier 4: /-prefixed text is NEVER broadcast as plain
|
||||
// speech. Filed after a 2026-04-25 trace where typing /ls (a
|
||||
// command-style request the user wanted) was getting echoed by
|
||||
// the server as "You say, \"/ls\"". Now we intercept and show
|
||||
// a local "Unknown command" line; nothing goes on the wire.
|
||||
// Phase J Tier 4 held: /-prefixed text is still NEVER broadcast
|
||||
// as plain speech. Retail treats / and @ as equivalent command
|
||||
// prefixes, so unknown verbs now go to the SERVER as @commands
|
||||
// (ACE's GameActionTalk intercepts @ on the Say action and
|
||||
// answers "Unknown command: x" itself) instead of a local guess.
|
||||
var log = new ChatLog();
|
||||
var vm = new ChatVM(log);
|
||||
var panel = new ChatPanel(vm);
|
||||
var bus = new RecordingBus();
|
||||
var renderer = new FakePanelRenderer
|
||||
{
|
||||
InputTextSubmitNextSubmitted = raw,
|
||||
InputTextSubmitNextBufferAfter = "",
|
||||
};
|
||||
|
||||
panel.Render(new PanelContext(0.016f, bus), renderer);
|
||||
|
||||
var sendCmd = Assert.IsType<SendChatCmd>(Assert.Single(bus.Published));
|
||||
Assert.Equal(ChatChannelKind.Say, sendCmd.Channel);
|
||||
Assert.Equal(expectedText, sendCmd.Text);
|
||||
Assert.Empty(log.Snapshot()); // no local "Unknown command" guess
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/")]
|
||||
[InlineData("//shrug")]
|
||||
public void Submit_CommandShapedWithoutVerb_ShowsUnknownAndDoesNotPublish(string raw)
|
||||
{
|
||||
// Command-shaped but no letter verb: refused locally — this is
|
||||
// the remaining Tier-4 guard (never broadcast /-text as speech,
|
||||
// and don't put junk @-rewrites on the wire either).
|
||||
var log = new ChatLog();
|
||||
var vm = new ChatVM(log);
|
||||
var panel = new ChatPanel(vm);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue