local QBCore = exports['qb-core']:GetCoreObject() local activeRentals = {} -- Füge einen Schlüssel für ein Mietfahrzeug hinzu function AddRentalKey(citizenid, plate, model) local player = QBCore.Functions.GetPlayerByCitizenId(citizenid) if player then -- Verwende MrNewbVehicleKeys Export exports.MrNewbVehicleKeys:GiveKeysByPlate(player.PlayerData.source, plate) print("[VehicleRental] Schlüssel für Mietfahrzeug hinzugefügt: " .. plate .. " für Spieler " .. citizenid) else -- Fallback für offline Spieler - Schlüssel werden beim nächsten Login hinzugefügt print("[VehicleRental] Spieler offline, Schlüssel wird beim nächsten Login hinzugefügt: " .. plate) end end -- Entferne einen Schlüssel für ein Mietfahrzeug function RemoveRentalKey(citizenid, plate, model) local player = QBCore.Functions.GetPlayerByCitizenId(citizenid) if player then -- Verwende MrNewbVehicleKeys Export exports.MrNewbVehicleKeys:RemoveKeysByPlate(player.PlayerData.source, plate) print("[VehicleRental] Schlüssel für Mietfahrzeug entfernt: " .. plate .. " für Spieler " .. citizenid) else print("[VehicleRental] Kein Spieler gefunden zum Entfernen des Schlüssels: " .. plate .. " für Spieler " .. citizenid) end end -- Prüfe, ob ein Spieler Schlüssel für ein Fahrzeug hat function HasRentalKey(source, plate) return exports.MrNewbVehicleKeys:HasKeysByPlate(source, plate) end -- Setze den Sperrstatus eines Fahrzeugs function SetRentalVehicleLock(netId, lockStatus) exports.MrNewbVehicleKeys:SetVehicleLock(netId, lockStatus) end -- 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("[VehicleRental] Lade " .. #results .. " aktive Mietfahrzeuge...") for _, rental in ipairs(results) do activeRentals[rental.vehicle_plate] = rental -- Stelle sicher, dass für jedes Mietfahrzeug ein Schlüssel existiert local player = QBCore.Functions.GetPlayerByCitizenId(rental.citizenid) if player then -- Verwende MrNewbVehicleKeys Export exports.MrNewbVehicleKeys:GiveKeysByPlate(player.PlayerData.source, rental.vehicle_plate) print("[VehicleRental] Schlüssel für Mietfahrzeug wiederhergestellt: " .. rental.vehicle_plate) end 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 QBCore.Functions.CreateCallback('vehiclerental:server:rentVehicle', function(source, cb, data) local Player = QBCore.Functions.GetPlayer(source) if not Player then return cb(false) end local totalCost = data.pricePerHour * data.hours local currentTime = os.time() local endTime = currentTime + (data.hours * 3600) -- Zahlung prüfen und abziehen local paymentSuccess = false if Config.UseOkokBanking then local bankMoney = exports['okokBanking']:GetAccount(Player.PlayerData.citizenid) if bankMoney and bankMoney >= totalCost then exports['okokBanking']:RemoveMoney(Player.PlayerData.citizenid, totalCost) paymentSuccess = true end else if Player.Functions.RemoveMoney('cash', totalCost) then paymentSuccess = true end end if not paymentSuccess then TriggerClientEvent('QBCore:Notify', source, 'Nicht genug Geld!', 'error') return cb(false) end -- Fahrzeug in Datenbank eintragen MySQL.Async.insert('INSERT INTO vehicle_rentals (citizenid, vehicle_model, vehicle_plate, rental_location, start_time, end_time, price_per_hour) VALUES (?, ?, ?, ?, ?, ?, ?)', { Player.PlayerData.citizenid, data.vehicleModel, data.plate, data.locationId, currentTime, endTime, data.pricePerHour }, 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 } -- Füge einen Schlüssel mit MrNewbVehicleKeys hinzu exports.MrNewbVehicleKeys:GiveKeysByPlate(source, data.plate) TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich gemietet für $' .. totalCost, 'success') cb(true) end) end) -- Fahrzeug zurückgeben QBCore.Functions.CreateCallback('vehiclerental:server:returnVehicle', function(source, cb, plate) local Player = QBCore.Functions.GetPlayer(source) if not Player then return cb(false) end MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND vehicle_plate = ? AND returned = FALSE', { Player.PlayerData.citizenid, plate }, function(result) if not result[1] then TriggerClientEvent('QBCore:Notify', source, 'Kein aktives Mietverhältnis für dieses Fahrzeug gefunden!', 'error') return cb(false) end 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 }) -- Aus dem aktiven Cache entfernen activeRentals[plate] = nil -- Entferne den Schlüssel mit MrNewbVehicleKeys exports.MrNewbVehicleKeys:RemoveKeysByPlate(source, plate) -- 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) -- 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 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) -- Befehl für Mietzeit QBCore.Commands.Add('mietzeit', 'Zeige deine aktuelle Mietzeit an', {}, false, function(source, args) local Player = QBCore.Functions.GetPlayer(source) if not Player then return end MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', { Player.PlayerData.citizenid }, function(result) if not result or #result == 0 then TriggerClientEvent('QBCore:Notify', source, 'Du hast keine aktiven Mietverhältnisse!', 'error') return end for i = 1, #result do local rental = result[i] local currentTime = os.time() local timeLeft = rental.end_time - currentTime local timeText = "" if timeLeft < 0 then local hoursOverdue = math.ceil(math.abs(timeLeft) / 3600) timeText = "Überfällig um " .. hoursOverdue .. " Stunden" TriggerClientEvent('QBCore:Notify', source, rental.vehicle_model .. " (" .. rental.vehicle_plate .. "): " .. timeText, 'error') else local hoursLeft = math.floor(timeLeft / 3600) local minutesLeft = math.floor((timeLeft % 3600) / 60) timeText = hoursLeft .. "h " .. minutesLeft .. "m verbleibend" TriggerClientEvent('QBCore:Notify', source, rental.vehicle_model .. " (" .. rental.vehicle_plate .. "): " .. timeText, 'primary') end end end) 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 }) -- Entferne den Schlüssel für das Mietfahrzeug local player = QBCore.Functions.GetPlayerByCitizenId(rental.citizenid) if player then exports.MrNewbVehicleKeys:RemoveKeysByPlate(player.PlayerData.source, plate) end -- 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(results) if results and #results > 0 then -- Stelle sicher, dass für jedes Mietfahrzeug ein Schlüssel existiert for _, rental in ipairs(results) do -- Direkt MrNewbVehicleKeys Export verwenden exports.MrNewbVehicleKeys:GiveKeysByPlate(src, rental.vehicle_plate) print("[VehicleRental] Schlüssel für Mietfahrzeug wiederhergestellt: " .. rental.vehicle_plate) end TriggerClientEvent('vehiclerental:client:loadRentals', src, results) end end) end) -- Exportiere Funktionen für andere Ressourcen exports('GetActiveRentals', function(citizenid) local playerRentals = {} for plate, rental in pairs(activeRentals) do if rental.citizenid == citizenid then table.insert(playerRentals, rental) end end return playerRentals end) -- Event für andere Ressourcen RegisterNetEvent('vehiclerental:server:checkRentalKeys') AddEventHandler('vehiclerental:server:checkRentalKeys', function() local src = source local Player = QBCore.Functions.GetPlayer(src) if not Player then return end -- Hole alle aktiven Mietverhältnisse des Spielers MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', { Player.PlayerData.citizenid }, function(rentals) if rentals and #rentals > 0 then for _, rental in ipairs(rentals) do -- Direkt MrNewbVehicleKeys Export verwenden exports.MrNewbVehicleKeys:GiveKeysByPlate(src, rental.vehicle_plate) print("[VehicleRental] Schlüssel für Mietfahrzeug wiederhergestellt: " .. rental.vehicle_plate) end end end) end) -- Funktion zum Löschen aller Mietfahrzeuge eines Spielers (z.B. bei Charakter-Löschung) exports('DeleteAllPlayerRentals', function(citizenid) if not citizenid then return false end -- Hole alle aktiven Mietverhältnisse des Spielers MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', { citizenid }, function(rentals) if rentals and #rentals > 0 then local player = QBCore.Functions.GetPlayerByCitizenId(citizenid) for _, rental in ipairs(rentals) do -- Markiere als zurückgegeben MySQL.Async.execute('UPDATE vehicle_rentals SET returned = TRUE WHERE id = ?', { rental.id }) -- Entferne den Schlüssel mit MrNewbVehicleKeys wenn Spieler online ist if player then exports.MrNewbVehicleKeys:RemoveKeysByPlate(player.PlayerData.source, rental.vehicle_plate) end -- Entferne aus dem Cache activeRentals[rental.vehicle_plate] = nil -- Benachrichtige alle Clients TriggerClientEvent('vehiclerental:client:vehicleReturned', -1, rental.vehicle_plate) end print("[VehicleRental] Deleted all rental vehicles for player: " .. citizenid) return true end return false end) end)