From 39968790c98e7ff79c8c9ef9ad66e1c227a6497c Mon Sep 17 00:00:00 2001 From: Nexius Date: Sun, 10 Aug 2025 14:25:00 +0300 Subject: [PATCH 1/8] prevent partial updates from invalid driver sync by fixing the code order in PlayerVehicleSyncHandler, moving validation checks first --- Server/Source/player_pool.hpp | 76 +++++++++++++++++------------------ 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index f148a3c9a..1155535c8 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1135,13 +1135,19 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public ScopedPoolReleaseLock lock(*self.vehiclesComponent, *vehiclePtr); IVehicle& vehicle = *lock.entry; - Player& player = static_cast(peer); + + const bool vehicleOk = vehicle.updateFromDriverSync(vehicleSync, player); + + if (!vehicleOk) + { + return false; + } + player.pos_ = vehicleSync.Position; player.health_ = vehicleSync.PlayerHealthArmour.x; player.armour_ = vehicleSync.PlayerHealthArmour.y; player.armedWeapon_ = player.areWeaponsAllowed() ? vehicleSync.WeaponID : 0; - const bool vehicleOk = vehicle.updateFromDriverSync(vehicleSync, player); uint32_t newKeys = vehicleSync.Keys; switch (vehicleSync.AdditionalKey) @@ -1171,33 +1177,29 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public } player.setState(PlayerState_Driver); - if (vehicleOk) + vehicleSync.HasTrailer = false; + if (vehicleSync.TrailerID) { - vehicleSync.PlayerID = player.poolID; - - vehicleSync.HasTrailer = false; - if (vehicleSync.TrailerID) + IVehicle* trailer = vehicle.getTrailer(); + if (trailer) { - IVehicle* trailer = vehicle.getTrailer(); - if (trailer) - { - vehicleSync.HasTrailer = true; - vehicleSync.TrailerID = trailer->getID(); - } + vehicleSync.HasTrailer = true; + vehicleSync.TrailerID = trailer->getID(); } + } - TimePoint now = Time::now(); - bool allowedupdate = self.playerUpdateDispatcher.stopAtFalse( - [&peer, now](PlayerUpdateEventHandler* handler) - { - return handler->onPlayerUpdate(peer, now); - }); - - if (allowedupdate) + TimePoint now = Time::now(); + bool allowedupdate = self.playerUpdateDispatcher.stopAtFalse( + [&peer, now](PlayerUpdateEventHandler* handler) { - player.vehicleSync_ = vehicleSync; - player.primarySyncUpdateType_ = PrimarySyncUpdateType::Driver; - } + return handler->onPlayerUpdate(peer, now); + }); + + if (allowedupdate) + { + vehicleSync.PlayerID = player.poolID; + player.vehicleSync_ = vehicleSync; + player.primarySyncUpdateType_ = PrimarySyncUpdateType::Driver; } return true; } @@ -1435,23 +1437,19 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public } player.setState(PlayerState_Passenger); - if (vehicleOk) - { - TimePoint now = Time::now(); - bool allowedupdate = self.playerUpdateDispatcher.stopAtFalse( - [&peer, now](PlayerUpdateEventHandler* handler) - { - return handler->onPlayerUpdate(peer, now); - }); - - if (allowedupdate) + TimePoint now = Time::now(); + bool allowedupdate = self.playerUpdateDispatcher.stopAtFalse( + [&peer, now](PlayerUpdateEventHandler* handler) { - passengerSync.PlayerID = player.poolID; - player.passengerSync_ = passengerSync; - player.primarySyncUpdateType_ = PrimarySyncUpdateType::Passenger; - } - } + return handler->onPlayerUpdate(peer, now); + }); + if (allowedupdate) + { + passengerSync.PlayerID = player.poolID; + player.passengerSync_ = passengerSync; + player.primarySyncUpdateType_ = PrimarySyncUpdateType::Passenger; + } return true; } } playerPassengerSyncHandler; From f69d18d6fb1893e4e42fb0eb323e2f31766f8e91 Mon Sep 17 00:00:00 2001 From: Nexius Date: Sun, 10 Aug 2025 15:00:13 +0300 Subject: [PATCH 2/8] prevent partial updates from invalid trailer by fixing the code order in updateFromDriverSync, moving pure validations first --- Server/Components/Vehicles/vehicle.cpp | 30 ++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Server/Components/Vehicles/vehicle.cpp b/Server/Components/Vehicles/vehicle.cpp index 8ae7e60e4..c1a48f54b 100644 --- a/Server/Components/Vehicles/vehicle.cpp +++ b/Server/Components/Vehicles/vehicle.cpp @@ -164,6 +164,13 @@ bool Vehicle::updateFromDriverSync(const VehicleDriverSyncPacket& vehicleSync, I return false; } + if (vehicleSync.TrailerID && trailer && trailer->getID() != vehicleSync.TrailerID) + { + // The client instantly jumped from one trailer to another one. Probably a cheat, so don't + // allow it. + return false; + } + pos = vehicleSync.Position; rot = vehicleSync.Rotation; velocity = vehicleSync.Velocity; @@ -218,16 +225,7 @@ bool Vehicle::updateFromDriverSync(const VehicleDriverSyncPacket& vehicleSync, I if (vehicleSync.TrailerID) { - if (trailer) - { - if (trailer->getID() != vehicleSync.TrailerID) - { - // The client instantly jumped from one trailer to another one. Probably a cheat, so don't - // allow it. - return false; - } - } - else + if (!trailer) { // Got a new one that we didn't know about. trailer = static_cast(pool->get(vehicleSync.TrailerID)); @@ -322,6 +320,12 @@ bool Vehicle::updateFromTrailerSync(const VehicleTrailerSyncPacket& trailerSync, return false; } + PlayerVehicleData* playerData = queryExtension(player); + if (!playerData) + { + return false; + } + pos = trailerSync.Position; velocity = trailerSync.Velocity; angularVelocity = trailerSync.TurnVelocity; @@ -329,12 +333,6 @@ bool Vehicle::updateFromTrailerSync(const VehicleTrailerSyncPacket& trailerSync, updateOccupied(); - PlayerVehicleData* playerData = queryExtension(player); - if (!playerData) - { - return false; - } - Vehicle* vehicle = static_cast(playerData->getVehicle()); if (!vehicle || vehicle->detaching) From 83af9751c1ca38cf3b4b1ce355159ef523e41592 Mon Sep 17 00:00:00 2001 From: Nexius Date: Sun, 10 Aug 2025 15:44:28 +0300 Subject: [PATCH 3/8] grab passenger pos from vehicle pos eliminating areas for abuse, like in SA-MP server --- Server/Source/player_pool.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index 1155535c8..8f7c9bd06 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1404,10 +1404,10 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public return false; } + player.pos_ = vehicle.getPosition(); player.health_ = passengerSync.HealthArmour.x; player.armour_ = passengerSync.HealthArmour.y; player.armedWeapon_ = player.areWeaponsAllowed() ? passengerSync.WeaponID : 0; - player.pos_ = passengerSync.Position; uint32_t newKeys = passengerSync.Keys; switch (passengerSync.AdditionalKey) From caf2700cf157557b10e654d3ba2437e07f0b2d84 Mon Sep 17 00:00:00 2001 From: Nexius Date: Sun, 10 Aug 2025 16:35:54 +0300 Subject: [PATCH 4/8] don't update unstreamed cars for passengers but also add a check for train carriages, since they always consider unstreamed --- Server/Source/player_pool.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index 8f7c9bd06..65fa91aad 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1392,7 +1392,7 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public IVehicle& vehicle = *lock.entry; Player& player = static_cast(peer); - if (vehicle.isRespawning()) + if (vehicle.isRespawning() || (!vehicle.isStreamedInForPlayer(player) && !vehicle.isTrainCarriage())) { return false; } From 55c605da2d8d96a7472a892ec67199e708f02871 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 19 Aug 2025 21:51:29 +0330 Subject: [PATCH 5/8] fix isTrainCarriage usage --- Server/Source/player_pool.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index 65fa91aad..b873204db 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1392,7 +1392,8 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public IVehicle& vehicle = *lock.entry; Player& player = static_cast(peer); - if (vehicle.isRespawning() || (!vehicle.isStreamedInForPlayer(player) && !vehicle.isTrainCarriage())) + int vehicleModel = vehicle.getModel(); + if (vehicle.isRespawning() || (!vehicle.isStreamedInForPlayer(player) && !(vehicleModel == 569 || vehicleModel == 570))) // Check if vehicle is a train carriage (TODO: Move Vehicle::isTrainCarriage to SDK/Components/Vehicles/Impl/vehicle_models.hpp) { return false; } From 855abf4ddf9a4d2b99c61e7a08199da6f88cb458 Mon Sep 17 00:00:00 2001 From: Nexius Date: Sat, 13 Sep 2025 16:24:02 +0300 Subject: [PATCH 6/8] More checks for unoccupied sync --- Server/Source/player_pool.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index 4742cf764..f53465cf7 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1506,6 +1506,10 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public { return false; } + else if (!unoccupiedSync.SeatID && player.state_ == PlayerState_Passenger) + { + return false; + } else if (unoccupiedSync.SeatID && (player.state_ != PlayerState_Passenger || (playerVehicleData && playerVehicleData->getVehicle() != &vehicle) || (playerVehicleData && unoccupiedSync.SeatID != playerVehicleData->getSeat()))) { return false; From 5f5a64616e28773237155d3a36992b8212647253 Mon Sep 17 00:00:00 2001 From: Nexius Date: Sat, 13 Sep 2025 17:25:55 +0300 Subject: [PATCH 7/8] Code cleanup, normalize trailer quat --- Server/Source/player_pool.hpp | 53 +++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index f53465cf7..c488de21c 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -881,7 +881,9 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public || (aimSync.CamMode >= 48u && aimSync.CamMode <= 50u) || aimSync.CamMode == 52u || aimSync.CamMode == 54u || aimSync.CamMode == 60u || aimSync.CamMode == 61u || aimSync.CamMode > 64u) + { aimSync.CamMode = 4u; + } aimSync.PlayerID = player.poolID; player.aimSync_ = aimSync; @@ -1393,7 +1395,8 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public Player& player = static_cast(peer); int vehicleModel = vehicle.getModel(); - if (vehicle.isRespawning() || (!vehicle.isStreamedInForPlayer(player) && !(vehicleModel == 569 || vehicleModel == 570))) // Check if vehicle is a train carriage (TODO: Move Vehicle::isTrainCarriage to SDK/Components/Vehicles/Impl/vehicle_models.hpp) + // Check if vehicle is a train carriage (TODO: Move Vehicle::isTrainCarriage to SDK/Components/Vehicles/Impl/vehicle_models.hpp) + if (vehicle.isRespawning() || (!vehicle.isStreamedInForPlayer(player) && !(vehicleModel == 569 || vehicleModel == 570))) { return false; } @@ -1472,12 +1475,16 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public return false; } - if (unoccupiedSync.AngularVelocity.x < -1.0f || unoccupiedSync.AngularVelocity.x > 1.0f || unoccupiedSync.AngularVelocity.y < -1.0f || unoccupiedSync.AngularVelocity.y > 1.0f || unoccupiedSync.AngularVelocity.z < -1.0f || unoccupiedSync.AngularVelocity.z > 1.0f) + if (unoccupiedSync.AngularVelocity.x < -1.0f || unoccupiedSync.AngularVelocity.x > 1.0f + || unoccupiedSync.AngularVelocity.y < -1.0f || unoccupiedSync.AngularVelocity.y > 1.0f + || unoccupiedSync.AngularVelocity.z < -1.0f || unoccupiedSync.AngularVelocity.z > 1.0f) { return false; } - if (glm::abs(1.0 - glm::length(unoccupiedSync.Roll)) >= 0.000001 || glm::abs(1.0 - glm::length(unoccupiedSync.Rotation)) >= 0.000001 || glm::abs(unoccupiedSync.Roll.x * unoccupiedSync.Rotation.x + unoccupiedSync.Roll.y * unoccupiedSync.Rotation.y + unoccupiedSync.Roll.z * unoccupiedSync.Rotation.z) >= 0.000001) + if (glm::abs(1.0 - glm::length(unoccupiedSync.Roll)) >= 0.000001 + || glm::abs(1.0 - glm::length(unoccupiedSync.Rotation)) >= 0.000001 + || glm::abs(glm::dot(unoccupiedSync.Roll, unoccupiedSync.Rotation)) >= 0.000001) { return false; } @@ -1498,19 +1505,26 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public IPlayerVehicleData* playerVehicleData = queryExtension(peer); - if (vehicle.getDriver()) - { - return false; - } - else if (!vehicle.isStreamedInForPlayer(peer)) + if (vehicle.getDriver() || !vehicle.isStreamedInForPlayer(peer)) { return false; } - else if (!unoccupiedSync.SeatID && player.state_ == PlayerState_Passenger) + else if (unoccupiedSync.SeatID > 0) { - return false; + if (player.state_ != PlayerState_Passenger) + { + return false; + } + else if (playerVehicleData && playerVehicleData->getVehicle() != &vehicle) + { + return false; + } + else if (playerVehicleData && unoccupiedSync.SeatID != playerVehicleData->getSeat()) + { + return false; + } } - else if (unoccupiedSync.SeatID && (player.state_ != PlayerState_Passenger || (playerVehicleData && playerVehicleData->getVehicle() != &vehicle) || (playerVehicleData && unoccupiedSync.SeatID != playerVehicleData->getSeat()))) + else if (player.state_ == PlayerState_Passenger) { return false; } @@ -1542,11 +1556,26 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public return false; } - if (trailerSync.TurnVelocity.x < -1.0f || trailerSync.TurnVelocity.x > 1.0f || trailerSync.TurnVelocity.y < -1.0f || trailerSync.TurnVelocity.y > 1.0f || trailerSync.TurnVelocity.z < -1.0f || trailerSync.TurnVelocity.z > 1.0f) + if (trailerSync.TurnVelocity.x < -1.0f || trailerSync.TurnVelocity.x > 1.0f + || trailerSync.TurnVelocity.y < -1.0f || trailerSync.TurnVelocity.y > 1.0f + || trailerSync.TurnVelocity.z < -1.0f || trailerSync.TurnVelocity.z > 1.0f) { return false; } + float magnitude = glm::length(trailerSync.Quat); + if (std::abs(1.0f - magnitude) >= 0.000001f) + { + if (magnitude < 0.1f) + { + trailerSync.Quat = glm::vec4(0.5f); + } + else + { + trailerSync.Quat /= magnitude; + } + } + IVehicle* vehiclePtr = self.vehiclesComponent->get(trailerSync.VehicleID); if (!vehiclePtr) { From c118bf6f64497d39ca006efa44c7069d523256b2 Mon Sep 17 00:00:00 2001 From: Nexius Date: Tue, 16 Sep 2025 18:31:59 +0300 Subject: [PATCH 8/8] Move quat normalization after dropping checks --- Server/Source/player_pool.hpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Server/Source/player_pool.hpp b/Server/Source/player_pool.hpp index c488de21c..9db36280d 100644 --- a/Server/Source/player_pool.hpp +++ b/Server/Source/player_pool.hpp @@ -1563,19 +1563,6 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public return false; } - float magnitude = glm::length(trailerSync.Quat); - if (std::abs(1.0f - magnitude) >= 0.000001f) - { - if (magnitude < 0.1f) - { - trailerSync.Quat = glm::vec4(0.5f); - } - else - { - trailerSync.Quat /= magnitude; - } - } - IVehicle* vehiclePtr = self.vehiclesComponent->get(trailerSync.VehicleID); if (!vehiclePtr) { @@ -1598,6 +1585,20 @@ struct PlayerPool final : public IPlayerPool, public NetworkEventHandler, public return false; } + // Normalise quaternions + float magnitude = glm::length(trailerSync.Quat); + if (std::abs(1.0f - magnitude) >= 0.000001f) + { + if (magnitude < 0.1f) + { + trailerSync.Quat = glm::vec4(0.5f); + } + else + { + trailerSync.Quat /= magnitude; + } + } + if (vehicle.updateFromTrailerSync(trailerSync, peer)) { trailerSync.PlayerID = player.poolID;