Fix setElementSyncer's persist argument - #5090
Open
HeresHavi wants to merge 4 commits into
Open
Conversation
HeresHavi
marked this pull request as draft
July 25, 2026 16:08
HeresHavi
marked this pull request as ready for review
July 25, 2026 18:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixed the
persistargument ofsetElementSyncer, which was applied to the wrong check and stored in the wrong place.The server picks one player to simulate each driverless vehicle and each ped, known as that element's syncer, and reassigns the job automatically as players come and go.
setElementSyncer(element, player, true)asks the server to keep a specific player in that role instead. Two separate things stopped that from working:Servers that do not pass the
persistargument are unaffected. This is server side only, so there is no protocol or client change.Motivation
The condition was grouped wrongly
CUnoccupiedVehicleSync::UpdateVehicledecides when to take a vehicle's syncer away. It should only do that when the syncer is not persistent, and then only if that player is too far away or in a different dimension. Missing parentheses left the dimension test outside the persistence check, because&&binds tighter than||:A dimension mismatch therefore took the syncer away on its own, whether or not
persistwas set. The indentation in the original code grouped the two tests together, but the code did not.Two further details made the result worse than simply losing the setting.
StopSyncclears the persistence flag while it runs, so the setting was gone afterwards as well. AndFindSynceronly considers players in the vehicle's own dimension, so there was usually nobody available to take over. The vehicle then stayed unsynced until a player entered that dimension.For example, a resource sets a player as the persistent syncer of an unoccupied vehicle, then moves that vehicle into another dimension for an interior or an event area. The setting was discarded on the next update, about half a second later.
CPedSync::UpdateSynceralready implements the same rule correctly, aspersistent || (sameDimension && closeEnough), so the intended grouping is not in question.The setting was stored in the wrong place
Because the flag lived on the sync manager, using it on one element changed how the server treated all of the others.
For a vehicle,
FindPlayerCloseToVehiclerejected every candidate player while the flag was set, so no other driverless vehicle could be given a syncer at all. Those vehicles stopped being simulated. For a ped,CPedSync::UpdateSyncerreturned early for every ped, so peds held on to syncers that were too far away or in another dimension.The setting also outlived the syncer it described.
StopSynccleared it, butCPlayer's destructor drops a syncer by callingCVehicle::SetSyncerdirectly and never goes throughStopSync. A player who was a persistent syncer and then disconnected therefore left the flag set with nobody holding it, and from that point no driverless vehicle could be given a syncer until an unrelatedStopSynchappened to clear it.The flag now lives on
CVehicleandCPednext tom_pSyncer, andSetSyncerclears it when the syncer is removed. Every route that drops a syncer goes throughSetSyncer, including the disconnect path, so the rule is enforced in one place instead of at each call site. With that invariant in place the persistence check insideFindPlayerCloseToVehiclecan no longer be reached, so it is removed.OverrideSynceralso now applies the persistence setting when the given player is already the element's syncer. It previously returned early and only ever cleared the setting, which madesetElementSyncer(element, player, true)a no-op whenever the engine had already picked that player.The four call sites that consulted the old manager-wide flag now read the flag of the element in front of them. Three of those are the enter and exit paths in
CGame, which are a straight substitution. The fourth is the trailer attach, which also passes the persistence on to the trailer, so a pinned towing vehicle does not keep its chosen syncer while the trailer is handed to somebody else on the next update. Worth knowing for review: trailer coupling has four independent server side paths and the attach handler inCGameis the only one that consults this flag, so that part only affects attaches arriving through the trailer packet rather than through the driver's own sync stream.Runtime evidence
Server log from the test resource, run against the same tree built from
masterand with these two commits. Only thedeathmatchmodule differed between runs.Before:
After:
Test plan
Runtime
Eighteen cases in a server side test resource, run against the same tree built from
masterand with these two commits. Eight fail onmasterand all eighteen pass here. The other ten pass in both runs and exist to catch regressions.master.setElementSyncer(v, p, true)now takes effect. Fails onmaster.setElementSyncer(v, p, false)on the current syncer clears it.master.master.master.Steps to re-check this later
Run as a server script with one player connected.
Expected in all three: the player's name. Before these changes each reports
none.Builds and Tests
Debug | Win32: Full build passed, 304 client tests passed.Release | Win32: Full build passed.Debug | x64: Full build passed.Release | x64: Full build passed.clang-format.Checklist