410 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local QBCore = exports['qb-core']:GetCoreObject()
 | 
						|
local activeRentals = {}
 | 
						|
 | 
						|
-- Füge einen Schlüssel für ein Mietfahrzeug hinzu
 | 
						|
function AddRentalKey(citizenid, plate, model)
 | 
						|
    MySQL.Async.execute('INSERT INTO vehicle_keys (owner, plate, count) VALUES (?, ?, ?)', {
 | 
						|
        citizenid,
 | 
						|
        plate,
 | 
						|
        1
 | 
						|
    }, function(rowsChanged)
 | 
						|
        if rowsChanged > 0 then
 | 
						|
            print("[VehicleRental] Schlüssel für Mietfahrzeug hinzugefügt: " .. plate .. " für Spieler " .. citizenid)
 | 
						|
        end
 | 
						|
    end)
 | 
						|
end
 | 
						|
 | 
						|
-- Entferne einen Schlüssel für ein Mietfahrzeug (gleiche Struktur wie AddRentalKey)
 | 
						|
function RemoveRentalKey(citizenid, plate, model)
 | 
						|
    MySQL.Async.execute('DELETE FROM vehicle_keys WHERE owner = ? AND plate = ?', {
 | 
						|
        citizenid,
 | 
						|
        plate
 | 
						|
    }, function(rowsChanged)
 | 
						|
        if rowsChanged > 0 then
 | 
						|
            print("[VehicleRental] Schlüssel für Mietfahrzeug entfernt: " .. plate .. " für Spieler " .. citizenid)
 | 
						|
        else
 | 
						|
            print("[VehicleRental] Kein Schlüssel zum Entfernen gefunden: " .. plate .. " für Spieler " .. citizenid)
 | 
						|
        end
 | 
						|
    end)
 | 
						|
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
 | 
						|
                MySQL.Async.fetchAll('SELECT * FROM vehicle_keys WHERE owner = ? AND plate = ?', {
 | 
						|
                    rental.citizenid,
 | 
						|
                    rental.vehicle_plate
 | 
						|
                }, function(keyResults)
 | 
						|
                    if not keyResults or #keyResults == 0 then
 | 
						|
                        -- Füge einen Schlüssel hinzu, wenn keiner existiert
 | 
						|
                        AddRentalKey(rental.citizenid, rental.vehicle_plate, rental.vehicle_model)
 | 
						|
                    end
 | 
						|
                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 für das Mietfahrzeug hinzu
 | 
						|
        AddRentalKey(Player.PlayerData.citizenid, data.plate, data.vehicleModel)
 | 
						|
        
 | 
						|
        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 für das Mietfahrzeug (mit gleicher Struktur wie beim Hinzufügen)
 | 
						|
        RemoveRentalKey(Player.PlayerData.citizenid, plate, rental.vehicle_model)
 | 
						|
        
 | 
						|
        -- 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
 | 
						|
                RemoveRentalKey(rental.citizenid, plate, rental.vehicle_model)
 | 
						|
                
 | 
						|
                -- 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
 | 
						|
                -- Prüfe, ob bereits ein Schlüssel existiert
 | 
						|
                MySQL.Async.fetchAll('SELECT * FROM vehicle_keys WHERE owner = ? AND plate = ?', {
 | 
						|
                    Player.PlayerData.citizenid,
 | 
						|
                    rental.vehicle_plate
 | 
						|
                }, function(keyResults)
 | 
						|
                    if not keyResults or #keyResults == 0 then
 | 
						|
                        -- Füge einen Schlüssel hinzu, wenn keiner existiert
 | 
						|
                        AddRentalKey(Player.PlayerData.citizenid, rental.vehicle_plate, rental.vehicle_model)
 | 
						|
                    end
 | 
						|
                end)
 | 
						|
            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
 | 
						|
                -- Prüfe, ob ein Schlüssel existiert
 | 
						|
                MySQL.Async.fetchAll('SELECT * FROM vehicle_keys WHERE owner = ? AND plate = ?', {
 | 
						|
                    Player.PlayerData.citizenid,
 | 
						|
                    rental.vehicle_plate
 | 
						|
                }, function(keyResults)
 | 
						|
                    if not keyResults or #keyResults == 0 then
 | 
						|
                        -- Füge einen Schlüssel hinzu, wenn keiner existiert
 | 
						|
                        AddRentalKey(Player.PlayerData.citizenid, rental.vehicle_plate, rental.vehicle_model)
 | 
						|
                    end
 | 
						|
                end)
 | 
						|
            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
 | 
						|
            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
 | 
						|
                RemoveRentalKey(citizenid, rental.vehicle_plate, rental.vehicle_model)
 | 
						|
                
 | 
						|
                -- 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)
 | 
						|
 |