From 950ae04a0f239acacc7107f1d2b0b5f0e1e0a894 Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Thu, 7 Aug 2025 13:21:12 +0200 Subject: [PATCH] ed --- .../nordi_antidespawn/client/main.lua | 280 ++++++++--- .../nordi_antidespawn/server/main.lua | 453 ++++++++++-------- 2 files changed, 469 insertions(+), 264 deletions(-) diff --git a/resources/[carscripts]/nordi_antidespawn/client/main.lua b/resources/[carscripts]/nordi_antidespawn/client/main.lua index d9b472c18..4c2ec22bc 100644 --- a/resources/[carscripts]/nordi_antidespawn/client/main.lua +++ b/resources/[carscripts]/nordi_antidespawn/client/main.lua @@ -2,6 +2,8 @@ local QBCore = exports['qb-core']:GetCoreObject() local trackedVehicles = {} local lastKnownCoords = {} local garagePending = {} -- Fahrzeuge, die gerade in die Garage gestellt werden +local spawnedVehicles = {} -- Track recently spawned vehicles to prevent duplication +local vehicleOwnership = {} -- Cache vehicle ownership status -- Helper function to count table entries function tableLength(T) @@ -17,40 +19,67 @@ local function Debug(msg) end end --- Extrem starke Anti-Despawn Funktion --- IMPORTANT: Move this function definition to the top, before it's called -local function PreventDespawn(vehicle) - if not DoesEntityExist(vehicle) then return false end +-- Anti-Duplication: Check if a vehicle with this plate already exists in the world +local function DoesVehicleExistInWorld(plate) + local vehicles = GetGamePool('CVehicle') + local count = 0 - -- Grundlegende Persistenz - SetEntityAsMissionEntity(vehicle, true, true) - SetVehicleHasBeenOwnedByPlayer(vehicle, true) - SetVehicleNeedsToBeHotwired(vehicle, false) - - -- Zusätzliche Flags - SetEntityLoadCollisionFlag(vehicle, true) - SetVehicleIsStolen(vehicle, false) - SetVehicleIsWanted(vehicle, false) - - -- Verhindere dass das Fahrzeug als "abandoned" markiert wird - if DecorIsRegisteredAsType("IgnoredByQuickSave", 2) then - DecorSetBool(vehicle, "IgnoredByQuickSave", false) + for _, vehicle in pairs(vehicles) do + local vehPlate = QBCore.Functions.GetPlate(vehicle) + if vehPlate == plate then + count = count + 1 + if count > 1 then + -- More than one vehicle with this plate exists! + Debug("DUPLICATION DETECTED: Multiple vehicles with plate " .. plate .. " exist!") + return true + end + end end - -- Setze Fahrzeug auf Boden - SetVehicleOnGroundProperly(vehicle) - - -- Verhindere dass das Fahrzeug gelöscht wird - NetworkRegisterEntityAsNetworked(vehicle) - local netID = NetworkGetNetworkIdFromEntity(vehicle) - SetNetworkIdExistsOnAllMachines(netID, true) - SetNetworkIdCanMigrate(netID, true) - - return true + return false end --- Function to check if player owns the vehicle +-- Anti-Duplication: Check if vehicle was recently spawned +local function WasVehicleRecentlySpawned(plate) + if spawnedVehicles[plate] then + local timeSinceSpawn = GetGameTimer() - spawnedVehicles[plate] + if timeSinceSpawn < 60000 then -- 60 seconds cooldown + Debug("Anti-Dupe: Vehicle " .. plate .. " was recently spawned (" .. math.floor(timeSinceSpawn/1000) .. " seconds ago)") + return true + else + -- Reset the timer if it's been more than 60 seconds + spawnedVehicles[plate] = nil + end + end + return false +end + +-- Anti-Duplication: Mark vehicle as recently spawned +local function MarkVehicleAsSpawned(plate) + spawnedVehicles[plate] = GetGameTimer() + Debug("Anti-Dupe: Marked vehicle " .. plate .. " as recently spawned") + + -- Clean up old entries every 5 minutes + SetTimeout(300000, function() + for p, time in pairs(spawnedVehicles) do + if GetGameTimer() - time > 300000 then -- 5 minutes + spawnedVehicles[p] = nil + end + end + end) +end + +-- Function to check if player owns the vehicle with caching local function DoesPlayerOwnVehicle(plate) + -- Check cache first + if vehicleOwnership[plate] ~= nil then + -- Cache expires after 5 minutes + local timeSinceCheck = GetGameTimer() - vehicleOwnership[plate].timestamp + if timeSinceCheck < 300000 then -- 5 minutes + return vehicleOwnership[plate].owned + end + end + local playerData = QBCore.Functions.GetPlayerData() if not playerData then return false end @@ -61,10 +90,14 @@ local function DoesPlayerOwnVehicle(plate) TriggerServerEvent('antidespawn:server:checkVehicleOwnership', plate) -- Register one-time event handler for the response - RegisterNetEvent('antidespawn:client:vehicleOwnershipResult') local eventHandler = AddEventHandler('antidespawn:client:vehicleOwnershipResult', function(result, checkPlate) if plate == checkPlate then isOwned = result + -- Cache the result + vehicleOwnership[plate] = { + owned = result, + timestamp = GetGameTimer() + } end end) @@ -102,6 +135,37 @@ local function IsVehicleClassAllowed(vehicle) return false end +-- Extrem starke Anti-Despawn Funktion +local function PreventDespawn(vehicle) + if not DoesEntityExist(vehicle) then return false end + + -- Grundlegende Persistenz + SetEntityAsMissionEntity(vehicle, true, true) + SetVehicleHasBeenOwnedByPlayer(vehicle, true) + SetVehicleNeedsToBeHotwired(vehicle, false) + + -- Zusätzliche Flags + SetEntityLoadCollisionFlag(vehicle, true) + SetVehicleIsStolen(vehicle, false) + SetVehicleIsWanted(vehicle, false) + + -- Verhindere dass das Fahrzeug als "abandoned" markiert wird + if DecorIsRegisteredAsType("IgnoredByQuickSave", 2) then + DecorSetBool(vehicle, "IgnoredByQuickSave", false) + end + + -- Setze Fahrzeug auf Boden + SetVehicleOnGroundProperly(vehicle) + + -- Verhindere dass das Fahrzeug gelöscht wird + NetworkRegisterEntityAsNetworked(vehicle) + local netID = NetworkGetNetworkIdFromEntity(vehicle) + SetNetworkIdExistsOnAllMachines(netID, true) + SetNetworkIdCanMigrate(netID, true) + + return true +end + -- Funktion um Fahrzeugmods zu erhalten local function GetVehicleMods(vehicle) local mods = {} @@ -301,8 +365,6 @@ function GetVehicleByPlate(plate) return nil end --- AFTER ALL FUNCTIONS ARE DEFINED, THEN ADD EVENT HANDLERS AND THREADS - -- Event Handler für Fahrzeug betreten (nur Fahrersitz) CreateThread(function() while true do @@ -320,6 +382,14 @@ CreateThread(function() if driver == playerPed and IsVehicleClassAllowed(currentVehicle) then local plate = QBCore.Functions.GetPlate(currentVehicle) + -- Anti-Duplication: Check if this plate already exists multiple times + if DoesVehicleExistInWorld(plate) then + Debug("Anti-Dupe: Detected duplicate vehicle with plate " .. plate .. ", not tracking") + -- Optionally, you could delete the duplicate here + -- DeleteEntity(currentVehicle) + goto continue + end + -- Check if this vehicle is already being tracked if not trackedVehicles[plate] and not garagePending[plate] then -- Check if player owns this vehicle @@ -328,7 +398,6 @@ CreateThread(function() local maxTrackedVehicles = 100 -- Adjust as needed if tableLength(trackedVehicles) >= maxTrackedVehicles then Debug("Maximum number of tracked vehicles reached") - -- You could implement logic to remove the oldest vehicle here else trackedVehicles[plate] = currentVehicle @@ -356,12 +425,10 @@ CreateThread(function() end end end + ::continue:: end end) --- Rest of your code (threads, event handlers, etc.) - - -- Kontinuierliche Despawn-Verhinderung für alle getrackten Fahrzeuge CreateThread(function() while true do @@ -371,6 +438,15 @@ CreateThread(function() local playerPos = GetEntityCoords(playerPed) for plate, vehicle in pairs(trackedVehicles) do + -- Anti-Duplication: Check if multiple vehicles with this plate exist + if DoesVehicleExistInWorld(plate) then + Debug("Anti-Dupe: Detected duplicate during tracking for plate " .. plate .. ", removing from tracking") + trackedVehicles[plate] = nil + lastKnownCoords[plate] = nil + TriggerServerEvent('antidespawn:server:removeVehicle', plate) + goto continue + end + -- Prüfe ob Fahrzeug gerade in die Garage gestellt wird if garagePending[plate] then Debug("Fahrzeug wird gerade in Garage gestellt, entferne aus Tracking: " .. plate) @@ -406,6 +482,14 @@ CreateThread(function() else Debug("Fahrzeug existiert nicht mehr: " .. plate) + -- Anti-Duplication: Check if vehicle was recently spawned before respawning + if WasVehicleRecentlySpawned(plate) then + Debug("Anti-Dupe: Not respawning recently spawned vehicle: " .. plate) + trackedVehicles[plate] = nil + lastKnownCoords[plate] = nil + goto continue + end + -- Versuche Fahrzeug wiederherzustellen, aber nur wenn es nicht in die Garage gestellt wird if lastKnownCoords[plate] and not garagePending[plate] then Debug("Versuche Fahrzeug wiederherzustellen: " .. plate) @@ -420,6 +504,7 @@ CreateThread(function() lastKnownCoords[plate] = nil end end + ::continue:: end end end) @@ -461,6 +546,12 @@ end) RegisterNetEvent('antidespawn:client:spawnVehicle', function(data) Debug("Spawne Fahrzeug: " .. data.plate) + -- Anti-Duplication: Check if vehicle was recently spawned + if WasVehicleRecentlySpawned(data.plate) then + Debug("Anti-Dupe: Blocking respawn of recently spawned vehicle: " .. data.plate) + return + end + -- Prüfe ob Fahrzeug bereits existiert local existingVehicle = GetVehicleByPlate(data.plate) if existingVehicle then @@ -477,6 +568,12 @@ RegisterNetEvent('antidespawn:client:spawnVehicle', function(data) return end + -- Anti-Duplication: Check if player owns this vehicle + if not DoesPlayerOwnVehicle(data.plate) then + Debug("Anti-Dupe: Player does not own vehicle, not spawning: " .. data.plate) + return + end + -- Konvertiere Modell zu Hash wenn nötig local modelHash = data.model if type(modelHash) == "string" then @@ -508,6 +605,13 @@ RegisterNetEvent('antidespawn:client:spawnVehicle', function(data) if HasModelLoaded(modelHash) then Debug("Modell geladen, erstelle Fahrzeug...") + -- Anti-Duplication: Final check before spawning + if GetVehicleByPlate(data.plate) then + Debug("Anti-Dupe: Vehicle with plate " .. data.plate .. " appeared during model loading, aborting spawn") + SetModelAsNoLongerNeeded(modelHash) + return + end + -- Verwende CREATE_AUTOMOBILE für bessere Persistenz local vehicle @@ -522,6 +626,9 @@ RegisterNetEvent('antidespawn:client:spawnVehicle', function(data) end if DoesEntityExist(vehicle) then + -- Anti-Duplication: Mark as recently spawned + MarkVehicleAsSpawned(data.plate) + -- Warte bis Fahrzeug vollständig geladen ist Wait(500) @@ -560,17 +667,6 @@ RegisterNetEvent('antidespawn:client:spawnVehicle', function(data) end end) --- Hilfsfunktion um Fahrzeug anhand Kennzeichen zu finden -function GetVehicleByPlate(plate) - local vehicles = GetGamePool('CVehicle') - for _, vehicle in pairs(vehicles) do - if QBCore.Functions.GetPlate(vehicle) == plate then - return vehicle - end - end - return nil -end - -- Event für Garage Store (wird ausgelöst, wenn der Spieler ein Fahrzeug in die Garage stellen will) RegisterNetEvent('jg-advancedgarages:client:store-vehicle', function(garageId, garageVehicleType) local playerPed = PlayerPedId() @@ -663,21 +759,32 @@ RegisterCommand('fixvehicle', function() if vehicle ~= 0 then local plate = QBCore.Functions.GetPlate(vehicle) + -- Anti-Duplication: Check if this plate already exists multiple times + if DoesVehicleExistInWorld(plate) then + Debug("Anti-Dupe: Detected duplicate vehicle with plate " .. plate .. ", not fixing") + return + end + -- Prüfe ob Fahrzeug gerade in die Garage gestellt wird if not garagePending[plate] then - PreventDespawn(vehicle) - trackedVehicles[plate] = vehicle - lastKnownCoords[plate] = GetEntityCoords(vehicle) - - -- Registriere Fahrzeug beim Server - local vehicleCoords = GetEntityCoords(vehicle) - local vehicleHeading = GetEntityHeading(vehicle) - local vehicleModel = GetEntityModel(vehicle) - local vehicleMods = GetVehicleMods(vehicle) - - TriggerServerEvent('antidespawn:server:registerVehicle', plate, vehicleModel, vehicleCoords, vehicleHeading, vehicleMods) - - Debug("Anti-Despawn für Fahrzeug aktiviert: " .. plate) + -- Check if player owns this vehicle + if DoesPlayerOwnVehicle(plate) then + PreventDespawn(vehicle) + trackedVehicles[plate] = vehicle + lastKnownCoords[plate] = GetEntityCoords(vehicle) + + -- Registriere Fahrzeug beim Server + local vehicleCoords = GetEntityCoords(vehicle) + local vehicleHeading = GetEntityHeading(vehicle) + local vehicleModel = GetEntityModel(vehicle) + local vehicleMods = GetVehicleMods(vehicle) + + TriggerServerEvent('antidespawn:server:registerVehicle', plate, vehicleModel, vehicleCoords, vehicleHeading, vehicleMods) + + Debug("Anti-Despawn für Fahrzeug aktiviert: " .. plate) + else + Debug("Fahrzeug gehört nicht dem Spieler, kann nicht fixiert werden: " .. plate) + end else Debug("Fahrzeug wird gerade in Garage gestellt, kann nicht fixiert werden: " .. plate) end @@ -686,11 +793,66 @@ RegisterCommand('fixvehicle', function() end end, false) +-- Server-side event handler for ownership verification +RegisterNetEvent('antidespawn:client:vehicleOwnershipResult', function(result, plate) + -- This event is handled by the DoesPlayerOwnVehicle function +end) + +-- Clean up resources when script stops AddEventHandler('onResourceStop', function(resourceName) if resourceName == GetCurrentResourceName() then Debug("Resource stopping, clearing all data") trackedVehicles = {} lastKnownCoords = {} garagePending = {} + spawnedVehicles = {} + vehicleOwnership = {} end end) + +-- Debug command to check vehicle duplication +RegisterCommand('checkdupes', function() + local vehicles = GetGamePool('CVehicle') + local plates = {} + local dupes = {} + + for _, vehicle in pairs(vehicles) do + local plate = QBCore.Functions.GetPlate(vehicle) + if plates[plate] then + if not dupes[plate] then + dupes[plate] = 2 + else + dupes[plate] = dupes[plate] + 1 + end + else + plates[plate] = true + end + end + + local dupeCount = 0 + for plate, count in pairs(dupes) do + Debug("Duplicate found: " .. plate .. " (Count: " .. count .. ")") + dupeCount = dupeCount + 1 + end + + if dupeCount == 0 then + Debug("No duplicate vehicles found") + else + Debug("Found " .. dupeCount .. " duplicate vehicles") + end +end, false) + +-- Debug command to clear recently spawned vehicles list +RegisterCommand('clearspawned', function() + spawnedVehicles = {} + Debug("Cleared recently spawned vehicles list") +end, false) + +-- Debug command to clear ownership cache +RegisterCommand('clearownership', function() + vehicleOwnership = {} + Debug("Cleared vehicle ownership cache") +end, false) + + + diff --git a/resources/[carscripts]/nordi_antidespawn/server/main.lua b/resources/[carscripts]/nordi_antidespawn/server/main.lua index 94ef7a772..1e7eef80b 100644 --- a/resources/[carscripts]/nordi_antidespawn/server/main.lua +++ b/resources/[carscripts]/nordi_antidespawn/server/main.lua @@ -1,5 +1,6 @@ local QBCore = exports['qb-core']:GetCoreObject() local vehicles = {} +local activeSpawns = {} -- Track active spawn requests to prevent duplicates -- Debug Funktion local function Debug(msg) @@ -65,26 +66,63 @@ CreateThread(function() end) end) --- Registriere ein Fahrzeug +-- Check if a player owns a vehicle +RegisterNetEvent('antidespawn:server:checkVehicleOwnership', function(plate) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + + if not Player then + TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, false, plate) + return + end + + MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ?', {plate, Player.PlayerData.citizenid}, function(result) + local isOwned = result and #result > 0 + TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, isOwned, plate) + end) +end) + +-- Enhanced anti-duplication for vehicle registration RegisterNetEvent('antidespawn:server:registerVehicle', function(plate, model, coords, heading, mods) local src = source + local Player = QBCore.Functions.GetPlayer(src) - if not plate or not model or not coords then - Debug("Ungültige Daten beim Registrieren eines Fahrzeugs") - return - end + if not Player then return end - -- Stelle sicher, dass das Modell als Zahl gespeichert wird - if type(model) == "string" then - model = tonumber(model) or model - end - - -- Prüfe ob Fahrzeug in der Garage ist - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND state = ?', {plate, 1}, function(result) - if result and #result > 0 then + -- Verify ownership before registering + MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result) + if not result or #result == 0 then + Debug("Vehicle not found in database: " .. plate) + return + end + + -- Check if player owns the vehicle + local isOwner = false + for _, veh in ipairs(result) do + if veh.citizenid == Player.PlayerData.citizenid then + isOwner = true + break + end + end + + if not isOwner then + Debug("Player does not own vehicle: " .. plate) + return + end + + -- Check if vehicle is in garage + local inGarage = false + for _, veh in ipairs(result) do + if veh.state == 1 then + inGarage = true + break + end + end + + if inGarage then Debug("Fahrzeug ist in der Garage, nicht registrieren: " .. plate) - -- Entferne aus Anti-Despawn Datenbank falls vorhanden + -- Remove from Anti-Despawn database if present if vehicles[plate] then vehicles[plate] = nil MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) @@ -93,6 +131,7 @@ RegisterNetEvent('antidespawn:server:registerVehicle', function(plate, model, co return end + -- Continue with registration as before vehicles[plate] = { model = model, coords = coords, @@ -101,9 +140,10 @@ RegisterNetEvent('antidespawn:server:registerVehicle', function(plate, model, co mods = mods, last_updated = os.time() } + MySQL.query("INSERT INTO vehicle_antidespawn (plate, model, coords, heading, fuel, mods) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE coords = VALUES(coords), heading = VALUES(heading), mods = VALUES(mods), last_updated = CURRENT_TIMESTAMP", { plate, - tostring(model), -- Speichere als String in der Datenbank + tostring(model), json.encode(coords), heading, 100, @@ -116,11 +156,43 @@ end) -- Aktualisiere ein Fahrzeug RegisterNetEvent('antidespawn:server:updateVehicle', function(plate, coords, heading, mods) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + + if not Player then return end if not vehicles[plate] then return end - -- Prüfe ob Fahrzeug in der Garage ist - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND state = ?', {plate, 1}, function(result) - if result and #result > 0 then + -- Verify ownership before updating + MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result) + if not result or #result == 0 then + Debug("Vehicle not found in database: " .. plate) + return + end + + -- Check if player owns the vehicle + local isOwner = false + for _, veh in ipairs(result) do + if veh.citizenid == Player.PlayerData.citizenid then + isOwner = true + break + end + end + + if not isOwner then + Debug("Player does not own vehicle: " .. plate) + return + end + + -- Check if vehicle is in garage + local inGarage = false + for _, veh in ipairs(result) do + if veh.state == 1 then + inGarage = true + break + end + end + + if inGarage then Debug("Fahrzeug ist in der Garage, entferne aus Tracking: " .. plate) vehicles[plate] = nil @@ -148,6 +220,7 @@ end) -- Entferne ein Fahrzeug RegisterNetEvent('antidespawn:server:removeVehicle', function(plate) + local src = source if not vehicles[plate] then return end vehicles[plate] = nil @@ -159,30 +232,76 @@ RegisterNetEvent('antidespawn:server:removeVehicle', function(plate) Debug("Fahrzeug entfernt: " .. plate) end) --- Respawn ein Fahrzeug +-- Enhanced anti-duplication for vehicle respawning RegisterNetEvent('antidespawn:server:respawnVehicle', function(plate) local src = source + local Player = QBCore.Functions.GetPlayer(src) - if not vehicles[plate] then - Debug("Fahrzeug nicht in Datenbank: " .. plate) - return + if not Player then return end + + -- Anti-Duplication: Check if there's already an active spawn request for this plate + if activeSpawns[plate] then + Debug("Anti-Dupe: Already processing spawn request for: " .. plate) + return end - -- Prüfe ob Fahrzeug in der Garage ist - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND state = ?', {plate, 1}, function(result) - if result and #result > 0 then - Debug("Fahrzeug ist in der Garage, nicht respawnen: " .. plate) - - -- Entferne aus Anti-Despawn Datenbank - vehicles[plate] = nil - - MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", { - plate - }) + -- Mark as active spawn + activeSpawns[plate] = true + + -- Set a timeout to clear the active spawn status + SetTimeout(10000, function() + activeSpawns[plate] = nil + end) + + -- Verify ownership before respawning + MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result) + if not result or #result == 0 then + Debug("Vehicle not found in database: " .. plate) + activeSpawns[plate] = nil return end - -- Sende Spawn-Event zurück an den Client + -- Check if player owns the vehicle + local isOwner = false + for _, veh in ipairs(result) do + if veh.citizenid == Player.PlayerData.citizenid then + isOwner = true + break + end + end + + if not isOwner then + Debug("Player does not own vehicle: " .. plate) + activeSpawns[plate] = nil + return + end + + if not vehicles[plate] then + Debug("Fahrzeug nicht in Datenbank: " .. plate) + activeSpawns[plate] = nil + return + end + + -- Check if vehicle is in garage + local inGarage = false + for _, veh in ipairs(result) do + if veh.state == 1 then + inGarage = true + break + end + end + + if inGarage then + Debug("Fahrzeug ist in der Garage, nicht respawnen: " .. plate) + + -- Remove from Anti-Despawn database + vehicles[plate] = nil + MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) + activeSpawns[plate] = nil + return + end + + -- Send spawn event back to client TriggerClientEvent('antidespawn:client:spawnVehicle', src, { plate = plate, model = vehicles[plate].model, @@ -212,68 +331,83 @@ RegisterNetEvent('antidespawn:server:loadVehicles', function() local loadedCount = 0 local vehiclesToLoad = {} - -- Sammle alle Fahrzeuge, die geladen werden sollen - for plate, vehicle in pairs(vehicles) do - -- Prüfe ob das Fahrzeug in der Garage ist - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ?', {plate}, function(result) - -- Prüfe ob das Fahrzeug überhaupt existiert - if not result or #result == 0 then - Debug("Fahrzeug existiert nicht in player_vehicles: " .. plate) - -- Entferne aus Anti-Despawn Datenbank - vehicles[plate] = nil - MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) - return - end - - -- Prüfe ob das Fahrzeug in der Garage ist (state = 1) - local inGarage = false - for _, veh in ipairs(result) do - if veh.state == 1 then - inGarage = true - break - end - end - - if inGarage then - Debug("Fahrzeug ist in der Garage, nicht laden: " .. plate) - -- Entferne aus Anti-Despawn Datenbank - vehicles[plate] = nil - MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) - return - end - - -- Lade nur Fahrzeuge in der Nähe des Spielers - local distance = #(playerCoords - vector3(vehicle.coords.x, vehicle.coords.y, vehicle.coords.z)) - - if distance < 100.0 then - -- Stelle sicher, dass das Modell als Zahl gespeichert ist - local model = vehicle.model - if type(model) == "string" then - model = tonumber(model) or model - end - - table.insert(vehiclesToLoad, { - plate = plate, - model = model, - coords = vehicle.coords, - heading = vehicle.heading, - fuel = vehicle.fuel, - mods = vehicle.mods - }) - - loadedCount = loadedCount + 1 - end - end) - end - - -- Warte kurz und lade dann die Fahrzeuge - SetTimeout(3000, function() - for _, vehicleData in ipairs(vehiclesToLoad) do - TriggerClientEvent('antidespawn:client:spawnVehicle', src, vehicleData) - Debug("Fahrzeug für Spieler geladen: " .. vehicleData.plate) + -- Get all vehicles owned by this player + MySQL.query('SELECT * FROM player_vehicles WHERE citizenid = ?', {Player.PlayerData.citizenid}, function(ownedVehicles) + if not ownedVehicles or #ownedVehicles == 0 then + Debug("Spieler besitzt keine Fahrzeuge") + return end - Debug("Fahrzeugladung abgeschlossen. " .. loadedCount .. " Fahrzeuge geladen.") + -- Create a lookup table for owned vehicles + local ownedPlates = {} + for _, veh in ipairs(ownedVehicles) do + ownedPlates[veh.plate] = veh.state + end + + -- Sammle alle Fahrzeuge, die geladen werden sollen + for plate, vehicle in pairs(vehicles) do + -- Check if player owns this vehicle + if ownedPlates[plate] then + -- Skip if vehicle is in garage (state = 1) + if ownedPlates[plate] == 1 then + Debug("Fahrzeug ist in der Garage, nicht laden: " .. plate) + -- Remove from Anti-Despawn database + vehicles[plate] = nil + MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) + goto continue + end + + -- Lade nur Fahrzeuge in der Nähe des Spielers + local distance = #(playerCoords - vector3(vehicle.coords.x, vehicle.coords.y, vehicle.coords.z)) + + if distance < 100.0 then + -- Stelle sicher, dass das Modell als Zahl gespeichert ist + local model = vehicle.model + if type(model) == "string" then + model = tonumber(model) or model + end + + table.insert(vehiclesToLoad, { + plate = plate, + model = model, + coords = vehicle.coords, + heading = vehicle.heading, + fuel = vehicle.fuel, + mods = vehicle.mods + }) + + loadedCount = loadedCount + 1 + end + else + -- Vehicle not owned by this player, remove from tracking + Debug("Fahrzeug gehört nicht dem Spieler, entferne aus Tracking: " .. plate) + vehicles[plate] = nil + MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) + end + ::continue:: + end + + -- Warte kurz und lade dann die Fahrzeuge + SetTimeout(3000, function() + for _, vehicleData in ipairs(vehiclesToLoad) do + -- Anti-Duplication: Check if there's already an active spawn request for this plate + if not activeSpawns[vehicleData.plate] then + activeSpawns[vehicleData.plate] = true + + -- Set a timeout to clear the active spawn status + SetTimeout(10000, function() + activeSpawns[vehicleData.plate] = nil + end) + + TriggerClientEvent('antidespawn:client:spawnVehicle', src, vehicleData) + Debug("Fahrzeug für Spieler geladen: " .. vehicleData.plate) + else + Debug("Anti-Dupe: Already processing spawn request for: " .. vehicleData.plate) + end + end + + Debug("Fahrzeugladung abgeschlossen. " .. loadedCount .. " Fahrzeuge geladen.") + end) end) end) @@ -387,116 +521,6 @@ RegisterCommand('clearvehicles', function(source, args, rawCommand) end end, true) - --- Check if a player owns a vehicle -RegisterNetEvent('antidespawn:server:checkVehicleOwnership', function(plate) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player then return end - - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ?', {plate, Player.PlayerData.citizenid}, function(result) - local isOwned = result and #result > 0 - TriggerClientEvent('antidespawn:client:vehicleOwnershipResult', src, isOwned, plate) - end) -end) - --- Modified registerVehicle function to verify ownership -RegisterNetEvent('antidespawn:server:registerVehicle', function(plate, model, coords, heading, mods) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player then return end - - -- Verify ownership before registering - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ?', {plate, Player.PlayerData.citizenid}, function(result) - if not result or #result == 0 then - Debug("Player does not own vehicle: " .. plate) - return - end - - -- Check if vehicle is in garage - if result[1].state == 1 then - Debug("Fahrzeug ist in der Garage, nicht registrieren: " .. plate) - - -- Remove from Anti-Despawn database if present - if vehicles[plate] then - vehicles[plate] = nil - MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) - Debug("Fahrzeug aus Anti-Despawn entfernt: " .. plate) - end - return - end - - -- Continue with registration as before - vehicles[plate] = { - model = model, - coords = coords, - heading = heading, - fuel = 100, - mods = mods, - last_updated = os.time() - } - - MySQL.query("INSERT INTO vehicle_antidespawn (plate, model, coords, heading, fuel, mods) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE coords = VALUES(coords), heading = VALUES(heading), mods = VALUES(mods), last_updated = CURRENT_TIMESTAMP", { - plate, - tostring(model), - json.encode(coords), - heading, - 100, - json.encode(mods) - }) - - Debug("Fahrzeug registriert: " .. plate .. " (Modell: " .. tostring(model) .. ")") - end) -end) - --- Also modify respawnVehicle to verify ownership -RegisterNetEvent('antidespawn:server:respawnVehicle', function(plate) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player then return end - - -- Verify ownership before respawning - MySQL.query('SELECT * FROM player_vehicles WHERE plate = ? AND citizenid = ?', {plate, Player.PlayerData.citizenid}, function(result) - if not result or #result == 0 then - Debug("Player does not own vehicle: " .. plate) - return - end - - if not vehicles[plate] then - Debug("Fahrzeug nicht in Datenbank: " .. plate) - return - end - - -- Check if vehicle is in garage - if result[1].state == 1 then - Debug("Fahrzeug ist in der Garage, nicht respawnen: " .. plate) - - -- Remove from Anti-Despawn database - vehicles[plate] = nil - MySQL.query("DELETE FROM vehicle_antidespawn WHERE plate = ?", {plate}) - return - end - - -- Send spawn event back to client - TriggerClientEvent('antidespawn:client:spawnVehicle', src, { - plate = plate, - model = vehicles[plate].model, - coords = vehicles[plate].coords, - heading = vehicles[plate].heading, - fuel = vehicles[plate].fuel, - mods = vehicles[plate].mods - }) - - Debug("Fahrzeug Respawn angefordert: " .. plate) - end) -end) - - - - -- Befehl zum Leeren der Datenbank RegisterCommand('clearalldespawn', function(source, args, rawCommand) if source == 0 then -- Nur über Konsole ausführbar @@ -505,5 +529,24 @@ RegisterCommand('clearalldespawn', function(source, args, rawCommand) Debug("Alle Fahrzeuge aus der Datenbank entfernt.") end end, true) - +-- Debug command to check active spawns +RegisterCommand('activespawns', function(source, args, rawCommand) + if source == 0 then -- Nur über Konsole ausführbar + local count = 0 + for plate, _ in pairs(activeSpawns) do + Debug("Active spawn: " .. plate) + count = count + 1 + end + Debug("Total active spawns: " .. count) + end +end, true) + +-- Clean up when resource stops +AddEventHandler('onResourceStop', function(resourceName) + if resourceName == GetCurrentResourceName() then + Debug("Resource stopping, clearing all data") + vehicles = {} + activeSpawns = {} + end +end)