From bc135c02b404d4eddb0e0bb35968b148eb8443b9 Mon Sep 17 00:00:00 2001 From: "Berk L." Date: Sun, 19 Apr 2026 15:58:15 +0300 Subject: [PATCH 1/3] fix: reset paintjob on vehicle respawn Vehicle paintjob was not being cleared in _respawn() while all other modifications (mods, colours, damage) were properly reset, causing the paintjob to persist after vehicle respawn. Fixes #1212 --- Server/Components/Vehicles/vehicle.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/Components/Vehicles/vehicle.cpp b/Server/Components/Vehicles/vehicle.cpp index be6ab215c..836cb417d 100644 --- a/Server/Components/Vehicles/vehicle.cpp +++ b/Server/Components/Vehicles/vehicle.cpp @@ -707,6 +707,7 @@ void Vehicle::_respawn() lastOccupiedChange = TimePoint(); timeOfSpawn = Time::now(); mods.fill(0); + paintJob = 0; doorDamage = 0; tyreDamage = 0; lightDamage = 0; From 9b474e40e7e51002111a993a911ca81f5a1d09b0 Mon Sep 17 00:00:00 2001 From: "Berk L." Date: Sun, 19 Apr 2026 16:30:32 +0300 Subject: [PATCH 2/3] fix: move SeedCookie after rakNetServer.Start() to prevent connection failures RakPeer::Start() internally calls RakPeer::Disconnect() which resets RakNet internal state. SeedCookie() was called before Start(), meaning the seeded cookie was discarded. Clients connecting in the window between server start and the first onTick() re-seed would fail cookie validation and have their packets silently ignored (issue #1211). Also adds an explanatory comment to the paintjob reset fix. --- .../Components/LegacyNetwork/legacy_network_impl.cpp | 10 ++++++++-- Server/Components/Vehicles/vehicle.cpp | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Server/Components/LegacyNetwork/legacy_network_impl.cpp b/Server/Components/LegacyNetwork/legacy_network_impl.cpp index 3cbe91088..354d182da 100644 --- a/Server/Components/LegacyNetwork/legacy_network_impl.cpp +++ b/Server/Components/LegacyNetwork/legacy_network_impl.cpp @@ -828,8 +828,6 @@ void RakNetLegacyNetwork::start() { SAMPRakNet::Init(core); SAMPRakNet::SeedToken(); - lastCookieSeed = Time::now(); - SAMPRakNet::SeedCookie(); playerFromRakIndex.fill(nullptr); @@ -915,6 +913,14 @@ void RakNetLegacyNetwork::start() { rakNetServer.ReserveSlots(npcComponent->count()); } + + // SeedCookie must be called after rakNetServer.Start() because Start() internally calls + // RakPeer::Disconnect() which resets RakNet's internal state. Seeding the cookie before + // Start() means the seeded value is discarded, leaving the cookie in an invalid state + // until the next onTick() re-seed. Clients connecting in that window fail the cookie + // check and their packets are silently ignored (issue #1211). + lastCookieSeed = Time::now(); + SAMPRakNet::SeedCookie(); } void RakNetLegacyNetwork::onTick(Microseconds elapsed, TimePoint now) diff --git a/Server/Components/Vehicles/vehicle.cpp b/Server/Components/Vehicles/vehicle.cpp index 836cb417d..9eaebd407 100644 --- a/Server/Components/Vehicles/vehicle.cpp +++ b/Server/Components/Vehicles/vehicle.cpp @@ -707,6 +707,7 @@ void Vehicle::_respawn() lastOccupiedChange = TimePoint(); timeOfSpawn = Time::now(); mods.fill(0); + // paintJob was not reset here unlike mods and colours, causing it to persist after vehicle respawn (issue #1212) paintJob = 0; doorDamage = 0; tyreDamage = 0; From a0b593ec8aa486689dbf55c2789d4a58ae403d66 Mon Sep 17 00:00:00 2001 From: "Berk L." Date: Sun, 19 Apr 2026 16:42:28 +0300 Subject: [PATCH 3/3] fix: trigger OnVehicleDeath and respawn for NPC-driven vehicles NPC players have no game client to send vehicle exit or death RPCs, so their vehicles remained permanently "occupied" after dying. The onTick death/respawn path was gated behind !isOccupied(), causing OnVehicleDeath and OnVehicleSpawn to never fire for NPC-driven vehicles. Detect the NPC-only occupied dead case and bypass the occupation check, allowing death events and respawn to proceed normally (respawn already clears the driver pointer via _respawn()). Fixes #1196 --- Server/Components/Vehicles/vehicles_impl.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Server/Components/Vehicles/vehicles_impl.hpp b/Server/Components/Vehicles/vehicles_impl.hpp index 0249857ec..2274f8066 100644 --- a/Server/Components/Vehicles/vehicles_impl.hpp +++ b/Server/Components/Vehicles/vehicles_impl.hpp @@ -590,7 +590,12 @@ class VehiclesComponent final : public IVehiclesComponent, public CoreEventHandl Vehicle* vehicle = static_cast(v); const Seconds delay = vehicle->getRespawnDelay(); - if (!vehicle->isOccupied()) + // A dead vehicle driven solely by an NPC has no client to send exit/death RPCs, + // so it stays "occupied" indefinitely. Treat it as unoccupied for death/respawn handling. + IPlayer* vehicleDriver = vehicle->getDriver(); + bool deadNPCOnly = vehicle->isDead() && vehicleDriver != nullptr && vehicleDriver->isBot() && vehicle->getPassengers().empty(); + + if (!vehicle->isOccupied() || deadNPCOnly) { TimePoint lastOccupied = vehicle->getLastOccupiedTime(); if (vehicle->isDead())