fix(chat): block / unknown commands from broadcasting as speech
User reported typing /ls (a command-style request, not chat) gets
echoed by the server as "You say, \"/ls\"". Slash-prefix is a
COMMAND surface, never a chat surface. Filed after the same flow
that produced @help and the welcome-message work.
Behavior change at the ChatPanel submit layer:
- Any /-prefixed input whose verb isn't in our alias tables now
renders a local "[System] Unknown command: /foo. Type /help for
the list." line and is NEVER published to the bus. No SendChatCmd,
no Talk packet. The server never sees /foo.
- Known /-verbs (/say /tell /reply /retell /general /allegiance
/patron /vassals /monarch /covassals /fellowship /lookingforgroup
/trade /roleplay /society /olthoi /help /clear /framerate /loc
and friends) still flow through ChatInputParser.Parse → SendChatCmd
exactly as before.
- @-prefix unchanged: ACE's CommandManager handles unknown @ verbs
server-side and replies via SystemChat ("Unknown command: foo")
per ACE GameActionTalk.cs:21. Our @ -> / normalization for known
verbs (Phase J Tier 1) and the @-passthrough fallthrough for
unknown verbs both still apply.
ChatInputParser now exposes:
- IsKnownVerb(string verb): query against the union of every alias
table. Used by ChatPanel to discriminate "unknown verb" from
"known verb with bad args".
- GetVerbToken(string command): public alias of the existing
ExtractVerb so callers can pull the first whitespace token without
reproducing the helper.
Parse itself is unchanged — its existing fall-through (Say with
literal text) still applies for unknown /-verbs called directly via
the parser, but ChatPanel intercepts before reaching that path so
the fall-through never fires through the live submit pipeline. Tests
that directly call Parse continue to pass; the new ChatPanel-level
tests pin the unknown-command rejection.
19 new tests:
- ChatInputParserTests: 10 IsKnownVerb Theory cases + 4 GetVerbToken
Theory cases.
- ChatPanelInputTests: 5 Theory cases for Submit_UnknownSlashCommand
covering /foo, /ls, /mp <path>, /genio, and bare /.
Solution total: 1086 green (243 Core.Net + 183 UI + 660 Core),
0 warnings.
Acceptance: type /ls, /mp /path, /anything-not-known — see local
"[System] Unknown command: /xxx. Type /help for the list of
supported commands." Nothing reaches the wire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a44488e277
commit
579cbfb48b
4 changed files with 101 additions and 2 deletions
|
|
@ -228,8 +228,11 @@ public sealed class ChatInputParserTests
|
|||
// Phase J added long-form aliases (/general, /allegiance,
|
||||
// /patron, etc.). The exact-token rule still applies — a
|
||||
// verb prefix that ISN'T one of the listed aliases falls
|
||||
// through to the default channel. "/genio" is not /g, /general,
|
||||
// or /gen — must stay as Say carrying the literal text.
|
||||
// through. The Parse-level behaviour for unknown /-verbs is
|
||||
// still "Say with the literal text" (matches holtburger);
|
||||
// the ChatPanel layer is what catches unknowns and shows the
|
||||
// local "Unknown command" line. ChatPanelInputTests cover
|
||||
// that end-to-end behaviour.
|
||||
var parsed = ChatInputParser.Parse("/genio public", ChatChannelKind.Say, lastTellSender: null);
|
||||
|
||||
Assert.NotNull(parsed);
|
||||
|
|
@ -237,6 +240,32 @@ public sealed class ChatInputParserTests
|
|||
Assert.Equal("/genio public", parsed.Value.Text);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/g", true)]
|
||||
[InlineData("/say", true)]
|
||||
[InlineData("/tell", true)]
|
||||
[InlineData("/retell", true)]
|
||||
[InlineData("/allegiance", true)]
|
||||
[InlineData("/lookingforgroup", true)]
|
||||
[InlineData("/genio", false)]
|
||||
[InlineData("/ls", false)]
|
||||
[InlineData("/foo", false)]
|
||||
[InlineData("/", false)]
|
||||
public void IsKnownVerb_ChecksAgainstAliasTables(string verb, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, ChatInputParser.IsKnownVerb(verb));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/g hello", "/g")]
|
||||
[InlineData("/tell Bob hi", "/tell")]
|
||||
[InlineData("/foo", "/foo")]
|
||||
[InlineData("/", "/")]
|
||||
public void GetVerbToken_PullsFirstWhitespaceToken(string command, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ChatInputParser.GetVerbToken(command));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/general what's the deal", ChatChannelKind.General, "what's the deal")]
|
||||
[InlineData("/allegiance recall", ChatChannelKind.Allegiance, "recall")]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue