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 be6ab215c..9eaebd407 100644 --- a/Server/Components/Vehicles/vehicle.cpp +++ b/Server/Components/Vehicles/vehicle.cpp @@ -707,6 +707,8 @@ 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; lightDamage = 0; 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())