acdream/src/AcDream.Runtime
Erik 544f8cb2d7 fix(CT): CT2 fix round — dedupe client-side title add, drop retail-inexact send guard
Opus dual-lens review of CT2 (bcfddc97) found 4 SHOULD-FIX + notes; this
applies the campaign lead's rulings.

F1 (the important one): retail's client-side table add is DEDUPED —
gmCharacterTitleUI::RecvNotice_AddCharacterTitle @0x0049a990 walks
mTitleList and returns without effect when the id is already present,
only inserting on a miss. The server-side SendNotice_AddCharacterTitle
broadcast is unconditional, but RuntimeCharacterTitleState.ApplyUpdateTitle
models the CLIENT receive side, so TitleAdded now fires only on a genuine
new membership. Inverted the pin:
ApplyUpdateTitle_AlreadyEarnedId_DoesNotFireTitleAddedOrBumpRevision.

F3: removed the send-side titleId==0 rejection from both command
adapters. Retail's own send path (Event_SetDisplayCharacterTitle
@0x006a5720) packs whatever id it is handed, and ACE accepts id 0
(CharacterTitle.Invalid is a defined enum value) — retail's real
protection is the UI ghost-when-current gate (CT3's job), not a
send-side rejection. No register row: this makes acdream MORE
retail-exact.

A2: ResetSession now publishes TableReplaced unconditionally and
DisplayTitleChanged when the display id was non-zero before the clear,
matching the LocalPlayerState.Clear() precedent (publish every category
even when Clear is repeated, so a failed reset can converge on retry).

A3: RuntimeCharacterState.CaptureOwnership reads the new non-allocating
Titles.Count instead of EarnedTitleIds.Count; EarnedTitleIds now carries
an XML warning that every read allocates.

A4/A5: ReplaceTable/ApplyUpdateTitle now mutate under one _gate hold with
change flags computed inside the lock and events raised after release;
every revision bump is gated on an actual state change (a no-op wire
resend produces zero revision edges), matching the change-gated
RuntimeMovementSkillState precedent. TableReplaced itself still fires
unconditionally per retail's own Refresh() dispatch on 0x0029.

A1/A6/A7/A8: CharacterTitleResolverLiveDatTests honors ACDREAM_DAT_DIR
first (CT1 fix-round pattern); documented the EmitResult
primaryObjectId-as-title-id precedent inline; corrected the "third
consumer" comment (CT1 §5 already records gmAttributeUI::PostInit's
icon-DID lookup — CT5 factors the shared GetDIDByEnum helper); added a
titleId -> resolved-string memo to CharacterTitleResolver, the DAT-static
equivalent of retail's lazy-hash cache on the string buffer.

Appended a "CT3 anchors from the CT2 review" list to the plan doc's CT2
ledger entry for CT3 to consume.

Build green. Runtime (102), Core.Net (12), and App (27 + 3 InstalledDat
pins under ACDREAM_RUN_INSTALLED_DAT_TESTS=1) title-scoped tests pass.
Full hermetic solution suite (Lane exclusions per the release gate) is
green: 0 failures across all 15 test projects.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:27:07 +02:00
..
Chat feat(chat): CT-B4 — @log, and the research block that was a wrong question 2026-08-21 11:28:20 +02:00
Entities fix(plugins): close LA5 ownership races 2026-08-14 19:28:14 +02:00
Gameplay fix(CT): CT2 fix round — dedupe client-side title add, drop retail-inexact send guard 2026-08-24 22:27:07 +02:00
Physics fix #435 (part 1): delete 17 probes that outlived their closed investigations 2026-08-24 11:41:20 +02:00
Session fix(CT): CT2 fix round — dedupe client-side title add, drop retail-inexact send guard 2026-08-24 22:27:07 +02:00
World fix: retail text and golden-string tests must not follow the machine's locale 2026-08-19 19:56:09 +02:00
AcDream.Runtime.csproj feat(platform): Campaign LA LA0 — extract ApplicationPathSet to AcDream.Platform 2026-08-14 15:19:02 +02:00
GameRuntime.cs feat(journal): QJ1/QJ2 — the journal's pages and their file 2026-08-21 15:31:47 +02:00
GameRuntimeActionViews.cs refactor(runtime): own combat and magic intent 2026-07-26 11:56:40 +02:00
GameRuntimeClock.cs feat(runtime): define borrowed views commands and ordered events 2026-07-25 19:08:42 +02:00
GameRuntimeCommands.cs feat(CT): CT2 — Runtime character-title ownership + wire 2026-08-24 22:03:22 +02:00
GameRuntimeEventHub.cs feat(headless): complete deterministic bot command parity 2026-07-27 08:23:36 +02:00
GameRuntimeEvents.cs feat(runtime): FA2 -- RuntimeFellowshipState + RuntimeAllegianceState sibling J-owners 2026-08-12 01:31:54 +02:00
GameRuntimeGameplayViews.cs feat(CT): CT2 — Runtime character-title ownership + wire 2026-08-24 22:03:22 +02:00
GameRuntimeViews.cs feat(app): Campaign CC slice CC4 — chargen screen shell + Heritage/Profession/Skills/Town pages 2026-08-15 17:45:51 +02:00
packages.linux-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.neutral.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.win-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
RuntimeAssemblyMarker.cs arch(runtime): establish presentation-independent boundary 2026-07-25 17:33:11 +02:00
RuntimeGeneration.cs feat(runtime): define borrowed views commands and ordered events 2026-07-25 19:08:42 +02:00
RuntimeGenerationReset.cs feat(journal): QJ1/QJ2 — the journal's pages and their file 2026-08-21 15:31:47 +02:00
RuntimeSimulationOwnership.cs feat: secure trade with other players - wire, RuntimeTradeState, the 2026-08-14 11:49:13 +02:00