fix(chat): CH4 review fixes — allegiance ownership guard, house-abandon confirmation
Blocker 1: an unrecognized "@allegiance <sub>" subcommand escaped TryMatchAllegiance (which only claimed "info"/"hometown") and fell through the unregistered-tag channel fallback, broadcasting the raw subcommand text to the Allegiance chat channel (0x02000000). Retail's own DoAllegiance never reaches DoChannelCommand for an unrecognized subcommand — it claims the whole verb and prints its own client-local refusal. TryMatchAllegiance now claims "allegiance"/"all" unconditionally and shows retail's "Please see @help Allegiance..." text; ChatCommandRouter also gained a blanket RetailClientCommandCatalog.KnownVerbs ownership guard in TryDispatchChannelFallback as defense in depth. Blocker 2: "@house abandon" sent 0x021F immediately with no confirmation. Retail runs a real two-stage dialog before Event_AbandonHouse(); ported both verbatim strings and chained two ShowConfirmation calls. Should-fixes: a bare unregistered tag with no text now passes through silently instead of showing a refusal that belongs to a different retail function; @join/@leave update RuntimeCharacterOptionsState locally (new SetOptionBit) before the wire push so the Turbine membership gate stops refusing a just-joined room; @permit accepts multi-word names; @clist/ @on/@off validate shape only and raise WeenieError 0x422 for an unknown tag; @mr/@pr help text is now the verbatim retail strings; corrected issue #360, register row TS-68, the campaign doc's B.7 note, and a stale RetailChannelTagTable comment; filed issue #363 + register row AP-183 for the deferred error-typing debt. Nits: fixed TryMatchHouse's stale doc comment, the AP-182/@title "stores the value" comments (the binding is a no-op), IsUnregisteredFallbackTag's olthoi false-positive, added /g and /rp binding-level conformance pins, made @index ignore extra arguments, and noted the six removed invented verbs in ISSUES.md. Suite: 12,216 passed / 4 skipped / 0 failed (Release), up from CH4's 12,190/4/0 — net +26 tests, no removals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
090825e703
commit
724ef2d389
17 changed files with 853 additions and 85 deletions
|
|
@ -651,6 +651,58 @@ public sealed class RuntimeCharacterOptionsState
|
|||
Interlocked.Increment(ref _revision);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set ONE character-option bit locally, by its linear
|
||||
/// <c>CharacterOptionId</c> (the same id carried on the wire by
|
||||
/// <c>SetSingleCharacterOption (0x0005)</c>). Retail's
|
||||
/// <c>PlayerModule::SetHearGeneralChat @0x005D35C0</c> (and its five
|
||||
/// <c>SetHear*Chat</c> siblings) write the bit into this LOCAL copy
|
||||
/// FIRST, before the client ever notifies the server. CH4
|
||||
/// REJECT-review SHOULD-FIX 4 (2026-08-09): acdream's <c>@join</c>/
|
||||
/// <c>@leave</c> previously pushed only the wire message and left this
|
||||
/// state untouched, so <see cref="AcDream.Runtime.Gameplay.TurbineChatMembershipGate"/>
|
||||
/// kept refusing a room the player had just joined until the next
|
||||
/// <c>PlayerDescription</c> happened to arrive. Only the six
|
||||
/// <c>ListenTo*Chat</c> ids <c>CharacterOptionId</c> models are
|
||||
/// recognized here; any other id is a silent no-op — this state only
|
||||
/// tracks what the Turbine-chat membership gate needs, not a complete
|
||||
/// <c>PlayerModule</c> mirror.
|
||||
/// </summary>
|
||||
public void SetOptionBit(uint characterOptionId, bool value)
|
||||
{
|
||||
(bool isOptions1, uint mask) = characterOptionId switch
|
||||
{
|
||||
(uint)CharacterOptionId.ListenToAllegianceChat =>
|
||||
(true, (uint)PlayerDescriptionParser.CharacterOptions1.HearAllegianceChat),
|
||||
(uint)CharacterOptionId.ListenToGeneralChat =>
|
||||
(false, (uint)PlayerDescriptionParser.CharacterOptions2.HearGeneralChat),
|
||||
(uint)CharacterOptionId.ListenToTradeChat =>
|
||||
(false, (uint)PlayerDescriptionParser.CharacterOptions2.HearTradeChat),
|
||||
(uint)CharacterOptionId.ListenToLFGChat =>
|
||||
(false, (uint)PlayerDescriptionParser.CharacterOptions2.HearLFGChat),
|
||||
(uint)CharacterOptionId.ListenToRoleplayChat =>
|
||||
(false, (uint)PlayerDescriptionParser.CharacterOptions2.HearRoleplayChat),
|
||||
(uint)CharacterOptionId.ListenToSocietyChat =>
|
||||
(false, (uint)PlayerDescriptionParser.CharacterOptions2.HearSocietyChat),
|
||||
_ => (false, 0u),
|
||||
};
|
||||
if (mask == 0u)
|
||||
return;
|
||||
|
||||
if (isOptions1)
|
||||
{
|
||||
uint updated = value ? (Options1 | mask) : (Options1 & ~mask);
|
||||
Volatile.Write(ref _options1, updated);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint updated = value ? (Options2 | mask) : (Options2 & ~mask);
|
||||
Volatile.Write(ref _options2, updated);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _revision);
|
||||
}
|
||||
|
||||
public void ResetSession()
|
||||
{
|
||||
Volatile.Write(ref _options1, DefaultOptions1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue