feat(quest): QT3 — the contract tracker becomes state, and the events get routed
Fourth sibling J-owner, built to the shape the other three established. It borrows nothing, because the retail client stores no quest state of its own — everything here is a projection of what the server pushed. Clearing at generation reset is safe for the same reason: a fresh session opens with a full 0x0314 replacement, so the reset cannot lose anything the next login will not immediately restate, while NOT clearing would show a previous character's quests. Three readings of the wire that would each lose contracts silently, one test apiece: a 0x0314 REPLACES rather than merges (merging resurrects contracts the server dropped); an empty 0x0314 clears rather than being ignored (it is how the server says "you have none", and ignoring it strands the last quest on screen); and a delete carries a full tracker struct, so it looks exactly like an add apart from one flag. Adding a teardown stage exposed a genuine trap: TeardownStageCount bounds the drain loop while GameRuntimeTeardownStage.Complete defines what the ledger demands, and nothing tied them together. Leave the constant behind and the new owner is never disposed at all, while the ledger goes on waiting for its flag — the runtime hangs in teardown rather than failing anywhere near the edit. The stage-ledger test now reads the constant by reflection and asserts it against the flag list, so the next owner fails at the edit instead. Campaign QT slice 3 of 6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ab3934e21d
commit
f629ce7f3d
9 changed files with 564 additions and 19 deletions
|
|
@ -92,7 +92,10 @@ public sealed record LiveSocialSessionBindings(
|
|||
// Batch C (Map/House toolbar panel, 2026-08-17): same trailing/optional
|
||||
// compatibility convention — a minimal owner (RuntimeHouseState's own
|
||||
// class doc), not a full sibling J-owner.
|
||||
RuntimeHouseState? House = null);
|
||||
RuntimeHouseState? House = null,
|
||||
// Campaign QT (2026-08-21): the fourth sibling J-owner, same
|
||||
// trailing/optional compatibility convention.
|
||||
RuntimeContractState? Contracts = null);
|
||||
|
||||
/// <summary>
|
||||
/// Owns every inbound subscription for one exact live session. Domain state
|
||||
|
|
@ -334,6 +337,14 @@ public sealed class LiveSessionEventRouter : ILiveSessionEventRouting
|
|||
: null,
|
||||
onHouseStatus: social.House is { } houseStatus
|
||||
? weenieError => houseStatus.ApplyHouseStatus(weenieError, inventory.PlayerGuid())
|
||||
: null,
|
||||
// Campaign QT (2026-08-21): same conditional delegate-hole
|
||||
// discipline as house above.
|
||||
onContractTable: social.Contracts is { } contractTable
|
||||
? contractTable.ApplyTable
|
||||
: null,
|
||||
onContractUpdate: social.Contracts is { } contractUpdate
|
||||
? contractUpdate.ApplyUpdate
|
||||
: null));
|
||||
ConstructionCheckpoint();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue