Skip to content

Fix playtest sync bugs: remote teleports, replayed quest rewards, dead dragons reappearing - #29

Open
absol89 wants to merge 4 commits into
devfrom
claude/project-thread-9f2vwo
Open

absol89 wants to merge 4 commits into
devfrom
claude/project-thread-9f2vwo

Conversation

@absol89

@absol89 absol89 commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Requested by Eric · project thread

Summary

Four fixes from the same playtest.

1. Don't teleport remote actors into a worldspace the player isn't in

When an owner gives up an actor, the server sends its position to every player (NotifyActorTeleport). MoveActor then calls LoadCell on the receiving client even when that player is somewhere else entirely. The playtest crashed this way: a player inside Embershard Mine received a burst of teleports for Riverwood actors, and 16 ms later a QueuedPromoteLargeReferencesTask crashed reading a null pointer at SkyrimSE+294230 (test byte ptr [rcx+0x40]) while promoting the Riverwood cell.

The same path leaves persistent actors in the wrong place in the receiving player's save. One log shows dead Mirmulnir (5A6CA) and the Bleak Falls dragon (263CE) being teleported into WhiterunWorld while that player was in Tamriel. That save now churns Mirmulnir's 3D nonstop on the worker threads, even offline.

The move is now skipped when the target worldspace isn't the player's. The actor is still placed by its spawn request once it becomes relevant.

2. Don't restart completed run once quests from party updates

A player who had finished Dragon Rising (MQ104, 2610C) received the Axe of Whiterun and the Fus prompt again after Remote quest started: 2610C, stage: 0. A remote Started went straight to ScriptSetStage, which restarts the quest. Starts and stage updates are now ignored for quests flagged RunOnce that are Completed locally. Radiant and repeatable quests aren't run once, so they're unaffected.

3. Don't spawn dead actors that are disabled locally

OnCharacterSpawn enabled a locally disabled actor and then called Kill() on it whenever the owner had it dead. That put the Bleak Falls dragon on the road (Disabled actor is being re-enabled: 263CE). Those actors are now skipped. Reconciliation (WaitingForDisable) behaves as before.

4. Don't block in the crash handler

This was a guess at the quit hang, and it turned out not to be the cause. It's still a safe change. The vectored exception handler now uses an atomic flag instead of a non-recursive mutex, and it flushes the logs instead of calling spdlog::shutdown().

Still open

  • The in-world quit hang. In the dump, the main thread is in the game's InitDeferredForms, waiting for promote tasks to drain, while a worker spins on a lock. The save with the churning Mirmulnir is the likely trigger. Fix 1 should stop new saves getting into that state, but saves that are already affected keep it.

Testing

Not run in game yet. All four commits are merged into API-playtest for verification.

🤖 Generated with Claude Code

https://claude.ai/code/session_018fvQxSuYsY5Nn19wykLCiD

The vectored exception handler held a non-recursive mutex for every exception, so a
second fault while it ran deadlocked the thread and every other thread that faulted
after it. It also shut down spdlog even when the exception was later handled, after
which any log call crashed. Use an atomic flag instead of the mutex and flush the
logs instead of shutting spdlog down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fvQxSuYsY5Nn19wykLCiD
@absol89
absol89 force-pushed the claude/project-thread-9f2vwo branch from d148b86 to c43915c Compare November 4, 2026 18:28
@absol89
absol89 changed the base branch from master to dev November 4, 2026 18:28
@absol89 absol89 self-assigned this Nov 4, 2026
When another player's save has an actor enabled and dead but ours has it disabled, the
spawn request enabled it and then killed it, which ran its OnDeath scripts again. With
quest dragons that replayed quest stages and rewards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fvQxSuYsY5Nn19wykLCiD
@absol89 absol89 changed the title Don't block in the crash handler Don't block in the crash handler; don't re-kill disabled dead actors Nov 4, 2026
A party member who hadn't finished a run once quest, like Dragon Rising, sent its start and
stages to players who already had. That restarted the quest for them and replayed its
rewards, such as the Axe of Whiterun and the Fus shout. Ignore starts and stages for run once
quests that are already completed locally, as the game itself would.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fvQxSuYsY5Nn19wykLCiD
@absol89 absol89 changed the title Don't block in the crash handler; don't re-kill disabled dead actors Fix replayed quest rewards and dead dragons reappearing; don't block in the crash handler Nov 4, 2026
When an owner gives up its actors, the server sends their positions to every player. A player
in an interior then loaded those exterior cells and moved the actors there, which crashed a
QueuedPromoteLargeReferencesTask with a null cell. It also left quest actors like dead dragons
in the wrong place in that player's save. Skip the move when the target worldspace isn't the
player's; the actor is placed when it spawns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fvQxSuYsY5Nn19wykLCiD
@absol89 absol89 changed the title Fix replayed quest rewards and dead dragons reappearing; don't block in the crash handler Fix playtest sync bugs: remote teleports, replayed quest rewards, dead dragons reappearing Nov 5, 2026
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.

2 participants