Skip to content

Conversation

@rfortier
Copy link
Contributor

@rfortier rfortier commented Jan 7, 2026

  • Definitely is not compatible with 1.8.0 servers.
  • Posted as draft for comment while we get some mileage on it.

The community loves Members being able to advance quests (instead of only Party Leader), but it introduced a bunch of problems with duplicate quest triggers. This rework deals with it (hopefully).

A large credit goes to co-author @miredirex for reverse engineering a couple more methods to make this work, especially having more awareness of when a Player is in a scene (BGSScene) or not. That's the key to making cutscenes sync, or at least sync much, much better. That, and deduplicating the storm of duplicate attempts to advance the quest stage that was introduced with Member-can-progress-quests PR #769. That introduced several bugs that are fixed here.

A lot of the effort in this PR was figuring out the real-world situations in which quests can and cannot rewind, and making sure the fix supported that. You can find the information in ck.uesp.net starting with SetStage(), but it isn't all consolidated in one place. Find comments in the code, it's too long and complicated to re-explain here (likely introducing errors)

The most fundamental problem is that ScopedQuestOverride() doesn't work, because quest progress is queued and executed later on the current or different thread; the ScopedQuestOverride() is destructed before the quest OnEvent...() routines are invoked. So all the Members send their updates to the server even when told not to. The server just ignored them all before, but now with Member quest progression enabled, it ends up causing replicated quest updates. Some quests ignore it, many break.

The engine is in control of the content of events passed to the OnEvent routines. I could not find a reliable way to pass any context to the OnEvent routines through that barrier (it runs async, so matching up was nigh-on impossible). So ended up implementing a dedup history server-side, where there is enough context to figure this out.

The worst-case flow (when a Member triggers progress), is:

  1. Member sends questStage update.
  2. Update is forwarded to Leader, only.
  3. QuestService::OnQuestUpdate() receives the remote update and sets the new quest stage. It may make decisions to not execute it if in-scene or if closing another dedup hole introduced by network lag.
    1. Leader OnEvent hooks send the Leader quest update to the server.
    1. Leader SendsToParty
    1. Party members reflect the updates to the Server, but are dropped by the dedup history because the Leader has already sent it.
    1. Entries in the dedup history time out in seconds (needs some tuning), so quests can rewind and stages can be triggered multiple times to evaluate.

Merged with old duplicate quest stage "fix"

@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch 2 times, most recently from 9cf7866 to bbde0e3 Compare January 8, 2026 00:41
@rfortier rfortier changed the title Rework of party member quest progresion PR #769 Rework of party member quest progression PR #769 Jan 8, 2026
@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch from 8616912 to 3298ed7 Compare January 9, 2026 19:02
Definitely is not compatible with 1.8.0 servers.

The community loves Members being able to advance quests (instead of only Party Leader), but it introduced a bunch of problems with duplicate quest triggers. This rework deals with it (hopefully).

The most fundamental problem is that ScopedQuestOverride() doesn't work, because quest progress is executed on a different thread; the ScopedQuestOverride() is destructed before the quest OnEvent...() routines are invoked. So all the Members send their updates to the server even when told not to. The server just ignored them all before, but now with Member quest progression enabled, it ends up causing replicated quest updates. Some quests ignore it, many break.

The engine is in control of the content of events passed to the OnEvent routines. I could not find a reliable way to pass any context to the OnEvent routines through that barrier (it runs async, so matching up was nigh-on impossible). So ended up implementing a dedup history server-side, where there is enough context to figure this out.

The worst-case flow (when a Member triggers progress), is:
1) Member sends update.
2) Update is forwarded to Leader, only.
3) Leader OnEvent hooks send the Leader update to the server.
4) Leader SendsToParty
5) Party members reflect the updates to the Server, but are dropped by the dedup history because the Leader has already sent it.
6) Entries in the dedup history time out in seconds (needs some tuning), so quests can rewind and stages can be triggered multiple times to evaluate.

Merged with old duplicate quest stage "fix"
…experimented with live.

Fix server crash if connection fails server authentication
Make the QuestStageDedupHistory timeout a server variable, because it looks like we're going to need to tweak it.
@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch from 3298ed7 to 9ad8969 Compare January 12, 2026 21:31
…g them. Due to network delays, blocking the send isn't enough, Leader can enter the scene after a follower.

Goal is Leader not to accept updates during a scene; follower could skip it ahead.
…checking instead of playercontrols check.

Needs SceneService moved into its own files, but that has to wait pending merge with Miredirex RE branch or it will just be harder.

Improved logging.

Many iterations of rewind management to settle on this: can't be the way it should be because Member scenes play much faster than leader's.  Full description from the code:

    // Party all playing a scene firing events in parallel can deliver updates that get through the
    // server dedup logic due to the network delay. So when playing a scene, reject rewinds
    // You'd think that would be it, but due to Member playing scenes 2-4x faster than Leader
    // (unfixed bug), we need the leader to rewind Member to try to stay more in sync.
@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch from e4475b6 to 25fa8c9 Compare January 16, 2026 02:28
Claim is that if NPC casted slow time effects the magnitude was doubled. The bug was is set the TARGET to the Player, instead of the CASTER.
…ne are ignored. Unless they include a SceneEnd flag. Those updates are sent when a scene ends and forces everyone to accept the update to catch up.
@rfortier
Copy link
Contributor Author

Substantial logic change. Remote updates received while playing a scene are ignored. Unless they include a SceneEnd flag. Those updates are sent when a scene ends and forces everyone to accept the update to catch up.

Many variations of scene support have been tried, this seems to be the best compromise.
When in a Scene, ignore updates from other players. Each player runs the scene and generates their own progression.
This mostly works for scenes that are "movies," but if the scene requires player interaction, one player makes the choice (usually, the one who started the scene, but if the party stands together they might all get the dialog prompts, for example). The scene progresses for that player, everyone else is "stuck" there.

Unless multiple players click the option to continue. That's often going to do something unpredictable, don't do that.

To unstick players, at Scene End, send a "poke" quest stage update that is not deduped, to catch everyone up.

A future improvement may send such a poke every time player interaction causes a quest stage change, but have to figure out how to detect that.

@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch from 639e016 to cfdd227 Compare January 21, 2026 17:41
@rfortier rfortier force-pushed the fix/Rework-PR-769-party-member-quest-progression branch from 5e2f603 to 3323bb6 Compare January 22, 2026 21:15
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