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
|
|
@ -54,13 +54,31 @@ public class ChatCommandRouterTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownSlashVerb_ShowsSystemMessage_DoesNotPublish()
|
||||
public void UnknownSlashVerb_RoutesToServerInAtForm()
|
||||
{
|
||||
// Retail / ≡ @: unknown verbs are the server's to judge. The
|
||||
// parser rewrites /-form to @-form (ACE's GameActionTalk only
|
||||
// intercepts @ on the wire); ACE answers "Unknown command: x"
|
||||
// for verbs it doesn't know — no local guess.
|
||||
var (vm, log, bus) = Fixture();
|
||||
var outcome = ChatCommandRouter.Submit("/notacommand", vm, bus, ChatChannelKind.Say);
|
||||
Assert.Equal(SubmitOutcome.UnknownCommand, outcome);
|
||||
Assert.Null(bus.Last);
|
||||
Assert.Contains(log.Snapshot(), e => e.Text.Contains("Unknown command"));
|
||||
Assert.Equal(SubmitOutcome.Sent, outcome);
|
||||
Assert.NotNull(bus.Last);
|
||||
Assert.Equal("@notacommand", bus.Last!.Text);
|
||||
Assert.DoesNotContain(log.Snapshot(), e => e.Text.Contains("Unknown command"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ServerCommandWithArgs_PublishesAtFormAsSay_EvenOnChannelDefault()
|
||||
{
|
||||
// Commands resolve before channel routing (retail behavior):
|
||||
// even with Fellowship as the active input channel, a command
|
||||
// goes out as Say/Talk — the only wire action ACE parses @ on.
|
||||
var (vm, _, bus) = Fixture();
|
||||
var outcome = ChatCommandRouter.Submit("/ci 629 5", vm, bus, ChatChannelKind.Fellowship);
|
||||
Assert.Equal(SubmitOutcome.Sent, outcome);
|
||||
Assert.Equal(ChatChannelKind.Say, bus.Last!.Channel);
|
||||
Assert.Equal("@ci 629 5", bus.Last.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue