Skip to content

fix(internaldata): stamp ended_at when the territory-drift check abandons an encounter - #33

Merged
nxships merged 2 commits into
mainfrom
fix/encounter-drift-ended-at
Aug 3, 2026
Merged

fix(internaldata): stamp ended_at when the territory-drift check abandons an encounter#33
nxships merged 2 commits into
mainfrom
fix/encounter-drift-ended-at

Conversation

@nxships

@nxships nxships commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

UpsertSightingAsync's territory-drift branch nulled mCurrentEncounterId
without stamping ended_at, unlike OnTerritoryChanged and the world-drift
branch immediately below it. A missed or late TerritoryChanged therefore
left the row dangling at ended_at IS NULL until the next session's orphan
sweep - and when that reload happened to land in the same territory, pass
1's unbounded crash-recovery branch resumed it and welded two unrelated
visits into one encounter with a bogus duration.

Both drift branches now feed a shared driftStampId slot. They are mutually
exclusive by construction: the territory branch nulls mCurrentEncounterId,
so the world branch's mCurrentEncounterId is { } drifted pattern can no
longer match afterwards.

Also add a one-shot warning to the watcher's ProcessAsync IsStopping bail.
That path returning silently is why the lifetime-token regression fixed in
NexusKit (Dalamud's load-timeout token being treated as the plugin
lifetime) went unnoticed for weeks: observation persistence died 60s into
every session, Recent and ObservationProcessed froze, and the nearby-player
list silently stopped growing with nothing in the log. On a legitimate
unload the warning fires at most once during teardown.

Docs: correct the "plugin unload leaves the encounter open" claim - the
Stopping transition now closes it cleanly, so only a hard crash leaves
ended_at NULL - and document both drift checks plus the
COUNT(*) WHERE ended_at IS NULL invariant.

nxships added 2 commits August 2, 2026 00:06
…dons an encounter

UpsertSightingAsync's territory-drift branch nulled mCurrentEncounterId
without stamping ended_at, unlike OnTerritoryChanged and the world-drift
branch immediately below it. A missed or late TerritoryChanged therefore
left the row dangling at ended_at IS NULL until the next session's orphan
sweep - and when that reload happened to land in the same territory, pass
1's unbounded crash-recovery branch resumed it and welded two unrelated
visits into one encounter with a bogus duration.

Both drift branches now feed a shared driftStampId slot. They are mutually
exclusive by construction: the territory branch nulls mCurrentEncounterId,
so the world branch's `mCurrentEncounterId is { } drifted` pattern can no
longer match afterwards.

Also add a one-shot warning to the watcher's ProcessAsync IsStopping bail.
That path returning silently is why the lifetime-token regression fixed in
NexusKit (Dalamud's load-timeout token being treated as the plugin
lifetime) went unnoticed for weeks: observation persistence died 60s into
every session, Recent and ObservationProcessed froze, and the nearby-player
list silently stopped growing with nothing in the log. On a legitimate
unload the warning fires at most once during teardown.

Docs: correct the "plugin unload leaves the encounter open" claim - the
Stopping transition now closes it cleanly, so only a hard crash leaves
ended_at NULL - and document both drift checks plus the
COUNT(*) WHERE ended_at IS NULL invariant.
@nxships
nxships merged commit 42c01d5 into main Aug 3, 2026
3 checks passed
@nxships
nxships deleted the fix/encounter-drift-ended-at branch August 3, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant