2025-08-06 17:33:26 +02:00
|
|
|
local QBCore = exports['qb-core']:GetCoreObject()
|
2025-08-06 18:18:04 +02:00
|
|
|
local trackedVehicles = {}
|
2025-08-06 18:32:55 +02:00
|
|
|
local lastKnownCoords = {}
|
2025-08-06 18:18:04 +02:00
|
|
|
|
|
|
|
-- Debug Funktion
|
|
|
|
local function Debug(msg)
|
|
|
|
if Config.Debug then
|
|
|
|
print("[AntiDespawn] " .. msg)
|
|
|
|
end
|
|
|
|
end
|
2025-08-06 17:33:26 +02:00
|
|
|
|
|
|
|
-- Funktion um zu prüfen ob Fahrzeugklasse erlaubt ist
|
|
|
|
local function IsVehicleClassAllowed(vehicle)
|
|
|
|
local vehicleClass = GetVehicleClass(vehicle)
|
2025-08-06 18:18:04 +02:00
|
|
|
|
|
|
|
-- Prüfe Blacklist
|
|
|
|
for _, blacklistedClass in pairs(Config.BlacklistedVehicleClasses) do
|
|
|
|
if vehicleClass == blacklistedClass then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Prüfe Whitelist
|
2025-08-06 17:33:26 +02:00
|
|
|
for _, allowedClass in pairs(Config.AllowedVehicleClasses) do
|
|
|
|
if vehicleClass == allowedClass then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
2025-08-06 18:18:04 +02:00
|
|
|
|
2025-08-06 17:33:26 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2025-08-06 18:32:55 +02:00
|
|
|
-- Extrem starke Anti-Despawn Funktion
|
2025-08-06 18:12:40 +02:00
|
|
|
local function PreventDespawn(vehicle)
|
2025-08-06 18:32:55 +02:00
|
|
|
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)
|
2025-08-06 18:12:40 +02:00
|
|
|
end
|
2025-08-06 18:32:55 +02:00
|
|
|
|
|
|
|
-- 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
|
2025-08-06 18:12:40 +02:00
|
|
|
end
|
|
|
|
|
2025-08-06 17:59:00 +02:00
|
|
|
-- Event Handler für Fahrzeug betreten (nur Fahrersitz)
|
2025-08-06 17:53:15 +02:00
|
|
|
CreateThread(function()
|
|
|
|
local lastVehicle = 0
|
|
|
|
|
|
|
|
while true do
|
|
|
|
Wait(1000)
|
|
|
|
|
|
|
|
local playerPed = PlayerPedId()
|
|
|
|
local currentVehicle = GetVehiclePedIsIn(playerPed, false)
|
|
|
|
|
2025-08-06 17:59:00 +02:00
|
|
|
-- Spieler ist als Fahrer in ein Fahrzeug eingestiegen
|
2025-08-06 17:53:15 +02:00
|
|
|
if currentVehicle ~= 0 and currentVehicle ~= lastVehicle then
|
2025-08-06 18:01:54 +02:00
|
|
|
-- Prüfe ob Spieler auf Fahrersitz ist
|
|
|
|
local driver = GetPedInVehicleSeat(currentVehicle, -1)
|
2025-08-06 17:59:00 +02:00
|
|
|
|
2025-08-06 18:01:54 +02:00
|
|
|
-- Nur wenn Spieler der Fahrer ist (Seat -1)
|
|
|
|
if driver == playerPed and IsVehicleClassAllowed(currentVehicle) then
|
2025-08-06 17:53:15 +02:00
|
|
|
local plate = QBCore.Functions.GetPlate(currentVehicle)
|
2025-08-06 18:18:04 +02:00
|
|
|
trackedVehicles[plate] = currentVehicle
|
2025-08-06 17:59:00 +02:00
|
|
|
|
2025-08-06 18:32:55 +02:00
|
|
|
-- Speichere letzte bekannte Position
|
|
|
|
lastKnownCoords[plate] = GetEntityCoords(currentVehicle)
|
|
|
|
|
2025-08-06 18:12:40 +02:00
|
|
|
-- Sofort starke Despawn-Verhinderung
|
|
|
|
PreventDespawn(currentVehicle)
|
2025-08-06 17:53:15 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
Debug("Fahrzeug wird nun getrackt: " .. plate)
|
|
|
|
|
|
|
|
-- Registriere Fahrzeug beim Server
|
|
|
|
local vehicleCoords = GetEntityCoords(currentVehicle)
|
|
|
|
local vehicleHeading = GetEntityHeading(currentVehicle)
|
|
|
|
local vehicleModel = GetEntityModel(currentVehicle)
|
|
|
|
|
|
|
|
TriggerServerEvent('antidespawn:server:registerVehicle', plate, vehicleModel, vehicleCoords, vehicleHeading)
|
2025-08-06 17:53:15 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
lastVehicle = currentVehicle
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2025-08-06 18:12:40 +02:00
|
|
|
-- Kontinuierliche Despawn-Verhinderung für alle getrackten Fahrzeuge
|
|
|
|
CreateThread(function()
|
|
|
|
while true do
|
2025-08-06 18:32:55 +02:00
|
|
|
Wait(5000) -- Alle 5 Sekunden
|
2025-08-06 18:12:40 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
for plate, vehicle in pairs(trackedVehicles) do
|
2025-08-06 17:53:15 +02:00
|
|
|
if DoesEntityExist(vehicle) then
|
2025-08-06 18:12:40 +02:00
|
|
|
PreventDespawn(vehicle)
|
2025-08-06 17:53:15 +02:00
|
|
|
|
2025-08-06 18:32:55 +02:00
|
|
|
-- Aktualisiere letzte bekannte Position
|
|
|
|
lastKnownCoords[plate] = GetEntityCoords(vehicle)
|
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
-- Aktualisiere Position
|
|
|
|
local vehicleCoords = GetEntityCoords(vehicle)
|
|
|
|
local vehicleHeading = GetEntityHeading(vehicle)
|
2025-08-06 17:49:33 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
TriggerServerEvent('antidespawn:server:updateVehicle', plate, vehicleCoords, vehicleHeading)
|
2025-08-06 17:53:15 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
Debug("Aktualisiere Fahrzeug: " .. plate)
|
2025-08-06 17:53:15 +02:00
|
|
|
else
|
2025-08-06 18:18:04 +02:00
|
|
|
Debug("Fahrzeug existiert nicht mehr: " .. plate)
|
2025-08-06 18:32:55 +02:00
|
|
|
|
|
|
|
-- Versuche Fahrzeug wiederherzustellen
|
|
|
|
if lastKnownCoords[plate] then
|
|
|
|
Debug("Versuche Fahrzeug wiederherzustellen: " .. plate)
|
|
|
|
TriggerServerEvent('antidespawn:server:respawnVehicle', plate)
|
|
|
|
|
|
|
|
-- Entferne aus lokaler Tracking-Liste, wird nach Respawn wieder hinzugefügt
|
|
|
|
trackedVehicles[plate] = nil
|
|
|
|
end
|
2025-08-06 17:49:33 +02:00
|
|
|
end
|
|
|
|
end
|
2025-08-06 17:33:26 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
-- Lade Fahrzeuge beim Spawn
|
|
|
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
|
|
|
|
Debug("Spieler geladen, lade Fahrzeuge...")
|
|
|
|
Wait(10000) -- Warte bis alles geladen ist
|
|
|
|
TriggerServerEvent('antidespawn:server:loadVehicles')
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Spawne ein Fahrzeug
|
|
|
|
RegisterNetEvent('antidespawn:client:spawnVehicle', function(data)
|
|
|
|
Debug("Spawne Fahrzeug: " .. data.plate)
|
|
|
|
|
|
|
|
-- Prüfe ob Fahrzeug bereits existiert
|
|
|
|
local existingVehicle = GetVehicleByPlate(data.plate)
|
|
|
|
if existingVehicle then
|
|
|
|
Debug("Fahrzeug existiert bereits: " .. data.plate)
|
|
|
|
trackedVehicles[data.plate] = existingVehicle
|
2025-08-06 18:32:55 +02:00
|
|
|
lastKnownCoords[data.plate] = GetEntityCoords(existingVehicle)
|
2025-08-06 18:18:04 +02:00
|
|
|
PreventDespawn(existingVehicle)
|
|
|
|
return
|
2025-08-06 17:49:33 +02:00
|
|
|
end
|
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
-- Spawne Fahrzeug
|
|
|
|
local modelHash = data.model
|
|
|
|
|
|
|
|
RequestModel(modelHash)
|
|
|
|
local timeout = 0
|
|
|
|
while not HasModelLoaded(modelHash) and timeout < 100 do
|
|
|
|
Wait(100)
|
|
|
|
timeout = timeout + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
if HasModelLoaded(modelHash) then
|
2025-08-06 18:32:55 +02:00
|
|
|
-- Verwende CREATE_AUTOMOBILE für bessere Persistenz
|
|
|
|
local vehicle
|
|
|
|
|
|
|
|
if Citizen and Citizen.InvokeNative then
|
|
|
|
-- OneSync Methode
|
|
|
|
vehicle = Citizen.InvokeNative(0xAF35D0D2583051B0, modelHash, data.coords.x, data.coords.y, data.coords.z, data.heading, true, true)
|
|
|
|
else
|
|
|
|
-- Fallback
|
|
|
|
vehicle = CreateVehicle(modelHash, data.coords.x, data.coords.y, data.coords.z, data.heading, true, false)
|
|
|
|
end
|
2025-08-06 17:33:26 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
if DoesEntityExist(vehicle) then
|
2025-08-06 18:32:55 +02:00
|
|
|
-- Warte bis Fahrzeug vollständig geladen ist
|
|
|
|
Wait(500)
|
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
-- Setze Kennzeichen
|
|
|
|
SetVehicleNumberPlateText(vehicle, data.plate)
|
2025-08-06 18:07:18 +02:00
|
|
|
|
2025-08-06 18:18:04 +02:00
|
|
|
-- Setze Fuel
|
|
|
|
if GetResourceState(Config.FuelSystem) == 'started' then
|
|
|
|
exports[Config.FuelSystem]:SetFuel(vehicle, data.fuel or 100)
|
2025-08-06 18:07:18 +02:00
|
|
|
end
|
2025-08-06 18:18:04 +02:00
|
|
|
|
|
|
|
-- Verhindere Despawn
|
|
|
|
PreventDespawn(vehicle)
|
|
|
|
|
|
|
|
-- Füge zu getrackten Fahrzeugen hinzu
|
|
|
|
trackedVehicles[data.plate] = vehicle
|
2025-08-06 18:32:55 +02:00
|
|
|
lastKnownCoords[data.plate] = GetEntityCoords(vehicle)
|
|
|
|
|
|
|
|
-- Registriere beim Server
|
|
|
|
TriggerServerEvent('antidespawn:server:registerVehicle', data.plate, modelHash, GetEntityCoords(vehicle), GetEntityHeading(vehicle))
|
|
|
|
|
|
|
|
-- Registriere bei jg-advancedgarages als "draußen"
|
|
|
|
TriggerEvent("jg-advancedgarages:server:register-vehicle-outside", data.plate, NetworkGetNetworkIdFromEntity(vehicle))
|
2025-08-06 18:18:04 +02:00
|
|
|
|
|
|
|
Debug("Fahrzeug erfolgreich gespawnt: " .. data.plate)
|
|
|
|
else
|
|
|
|
Debug("Fehler beim Spawnen des Fahrzeugs: " .. data.plate)
|
2025-08-06 17:33:26 +02:00
|
|
|
end
|
2025-08-06 18:18:04 +02:00
|
|
|
|
|
|
|
SetModelAsNoLongerNeeded(modelHash)
|
|
|
|
else
|
|
|
|
Debug("Modell konnte nicht geladen werden: " .. data.plate)
|
2025-08-06 17:33:26 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
-- jg-advanced-garage Events
|
|
|
|
RegisterNetEvent('jg-advancedgarages:client:vehicle-stored', function(data)
|
2025-08-06 18:18:04 +02:00
|
|
|
if data and data.plate and trackedVehicles[data.plate] then
|
|
|
|
trackedVehicles[data.plate] = nil
|
2025-08-06 18:32:55 +02:00
|
|
|
lastKnownCoords[data.plate] = nil
|
2025-08-06 18:18:04 +02:00
|
|
|
TriggerServerEvent('antidespawn:server:removeVehicle', data.plate)
|
|
|
|
Debug("Fahrzeug in Garage gespeichert, entferne aus Tracking: " .. data.plate)
|
2025-08-06 17:33:26 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2025-08-06 18:32:55 +02:00
|
|
|
RegisterNetEvent('jg-advancedgarages:client:vehicle-spawned', function(data)
|
|
|
|
if data and data.plate then
|
|
|
|
Debug("Fahrzeug aus Garage gespawnt: " .. data.plate)
|
|
|
|
|
|
|
|
-- Warte kurz bis das Fahrzeug vollständig gespawnt ist
|
|
|
|
Wait(1000)
|
|
|
|
|
|
|
|
-- Finde das Fahrzeug
|
|
|
|
local vehicle = GetVehicleByPlate(data.plate)
|
|
|
|
if vehicle then
|
|
|
|
-- Füge zu getrackten Fahrzeugen hinzu
|
|
|
|
trackedVehicles[data.plate] = vehicle
|
|
|
|
lastKnownCoords[data.plate] = GetEntityCoords(vehicle)
|
|
|
|
|
|
|
|
-- Verhindere Despawn
|
|
|
|
PreventDespawn(vehicle)
|
|
|
|
|
|
|
|
Debug("Fahrzeug aus Garage zum Tracking hinzugefügt: " .. data.plate)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Öffnen der Garage - entferne Tracking für Fahrzeuge in der Nähe
|
|
|
|
RegisterNetEvent('jg-advancedgarages:client:open-garage', function()
|
|
|
|
Debug("Garage geöffnet, prüfe Fahrzeuge in der Nähe")
|
|
|
|
|
|
|
|
local playerPos = GetEntityCoords(PlayerPedId())
|
|
|
|
|
|
|
|
-- Prüfe alle getrackten Fahrzeuge
|
|
|
|
for plate, vehicle in pairs(trackedVehicles) do
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
|
|
local vehiclePos = GetEntityCoords(vehicle)
|
|
|
|
local distance = #(playerPos - vehiclePos)
|
|
|
|
|
|
|
|
-- Wenn Fahrzeug in der Nähe ist (50m), entferne aus Tracking
|
|
|
|
-- da es wahrscheinlich in die Garage gestellt wird
|
|
|
|
if distance < 50.0 then
|
|
|
|
Debug("Fahrzeug in Garagennähe, entferne temporär aus Tracking: " .. plate)
|
|
|
|
-- Nicht komplett entfernen, nur temporär ignorieren
|
|
|
|
-- Das Event jg-advancedgarages:client:vehicle-stored wird ausgelöst wenn es eingelagert wird
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- Debug Command
|
|
|
|
RegisterCommand('fixvehicle', function()
|
|
|
|
local playerPed = PlayerPedId()
|
|
|
|
local vehicle = GetVehiclePedIsIn(playerPed, false)
|
|
|
|
|
|
|
|
if vehicle ~= 0 then
|
|
|
|
local plate = QBCore.Functions.GetPlate(vehicle)
|
|
|
|
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)
|
|
|
|
|
|
|
|
TriggerServerEvent('antidespawn:server:registerVehicle', plate, vehicleModel, vehicleCoords, vehicleHeading)
|
|
|
|
|
|
|
|
Debug("Anti-Despawn für Fahrzeug aktiviert: " .. plate)
|
|
|
|
else
|
|
|
|
Debug("Du musst in einem Fahrzeug sitzen!")
|
|
|
|
end
|
|
|
|
end, false)
|
|
|
|
|
2025-08-06 17:33:26 +02:00
|
|
|
-- Cleanup beim Disconnect
|
|
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
|
|
if resourceName == GetCurrentResourceName() then
|
2025-08-06 18:18:04 +02:00
|
|
|
trackedVehicles = {}
|
2025-08-06 18:32:55 +02:00
|
|
|
lastKnownCoords = {}
|
2025-08-06 17:33:26 +02:00
|
|
|
end
|
|
|
|
end)
|