fix(runtime)+docs: CC3 re-review CLOSED — R1 second-create wire slot, risk-8 measured LATENT
The CC3 narrow re-review returned CLOSED (both lenses PASS, merge recommended) with residual R1: the post-create wire-slot assignment read the cached wire CharacterList count, which ACE never refreshes after a create — correct for the first create, off by one for a second create in the same session (reachable via create Ok -> server-rejected guid enter -> ReturnToSelection -> create again), the same wire-contract failure class F2 fixed. Root fix now rather than carried: a creates-since-CharacterList counter (the equivalent of retail's own CharacterSet growing via AddIdentity per create), reset on every fresh wire CharacterList apply and at generation reset, applied only to the cached-wire branch since the display-roster fallback already contains prior appends. Regression test drives the full create->Ok->rejected-enter->create-again flow and pins wire slots 0/1/2/3. Docs: CC3 ledger row flipped to REVIEW-CLOSED with real shas (re-review R2); CC7 risk item 8 downgraded to LATENT with measured installed-DAT data (user-prompted): every heritage's single cost override is Arcane Lore at NormalCost=0/PrimaryCost=2 vs global 4/6, so ACE's over-deduction (= NormalCost = 0) cannot fire with end-of-retail data — the earlier "may be rejected" claim was inferred from code without measuring. Runtime 1707/0 Release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
397ccd62cd
commit
3a6b7e3115
3 changed files with 108 additions and 4 deletions
|
|
@ -433,6 +433,16 @@ public sealed class LiveSessionController
|
|||
private bool _disposed;
|
||||
private ulong _generation;
|
||||
private RuntimeTeardownStage _lastTeardownStages;
|
||||
|
||||
/// <summary>
|
||||
/// CC3 re-review R1: creates accepted since the last wire
|
||||
/// CharacterList was applied. ACE appends each created character to
|
||||
/// its own session list but never resends the list, so the cached
|
||||
/// wire count under-counts by exactly this number — the equivalent
|
||||
/// of retail's own CharacterSet growing via AddIdentity per create.
|
||||
/// Guarded by <c>_gate</c> like every sibling field.
|
||||
/// </summary>
|
||||
private int _createsSinceCharacterList;
|
||||
private LiveSessionCharacterSelection? _activeSelection;
|
||||
private Action<WorldSession>? _autoSaveTickHook;
|
||||
private Action<WorldSession>? _preLogoffFlushHook;
|
||||
|
|
@ -745,6 +755,7 @@ public sealed class LiveSessionController
|
|||
RuntimeGenerationToken activeGeneration = new(generation);
|
||||
CharacterSelectionState.Reset(activeGeneration);
|
||||
CharacterCreationState.Reset(activeGeneration);
|
||||
_createsSinceCharacterList = 0;
|
||||
try
|
||||
{
|
||||
DrainRetiredScope();
|
||||
|
|
@ -818,6 +829,7 @@ public sealed class LiveSessionController
|
|||
CharacterList.Parsed? characters = _operations.GetCharacters(session);
|
||||
if (characters is not null)
|
||||
{
|
||||
_createsSinceCharacterList = 0;
|
||||
LiveSessionRosterReport roster = BuildRosterReport(characters);
|
||||
CharacterSelectionState.ApplyRoster(roster);
|
||||
host.ReportRoster(roster);
|
||||
|
|
@ -947,6 +959,7 @@ public sealed class LiveSessionController
|
|||
{
|
||||
if (!IsCurrent(scope, generation))
|
||||
return;
|
||||
_createsSinceCharacterList = 0;
|
||||
LiveSessionRosterReport report = BuildRosterReport(roster);
|
||||
CharacterSelectionState.ApplyRoster(report);
|
||||
scope.Host.ReportRoster(report);
|
||||
|
|
@ -1337,9 +1350,22 @@ public sealed class LiveSessionController
|
|||
// THIS character by design, but its COUNT is still exactly the
|
||||
// 0-based slot ACE assigned). Falls back to the display roster
|
||||
// count only if the cached wire list is unexpectedly unavailable.
|
||||
// CC3 re-review R1: the cached count is stale by the number of
|
||||
// creates since the last CharacterList (ACE never resends one
|
||||
// post-create), so a SECOND create in the same session must add
|
||||
// the creates the cache hasn't seen — retail's own CharacterSet
|
||||
// grows via AddIdentity per create, keeping GetSlot correct the
|
||||
// same way. The counter applies ONLY to the cached-wire branch:
|
||||
// the display-roster fallback already contains every prior
|
||||
// create (AppendCreatedCharacter added them), so adding the
|
||||
// counter there would double-count. Resets whenever a fresh wire
|
||||
// CharacterList is applied and at generation reset.
|
||||
int wireIndex =
|
||||
_operations.GetCharacters(scope.Session)?.Characters.Count
|
||||
?? before.RosterCount;
|
||||
is int cachedWireCount
|
||||
? cachedWireCount + _createsSinceCharacterList
|
||||
: before.RosterCount;
|
||||
_createsSinceCharacterList++;
|
||||
CharacterSelectionState.AppendCreatedCharacter(
|
||||
identity.Guid,
|
||||
identity.Name,
|
||||
|
|
@ -1662,6 +1688,7 @@ public sealed class LiveSessionController
|
|||
_activeSelection = null;
|
||||
CharacterSelectionState.Reset(new RuntimeGenerationToken(_generation));
|
||||
CharacterCreationState.Reset(new RuntimeGenerationToken(_generation));
|
||||
_createsSinceCharacterList = 0;
|
||||
if (_scope is { } scope)
|
||||
{
|
||||
_scope = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue