fix(runtime,net): OP1 re-review residuals R1/R2/R3 (coordinator pass)
R1: the K4-load-bearing IsDirty pre-check moves into GameRuntime's two once-allocated hook lambdas — SendBlob's closure environment is allocated in FlushCharacterOptions's PROLOGUE, ahead of any guard inside the body, so the clean-tick fast path must never enter the method at all. The body keeps its check as idempotent defense only; the doc comment now describes the real mechanism instead of overclaiming. R2: RuntimeCharacterOptionsState gains a dirty-generation token. MarkDirty bumps it on EVERY call (including while already dirty); TryFlush / TryFlushIfAutoSaveDue capture it before invoking the callback and only clear IsDirty when it is unchanged after — a dirtying change landing DURING a flush (cross-thread, or re-entrant from the callback itself, the re-review's NOTE-6 case) now stays dirty and flushes on its own later trigger instead of being silently erased by the trailing clear. The S2 interleaving test now asserts the retained dirty state it previously ignored; a deterministic re-entrancy test pins the same-thread shape. R3: a trailer-truncated PlayerDescription parse carries zero placeholder option words, not server truth — GameEventWiring now forwards TrailerTruncated, LiveSessionEventRouter passes armServerSeed: !trailerTruncated, and Replace withholds the 0x01A1 flush authorization for truncated seeds while still installing the words (pre-existing local behavior unchanged). Newly wire-reaching via the R1/MF-1 timer, hence closed now rather than left a NOTE. Full Release suite: 12,870 passed / 4 skipped / 0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b236a44279
commit
6f48e34152
6 changed files with 130 additions and 25 deletions
|
|
@ -336,10 +336,25 @@ public sealed class GameRuntime
|
|||
// WorldSession directly rather than through App's
|
||||
// LiveSessionCommandRouter/LiveCommandBus, which is what keeps
|
||||
// it off the S2 lock-order hazard the blast-lens review named.
|
||||
// The IsDirty pre-check lives HERE, in the once-allocated hook
|
||||
// lambdas, not (only) inside FlushCharacterOptions: SendBlob's
|
||||
// closure environment is allocated in that method's PROLOGUE,
|
||||
// ahead of any guard inside it (OP1 re-review R1), so the clean-
|
||||
// tick fast path must never ENTER the method at all. Retail-
|
||||
// faithful too — CPlayerModule::UseTime @0x0059A710 opens with
|
||||
// the identical m_bDirty byte compare.
|
||||
context.Session.ConfigureAutoSaveTick(
|
||||
session => FlushCharacterOptions(session, ifAutoSaveDue: true));
|
||||
session =>
|
||||
{
|
||||
if (CharacterOwner.Options.IsDirty)
|
||||
FlushCharacterOptions(session, ifAutoSaveDue: true);
|
||||
});
|
||||
context.Session.ConfigurePreLogoffFlush(
|
||||
session => FlushCharacterOptions(session, ifAutoSaveDue: false));
|
||||
session =>
|
||||
{
|
||||
if (CharacterOwner.Options.IsDirty)
|
||||
FlushCharacterOptions(session, ifAutoSaveDue: false);
|
||||
});
|
||||
|
||||
construction.Complete();
|
||||
}
|
||||
|
|
@ -364,17 +379,13 @@ public sealed class GameRuntime
|
|||
/// both are always populated long before the session can ever tick or
|
||||
/// stop.
|
||||
/// <para>
|
||||
/// The <see cref="RuntimeCharacterOptionsState.IsDirty"/> pre-check
|
||||
/// below is retail-faithful (<c>CPlayerModule::UseTime @0x0059A710</c>
|
||||
/// opens with the identical <c>m_bDirty</c> byte compare before it ever
|
||||
/// touches the FPU timer math) AND load-bearing for allocation: without
|
||||
/// it, the auto-save-tick hook would allocate a fresh flush closure on
|
||||
/// EVERY <see cref="LiveSessionController.Tick"/> call — every frame,
|
||||
/// for every live session, whether or not anything is actually
|
||||
/// dirty — which is exactly the per-tick allocation the K4 headless
|
||||
/// resource-envelope gate measures. Gating on the cheap flag first means
|
||||
/// the closure below is only ever allocated on the rare tick where a
|
||||
/// flush might really happen.
|
||||
/// The allocation-load-bearing <see cref="RuntimeCharacterOptionsState.
|
||||
/// IsDirty"/> pre-check lives in the two HOOK LAMBDAS in the constructor,
|
||||
/// not here: <c>SendBlob</c>'s closure environment is allocated in this
|
||||
/// method's prologue — ahead of any guard written inside the body (OP1
|
||||
/// re-review R1) — so keeping clean ticks out of this method entirely is
|
||||
/// what protects the K4 headless per-tick allocation envelope. The check
|
||||
/// below remains only as cheap idempotent defense for direct callers.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private void FlushCharacterOptions(WorldSession session, bool ifAutoSaveDue)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue