diff --git a/resources/[carscripts]/nordi_rental/client.lua b/resources/[carscripts]/nordi_rental/client.lua index f5e8246cd..11a7de231 100644 --- a/resources/[carscripts]/nordi_rental/client.lua +++ b/resources/[carscripts]/nordi_rental/client.lua @@ -1,44 +1,64 @@ local QBCore = exports['qb-core']:GetCoreObject() local spawnedNPCs = {} -local currentRental = nil +local activeRentalVehicles = {} -- NPCs spawnen CreateThread(function() + -- Warte kurz, bis die Welt geladen ist + Wait(2000) + for i = 1, #Config.RentalLocations do local location = Config.RentalLocations[i] + local modelHash = GetHashKey(location.npc.model) - RequestModel(location.npc.model) - while not HasModelLoaded(location.npc.model) do - Wait(1) + -- Modell laden + RequestModel(modelHash) + local timeout = 0 + while not HasModelLoaded(modelHash) and timeout < 30 do + Wait(100) + timeout = timeout + 1 end - - local npc = CreatePed(4, location.npc.model, location.npc.coords.x, location.npc.coords.y, location.npc.coords.z - 1.0, location.npc.coords.w, false, true) - FreezeEntityPosition(npc, true) - SetEntityInvincible(npc, true) - SetBlockingOfNonTemporaryEvents(npc, true) - spawnedNPCs[location.id] = npc - - -- QB-Target für NPC - exports['qb-target']:AddTargetEntity(npc, { - options = { - { - type = "client", - event = "vehiclerental:client:openMenu", - icon = "fas fa-car", - label = "Fahrzeug mieten", - locationId = location.id + if HasModelLoaded(modelHash) then + -- NPC erstellen + local coords = location.npc.coords + local npc = CreatePed(4, modelHash, coords.x, coords.y, coords.z - 1.0, coords.w, false, false) + + -- NPC-Eigenschaften setzen + FreezeEntityPosition(npc, true) + SetEntityInvincible(npc, true) + SetBlockingOfNonTemporaryEvents(npc, true) + SetPedDefaultComponentVariation(npc) + + -- NPC speichern + spawnedNPCs[location.id] = npc + + -- Debug-Info + print("NPC spawned at location: " .. location.name) + + -- QB-Target für NPC + exports['qb-target']:AddTargetEntity(npc, { + options = { + { + type = "client", + event = "vehiclerental:client:openMenu", + icon = "fas fa-car", + label = "Fahrzeug mieten", + locationId = location.id + }, + { + type = "client", + event = "vehiclerental:client:returnVehicle", + icon = "fas fa-car-side", + label = "Fahrzeug zurückgeben", + locationId = location.id + } }, - { - type = "client", - event = "vehiclerental:client:returnVehicle", - icon = "fas fa-car-side", - label = "Fahrzeug zurückgeben", - locationId = location.id - } - }, - distance = 2.0 - }) + distance = 2.0 + }) + else + print("Failed to load NPC model for location: " .. location.name) + end end end) @@ -130,12 +150,18 @@ function spawnRentalVehicle(model, spawnPoint, plate) TriggerEvent("vehiclekeys:client:SetOwner", plate) SetModelAsNoLongerNeeded(model) - -- Registriere das Fahrzeug im Parking-System - TriggerEvent('vehiclerental:client:vehicleRented', vehicle, plate) + -- Speichere das Fahrzeug lokal + activeRentalVehicles[plate] = vehicle + + -- Speichere die initiale Position + local pos = GetEntityCoords(vehicle) + local rot = GetEntityRotation(vehicle) + TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot) + + print("Neues Mietfahrzeug erstellt: " .. plate) end - --- Fahrzeug zurückgeben (KORRIGIERT - ohne im Auto zu sitzen) +-- Fahrzeug zurückgeben RegisterNetEvent('vehiclerental:client:returnVehicle', function(data) -- Hole alle aktiven Mietverhältnisse des Spielers QBCore.Functions.TriggerCallback('vehiclerental:server:getPlayerRentals', function(rentals) @@ -171,17 +197,33 @@ end) -- Spezifisches Fahrzeug zurückgeben function returnSpecificVehicle(plate, locationId) + -- Finde die Location-Daten + local location = nil + for i = 1, #Config.RentalLocations do + if Config.RentalLocations[i].id == locationId then + location = Config.RentalLocations[i] + break + end + end + + if not location then + QBCore.Functions.Notify('Fehler beim Finden des Rückgabeorts!', 'error') + return + end + + -- Definiere den Rückgabeort + local returnPoint = vector3(location.returnPoint.x, location.returnPoint.y, location.returnPoint.z) + -- Finde das Fahrzeug in der Nähe - local playerPos = GetEntityCoords(PlayerPedId()) local vehicle = nil - local closestDistance = 50.0 -- Maximale Entfernung + local closestDistance = 50.0 -- Maximale Suchentfernung -- Suche nach dem Fahrzeug mit dem Kennzeichen for veh in EnumerateVehicles() do local vehPlate = GetVehicleNumberPlateText(veh) if string.gsub(vehPlate, "%s+", "") == string.gsub(plate, "%s+", "") then local vehPos = GetEntityCoords(veh) - local distance = #(playerPos - vehPos) + local distance = #(returnPoint - vehPos) if distance < closestDistance then vehicle = veh @@ -191,38 +233,137 @@ function returnSpecificVehicle(plate, locationId) end if not vehicle then - QBCore.Functions.Notify('Fahrzeug nicht in der Nähe gefunden! Bringe es zum Mietort zurück.', 'error') + QBCore.Functions.Notify('Fahrzeug nicht in der Nähe des Rückgabeorts gefunden!', 'error') return end - -- Prüfe ob das Fahrzeug am richtigen Ort ist - local location = nil - for i = 1, #Config.RentalLocations do - if Config.RentalLocations[i].id == locationId then - location = Config.RentalLocations[i] - break - end - end - - if location then - local returnPos = vector3(location.returnPoint.x, location.returnPoint.y, location.returnPoint.z) - local vehPos = GetEntityCoords(vehicle) - local distance = #(returnPos - vehPos) - - if distance > 10.0 then - QBCore.Functions.Notify('Bringe das Fahrzeug näher zum Rückgabeort!', 'error') - return - end + -- Prüfe ob das Fahrzeug nahe genug am Rückgabeort ist + local vehPos = GetEntityCoords(vehicle) + local distance = #(returnPoint - vehPos) + + if distance > Config.MaxReturnDistance then + QBCore.Functions.Notify('Das Fahrzeug muss näher am Rückgabeort sein! (Max. ' .. Config.MaxReturnDistance .. 'm)', 'error') + return end -- Fahrzeug zurückgeben QBCore.Functions.TriggerCallback('vehiclerental:server:returnVehicle', function(success) if success then DeleteVehicle(vehicle) + activeRentalVehicles[plate] = nil + QBCore.Functions.Notify('Fahrzeug erfolgreich zurückgegeben!', 'success') end end, plate) end +-- Lade alle aktiven Mietfahrzeuge +RegisterNetEvent('vehiclerental:client:loadRentals') +AddEventHandler('vehiclerental:client:loadRentals', function(rentals) + -- Lösche alle vorhandenen Mietfahrzeuge + for plate, vehicle in pairs(activeRentalVehicles) do + if DoesEntityExist(vehicle) then + DeleteVehicle(vehicle) + end + end + + activeRentalVehicles = {} + + -- Spawne alle aktiven Mietfahrzeuge + for _, rental in ipairs(rentals) do + -- Prüfe, ob das Fahrzeug dem aktuellen Spieler gehört + local playerCitizenId = QBCore.Functions.GetPlayerData().citizenid + + if rental.citizenid == playerCitizenId then + -- Spawne das Fahrzeug nur, wenn es eine Position hat + if rental.posX ~= 0 or rental.posY ~= 0 or rental.posZ ~= 0 then + Citizen.CreateThread(function() + local model = rental.vehicle_model + local plate = rental.vehicle_plate + + -- Lade das Modell + RequestModel(model) + while not HasModelLoaded(model) do + Citizen.Wait(10) + end + + -- Spawne das Fahrzeug + local vehicle = CreateVehicle(model, + rental.posX, rental.posY, rental.posZ, + rental.rotZ or 0.0, true, false) + + -- Setze Eigenschaften + SetVehicleNumberPlateText(vehicle, plate) + SetEntityAsMissionEntity(vehicle, true, true) + SetVehicleDoorsLocked(vehicle, 2) -- Abgeschlossen + + -- Setze Rotation + SetEntityRotation(vehicle, + rental.rotX or 0.0, + rental.rotY or 0.0, + rental.rotZ or 0.0, + 2, true) + + -- Gib dem Spieler die Schlüssel + TriggerEvent("vehiclekeys:client:SetOwner", plate) + + -- Speichere das Fahrzeug lokal + activeRentalVehicles[plate] = vehicle + + print("Mietfahrzeug geladen: " .. plate) + end) + end + end + end +end) + +-- Fahrzeug zurückgegeben +RegisterNetEvent('vehiclerental:client:vehicleReturned') +AddEventHandler('vehiclerental:client:vehicleReturned', function(plate) + -- Lösche das Fahrzeug, wenn es existiert + if activeRentalVehicles[plate] and DoesEntityExist(activeRentalVehicles[plate]) then + DeleteVehicle(activeRentalVehicles[plate]) + activeRentalVehicles[plate] = nil + print("Mietfahrzeug gelöscht: " .. plate) + end +end) + +-- Regelmäßiges Update der Fahrzeugpositionen +Citizen.CreateThread(function() + while true do + Citizen.Wait(30000) -- Alle 30 Sekunden + + -- Update alle aktiven Mietfahrzeuge + for plate, vehicle in pairs(activeRentalVehicles) do + if DoesEntityExist(vehicle) then + local pos = GetEntityCoords(vehicle) + local rot = GetEntityRotation(vehicle) + TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot) + end + end + end +end) + +-- Wenn der Spieler das Fahrzeug verlässt, aktualisiere die Position +Citizen.CreateThread(function() + while true do + Citizen.Wait(500) + + local playerPed = PlayerPedId() + local vehicle = GetVehiclePedIsIn(playerPed, true) + + if vehicle ~= 0 then + local plate = GetVehicleNumberPlateText(vehicle) + + -- Wenn es ein Mietfahrzeug ist und der Spieler es gerade verlassen hat + if activeRentalVehicles[plate] and not IsPedInVehicle(playerPed, vehicle, false) then + local pos = GetEntityCoords(vehicle) + local rot = GetEntityRotation(vehicle) + TriggerServerEvent('vehiclerental:server:updatePosition', plate, pos, rot) + end + end + end +end) + -- Fahrzeug-Enumerator function EnumerateVehicles() return coroutine.wrap(function() @@ -236,7 +377,7 @@ function EnumerateVehicles() end) end --- Kennzeichen generieren (GEÄNDERT - RENTAL + 3 zufällige Zeichen) +-- Kennzeichen generieren (RENT + 4 zufällige Zeichen) function GeneratePlate() local plate = "RENT" for i = 1, 4 do @@ -258,24 +399,10 @@ AddEventHandler('onResourceStop', function(resourceName) DeleteEntity(npc) end end -end) --- Integration mit mh_Parking -RegisterNetEvent('vehiclerental:client:vehicleRented') -AddEventHandler('vehiclerental:client:vehicleRented', function(vehicle, plate) - -- Warte kurz, bis das Fahrzeug vollständig gespawnt ist - Citizen.Wait(1000) - -- Aktualisiere das Fahrzeug im Parking-System - if DoesEntityExist(vehicle) then - TriggerEvent("mh_Parking:updateVehicle", vehicle) - print("Mietfahrzeug im Parking-System registriert: " .. plate) + for plate, vehicle in pairs(activeRentalVehicles) do + if DoesEntityExist(vehicle) then + DeleteVehicle(vehicle) + end end end) - --- Wenn ein Mietfahrzeug zurückgegeben wird -RegisterNetEvent('vehiclerental:client:vehicleReturned') -AddEventHandler('vehiclerental:client:vehicleReturned', function(plate) - -- Entferne das Fahrzeug aus dem Parking-System - TriggerServerEvent("mh_Parking:deleteVehicle", plate, true) - print("Mietfahrzeug aus dem Parking-System entfernt: " .. plate) -end) diff --git a/resources/[carscripts]/nordi_rental/config.lua b/resources/[carscripts]/nordi_rental/config.lua index 8f4ebfaf1..41c31a702 100644 --- a/resources/[carscripts]/nordi_rental/config.lua +++ b/resources/[carscripts]/nordi_rental/config.lua @@ -3,7 +3,8 @@ Config = {} Config.MaxRentalTime = 24 -- Maximale Mietdauer in Stunden Config.PenaltyPerHour = 100 -- Strafe pro Stunde Verspätung Config.UseOkokBanking = false -- true für okokBanking, false für Bargeld -Config.MaxReturnDistance = 10.0 -- Maximale Entfernung zum Rückgabepunkt in Metern +Config.MaxReturnDistance = 20.0 -- Maximale Entfernung zum Rückgabepunkt in Metern +Config.MaxOverdueHours = 24 -- Nach wie vielen Stunden überfällige Fahrzeuge automatisch zurückgegeben werden Config.RentalLocations = { { diff --git a/resources/[carscripts]/nordi_rental/server.lua b/resources/[carscripts]/nordi_rental/server.lua index 89e0ac5ca..5549dbea7 100644 --- a/resources/[carscripts]/nordi_rental/server.lua +++ b/resources/[carscripts]/nordi_rental/server.lua @@ -1,23 +1,46 @@ -local QBCore = exports['qb-core']:GetCoreObject() --- Datenbank Tabelle erstellen -MySQL.ready(function() - MySQL.Async.execute([[ - CREATE TABLE IF NOT EXISTS vehicle_rentals ( - id INT AUTO_INCREMENT PRIMARY KEY, - citizenid VARCHAR(50) NOT NULL, - vehicle_model VARCHAR(50) NOT NULL, - vehicle_plate VARCHAR(10) NOT NULL, - rental_location VARCHAR(50) NOT NULL, - start_time BIGINT NOT NULL, - end_time BIGINT NOT NULL, - price_per_hour INT NOT NULL, - returned BOOLEAN DEFAULT FALSE, - penalty_paid BOOLEAN DEFAULT FALSE, - INDEX(citizenid), - INDEX(vehicle_plate) - ) - ]]) +local QBCore = exports['qb-core']:GetCoreObject() +local activeRentals = {} + +-- Lade alle aktiven Mietfahrzeuge beim Serverstart +Citizen.CreateThread(function() + Wait(5000) -- Warte, bis die Datenbank bereit ist + + MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE returned = FALSE', {}, function(results) + if results and #results > 0 then + print("Lade " .. #results .. " aktive Mietfahrzeuge...") + + for _, rental in ipairs(results) do + activeRentals[rental.vehicle_plate] = rental + end + + -- Benachrichtige alle Clients, dass sie die Fahrzeuge spawnen sollen + TriggerClientEvent('vehiclerental:client:loadRentals', -1, results) + end + end) +end) + +-- Aktualisiere die Position eines Mietfahrzeugs +RegisterServerEvent('vehiclerental:server:updatePosition') +AddEventHandler('vehiclerental:server:updatePosition', function(plate, position, rotation) + if not plate or not position then return end + + -- Aktualisiere in der Datenbank + MySQL.Async.execute('UPDATE vehicle_rentals SET posX = ?, posY = ?, posZ = ?, rotX = ?, rotY = ?, rotZ = ?, lastUpdated = CURRENT_TIMESTAMP WHERE vehicle_plate = ? AND returned = FALSE', { + position.x, position.y, position.z, + rotation.x, rotation.y, rotation.z, + plate + }) + + -- Aktualisiere im Cache + if activeRentals[plate] then + activeRentals[plate].posX = position.x + activeRentals[plate].posY = position.y + activeRentals[plate].posZ = position.z + activeRentals[plate].rotX = rotation.x + activeRentals[plate].rotY = rotation.y + activeRentals[plate].rotZ = rotation.z + end end) -- Fahrzeug mieten @@ -57,10 +80,23 @@ QBCore.Functions.CreateCallback('vehiclerental:server:rentVehicle', function(sou currentTime, endTime, data.pricePerHour - }) - - TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich gemietet für $' .. totalCost, 'success') - cb(true) + }, function(rentalId) + -- Füge das Fahrzeug zum aktiven Cache hinzu + activeRentals[data.plate] = { + id = rentalId, + citizenid = Player.PlayerData.citizenid, + vehicle_model = data.vehicleModel, + vehicle_plate = data.plate, + rental_location = data.locationId, + start_time = currentTime, + end_time = endTime, + price_per_hour = data.pricePerHour, + returned = false + } + + TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich gemietet für $' .. totalCost, 'success') + cb(true) + end) end) -- Fahrzeug zurückgeben @@ -115,39 +151,48 @@ QBCore.Functions.CreateCallback('vehiclerental:server:returnVehicle', function(s penalty > 0, rental.id }) + + -- Aus dem aktiven Cache entfernen + activeRentals[plate] = nil + + -- Benachrichtige alle Clients, dass das Fahrzeug zurückgegeben wurde + TriggerClientEvent('vehiclerental:client:vehicleReturned', -1, plate) TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich zurückgegeben!', 'success') cb(true) end) end) --- Mietzeit abfragen -QBCore.Functions.CreateCallback('vehiclerental:server:getRentalInfo', function(source, cb) +-- Spieler Mietverhältnisse abrufen +QBCore.Functions.CreateCallback('vehiclerental:server:getPlayerRentals', function(source, cb) local Player = QBCore.Functions.GetPlayer(source) if not Player then return cb(nil) end MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', { Player.PlayerData.citizenid }, function(result) - if not result[1] then + if not result or #result == 0 then return cb(nil) end - local rentals = {} + local currentTime = os.time() for i = 1, #result do - local rental = result[i] - local currentTime = os.time() - local timeLeft = rental.end_time - currentTime + -- Berechne verbleibende Zeit auf dem Server + result[i].timeLeft = result[i].end_time - currentTime + result[i].isOverdue = result[i].timeLeft < 0 - table.insert(rentals, { - vehicleModel = rental.vehicle_model, - plate = rental.vehicle_plate, - timeLeft = timeLeft, - isOverdue = timeLeft < 0 - }) + -- Formatiere die Zeit für die Anzeige + if result[i].isOverdue then + local hoursOverdue = math.ceil(math.abs(result[i].timeLeft) / 3600) + result[i].timeText = "(Überfällig um " .. hoursOverdue .. " Stunden)" + else + local hoursLeft = math.floor(result[i].timeLeft / 3600) + local minutesLeft = math.floor((result[i].timeLeft % 3600) / 60) + result[i].timeText = "(" .. hoursLeft .. "h " .. minutesLeft .. "m verbleibend)" + end end - cb(rentals) + cb(result) end) end) @@ -184,115 +229,49 @@ QBCore.Commands.Add('mietzeit', 'Zeige deine aktuelle Mietzeit an', {}, false, f end) end) --- Spieler Mietverhältnisse abrufen (VERBESSERT) -QBCore.Functions.CreateCallback('vehiclerental:server:getPlayerRentals', function(source, cb) - local Player = QBCore.Functions.GetPlayer(source) - if not Player then return cb(nil) end +-- Regelmäßige Überprüfung auf überfällige Fahrzeuge +Citizen.CreateThread(function() + while true do + Citizen.Wait(60000) -- Überprüfe jede Minute + + local currentTime = os.time() + local overdueVehicles = {} + + -- Überprüfe alle aktiven Mietfahrzeuge + for plate, rental in pairs(activeRentals) do + if currentTime > rental.end_time + (Config.MaxOverdueHours * 3600) then + -- Fahrzeug ist zu lange überfällig, markiere es als zurückgegeben + MySQL.Async.execute('UPDATE vehicle_rentals SET returned = TRUE, penalty_paid = TRUE WHERE vehicle_plate = ? AND returned = FALSE', { + plate + }) + + -- Füge es zur Liste der zu entfernenden Fahrzeuge hinzu + table.insert(overdueVehicles, plate) + end + end + + -- Entferne überfällige Fahrzeuge aus dem Cache + for _, plate in ipairs(overdueVehicles) do + activeRentals[plate] = nil + -- Benachrichtige alle Clients, dass das Fahrzeug entfernt werden soll + TriggerClientEvent('vehiclerental:client:vehicleReturned', -1, plate) + end + end +end) +-- Spieler verbindet sich +RegisterNetEvent('QBCore:Server:PlayerLoaded') +AddEventHandler('QBCore:Server:PlayerLoaded', function() + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + + -- Sende dem Spieler seine aktiven Mietfahrzeuge MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', { Player.PlayerData.citizenid - }, function(result) - if not result or #result == 0 then - return cb(nil) - end - - local currentTime = os.time() - for i = 1, #result do - -- Berechne verbleibende Zeit auf dem Server - result[i].timeLeft = result[i].end_time - currentTime - result[i].isOverdue = result[i].timeLeft < 0 - - -- Formatiere die Zeit für die Anzeige - if result[i].isOverdue then - local hoursOverdue = math.ceil(math.abs(result[i].timeLeft) / 3600) - result[i].timeText = "(Überfällig um " .. hoursOverdue .. " Stunden)" - else - local hoursLeft = math.floor(result[i].timeLeft / 3600) - local minutesLeft = math.floor((result[i].timeLeft % 3600) / 60) - result[i].timeText = "(" .. hoursLeft .. "h " .. minutesLeft .. "m verbleibend)" - end - end - - cb(result) - end) -end) --- Funktion zum Entfernen von Mietfahrzeugen aus dem Parking-System -function RemoveRentalFromParking(plate) - -- Entferne das Fahrzeug aus dem mh_Parking System - TriggerEvent("mh_Parking:removeVehicle", plate) - - -- Lösche es auch aus der Datenbank des Parking-Systems - MySQL.Async.execute("DELETE FROM vehicle_parking WHERE plate = @plate", { - ["@plate"] = plate - }) -end - --- Wenn ein Fahrzeug zurückgegeben wird, entferne es aus dem Parking-System -RegisterNetEvent('vehiclerental:server:vehicleReturned') -AddEventHandler('vehiclerental:server:vehicleReturned', function(plate) - RemoveRentalFromParking(plate) -end) - --- Modifiziere die bestehende returnVehicle Callback-Funktion -local originalReturnCallback = QBCore.Functions.CreateCallback('vehiclerental:server:returnVehicle') -QBCore.Functions.CreateCallback('vehiclerental:server:returnVehicle', function(source, cb, plate) - -- Wenn das Fahrzeug erfolgreich zurückgegeben wurde - MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE vehicle_plate = ? AND returned = FALSE', { - plate - }, function(result) - if result[1] then - -- Führe die normale Rückgabe durch - local Player = QBCore.Functions.GetPlayer(source) - local rental = result[1] - local currentTime = os.time() - local penalty = 0 - - -- Strafe berechnen wenn verspätet - if currentTime > rental.end_time then - local hoursLate = math.ceil((currentTime - rental.end_time) / 3600) - penalty = hoursLate * Config.PenaltyPerHour - end - - -- Strafe abziehen falls vorhanden - if penalty > 0 then - local penaltyPaid = false - if Config.UseOkokBanking then - local bankMoney = exports['okokBanking']:GetAccount(Player.PlayerData.citizenid) - if bankMoney and bankMoney >= penalty then - exports['okokBanking']:RemoveMoney(Player.PlayerData.citizenid, penalty) - penaltyPaid = true - end - else - if Player.Functions.RemoveMoney('cash', penalty) then - penaltyPaid = true - end - end - - if penaltyPaid then - TriggerClientEvent('QBCore:Notify', source, 'Verspätungsstrafe von $' .. penalty .. ' wurde abgezogen!', 'error') - else - TriggerClientEvent('QBCore:Notify', source, 'Nicht genug Geld für Verspätungsstrafe!', 'error') - return cb(false) - end - end - - -- Mietverhältnis als zurückgegeben markieren - MySQL.Async.execute('UPDATE vehicle_rentals SET returned = TRUE, penalty_paid = ? WHERE id = ?', { - penalty > 0, - rental.id - }) - - -- Entferne das Fahrzeug aus dem Parking-System - RemoveRentalFromParking(plate) - - -- Benachrichtige alle Clients - TriggerClientEvent('vehiclerental:client:vehicleReturned', -1, plate) - - TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich zurückgegeben!', 'success') - cb(true) - else - TriggerClientEvent('QBCore:Notify', source, 'Kein aktives Mietverhältnis für dieses Fahrzeug gefunden!', 'error') - cb(false) + }, function(results) + if results and #results > 0 then + TriggerClientEvent('vehiclerental:client:loadRentals', src, results) end end) end)