diff --git a/resources/[tools]/nordi_license/server/main.lua b/resources/[tools]/nordi_license/server/main.lua index 3c22cf16f..82c272383 100644 --- a/resources/[tools]/nordi_license/server/main.lua +++ b/resources/[tools]/nordi_license/server/main.lua @@ -4,7 +4,7 @@ local QBCore = exports['qb-core']:GetCoreObject() local licenseCache = {} local cacheTimeout = 300000 -- 5 Minuten --- Hilfsfunktionen +-- Debug-Funktion local function debugPrint(message) if Config.Debug then print("^3[License-System Server] " .. message .. "^7") @@ -24,6 +24,17 @@ local function getPlayerName(src) return "Unbekannt" end +-- Berechtigung prüfen +local function hasPermission(src) + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return false end + + local job = Player.PlayerData.job + if not job then return false end + + return Config.AuthorizedJobs[job.name] or false +end + -- Cache-Funktionen local function getCachedLicense(citizenid, licenseType) local cacheKey = citizenid .. "_" .. licenseType @@ -46,20 +57,7 @@ local function setCachedLicense(citizenid, licenseType, data) debugPrint("Lizenz gecacht: " .. cacheKey) end --- Berechtigung prüfen -local function hasPermission(src) - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return false end - - local job = Player.PlayerData.job - if not job then return false end - - local hasAuth = Config.AuthorizedJobs[job.name] or false - debugPrint("Berechtigung für " .. job.name .. ": " .. tostring(hasAuth)) - return hasAuth -end - --- Spieler-Name aus JSON extrahieren (Collation-sicher) +-- Spieler-Name aus JSON extrahieren local function extractPlayerName(charinfo_json) if not charinfo_json then return "Unbekannt" end @@ -71,7 +69,7 @@ local function extractPlayerName(charinfo_json) return "Unbekannt" end --- Sichere Datenbankoperationen mit Retry-Mechanismus +-- Sichere DB-Operation local function safeDBOperation(operation, errorMessage, maxRetries) maxRetries = maxRetries or 3 local retries = 0 @@ -89,14 +87,14 @@ local function safeDBOperation(operation, errorMessage, maxRetries) debugPrint("^1Details: " .. tostring(result) .. "^7") return nil end - Wait(1000) -- 1 Sekunde warten vor Retry + Wait(1000) end end return nil end --- Lizenz aus Datenbank abrufen (KORRIGIERT - Collation-sicher) +-- KORRIGIERTE Lizenz-Abfrage (Erweiterte Suche) local function getLicenseFromDB(citizenid, licenseType) debugPrint("=== getLicenseFromDB START ===") debugPrint("CitizenID: " .. tostring(citizenid)) @@ -105,74 +103,103 @@ local function getLicenseFromDB(citizenid, licenseType) -- Cache prüfen local cached = getCachedLicense(citizenid, licenseType) if cached then + debugPrint("Lizenz aus Cache geladen") return cached end - -- Einfache Query ohne JOINs um Collation-Probleme zu vermeiden - local query = [[ - SELECT * FROM player_licenses - WHERE citizenid = ? AND license_type = ? AND is_active = 1 - ORDER BY created_at DESC - LIMIT 1 - ]] + -- ERWEITERTE SUCHE: Erst aktive, dann alle Lizenzen + local queries = { + -- 1. Suche nach aktiven Lizenzen (is_active = 1) + { + query = "SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1", + description = "Aktive Lizenz" + }, + -- 2. Suche nach allen Lizenzen (falls is_active nicht gesetzt) + { + query = "SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? ORDER BY created_at DESC LIMIT 1", + description = "Neueste Lizenz" + }, + -- 3. Fallback: Suche ohne is_active Bedingung + { + query = "SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? ORDER BY id DESC LIMIT 1", + description = "Fallback-Suche" + } + } - local result = safeDBOperation(function() - return MySQL.query.await(query, {citizenid, licenseType}) - end, "Fehler beim Abrufen der Lizenz") + local license = nil - if result and #result > 0 then - local license = result[1] + for i, queryData in ipairs(queries) do + debugPrint("Versuche Query " .. i .. ": " .. queryData.description) - -- Spieler-Namen separat abrufen - local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" - local holderResult = safeDBOperation(function() - return MySQL.query.await(holderQuery, {citizenid}) - end, "Fehler beim Abrufen des Spieler-Namens") + local result = safeDBOperation(function() + return MySQL.query.await(queryData.query, {citizenid, licenseType}) + end, "Fehler bei " .. queryData.description) - if holderResult and #holderResult > 0 then - license.holder_name = extractPlayerName(holderResult[1].charinfo) + if result and #result > 0 then + license = result[1] + debugPrint("Lizenz gefunden mit Query " .. i .. ": " .. queryData.description) + break else - license.holder_name = "Unbekannt" + debugPrint("Keine Lizenz mit Query " .. i .. " gefunden") end + end + + if not license then + debugPrint("^1Keine Lizenz in DB gefunden für " .. citizenid .. " / " .. licenseType .. "^7") + return nil + end + + -- Spieler-Namen abrufen + local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" + local holderResult = safeDBOperation(function() + return MySQL.query.await(holderQuery, {citizenid}) + end, "Fehler beim Abrufen des Spieler-Namens") + + if holderResult and #holderResult > 0 then + license.holder_name = extractPlayerName(holderResult[1].charinfo) + else + license.holder_name = "Unbekannt" + end + + -- Aussteller-Namen abrufen + if license.issued_by then + local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?" + local issuerResult = safeDBOperation(function() + return MySQL.query.await(issuerQuery, {license.issued_by}) + end, "Fehler beim Abrufen des Aussteller-Namens") - -- Aussteller-Namen separat abrufen - if license.issued_by then - local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?" - local issuerResult = safeDBOperation(function() - return MySQL.query.await(issuerQuery, {license.issued_by}) - end, "Fehler beim Abrufen des Aussteller-Namens") - - if issuerResult and #issuerResult > 0 then - license.issued_by_name = extractPlayerName(issuerResult[1].charinfo) - else - license.issued_by_name = "System" - end + if issuerResult and #issuerResult > 0 then + license.issued_by_name = extractPlayerName(issuerResult[1].charinfo) else license.issued_by_name = "System" end - - -- Classes parsen - if license.classes then - local success, classes = pcall(json.decode, license.classes) - if success and type(classes) == "table" then - license.classes = classes - else - license.classes = {} - end + else + license.issued_by_name = "System" + end + + -- Classes parsen + if license.classes then + local success, classes = pcall(json.decode, license.classes) + if success and type(classes) == "table" then + license.classes = classes else license.classes = {} end - - debugPrint("Lizenz aus DB geladen: " .. license.license_type) - - -- In Cache speichern - setCachedLicense(citizenid, licenseType, license) - - return license + else + license.classes = {} end - debugPrint("Keine Lizenz in DB gefunden") - return nil + -- is_active standardisieren (falls nicht gesetzt) + if license.is_active == nil then + license.is_active = 1 + end + + debugPrint("Lizenz erfolgreich geladen: " .. license.license_type .. " (Active: " .. tostring(license.is_active) .. ")") + + -- In Cache speichern + setCachedLicense(citizenid, licenseType, license) + + return license end -- Alle Lizenzen eines Spielers abrufen (KORRIGIERT) @@ -180,80 +207,97 @@ local function getAllPlayerLicenses(citizenid) debugPrint("=== getAllPlayerLicenses START ===") debugPrint("CitizenID: " .. tostring(citizenid)) - -- Einfache Query ohne JOINs - local query = [[ - SELECT * FROM player_licenses - WHERE citizenid = ? AND is_active = 1 - ORDER BY license_type, created_at DESC - ]] + -- Erweiterte Suche für alle Lizenzen + local queries = { + -- 1. Aktive Lizenzen + "SELECT * FROM player_licenses WHERE citizenid = ? AND is_active = 1 ORDER BY license_type, created_at DESC", + -- 2. Alle Lizenzen (Fallback) + "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC" + } - local result = safeDBOperation(function() - return MySQL.query.await(query, {citizenid}) - end, "Fehler beim Abrufen aller Lizenzen") + local result = nil - if result and #result > 0 then - local licenses = {} - local seenTypes = {} + for i, query in ipairs(queries) do + debugPrint("Versuche Abfrage " .. i .. " für alle Lizenzen") - -- Spieler-Namen einmal abrufen - local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" - local holderResult = safeDBOperation(function() - return MySQL.query.await(holderQuery, {citizenid}) - end, "Fehler beim Abrufen des Spieler-Namens") + result = safeDBOperation(function() + return MySQL.query.await(query, {citizenid}) + end, "Fehler bei Abfrage " .. i) - local holderName = "Unbekannt" - if holderResult and #holderResult > 0 then - holderName = extractPlayerName(holderResult[1].charinfo) + if result and #result > 0 then + debugPrint("Lizenzen gefunden mit Abfrage " .. i .. ": " .. #result .. " Einträge") + break end - - for _, license in ipairs(result) do - -- Nur die neueste Lizenz pro Typ nehmen - if not seenTypes[license.license_type] then - seenTypes[license.license_type] = true + end + + if not result or #result == 0 then + debugPrint("Keine Lizenzen gefunden für: " .. citizenid) + return {} + end + + local licenses = {} + local seenTypes = {} + + -- Spieler-Namen einmal abrufen + local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" + local holderResult = safeDBOperation(function() + return MySQL.query.await(holderQuery, {citizenid}) + end, "Fehler beim Abrufen des Spieler-Namens") + + local holderName = "Unbekannt" + if holderResult and #holderResult > 0 then + holderName = extractPlayerName(holderResult[1].charinfo) + end + + for _, license in ipairs(result) do + -- Nur die neueste Lizenz pro Typ nehmen + if not seenTypes[license.license_type] then + seenTypes[license.license_type] = true + + license.holder_name = holderName + + -- Aussteller-Namen abrufen + if license.issued_by then + local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?" + local issuerResult = safeDBOperation(function() + return MySQL.query.await(issuerQuery, {license.issued_by}) + end, "Fehler beim Abrufen des Aussteller-Namens") - license.holder_name = holderName - - -- Aussteller-Namen abrufen - if license.issued_by then - local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?" - local issuerResult = safeDBOperation(function() - return MySQL.query.await(issuerQuery, {license.issued_by}) - end, "Fehler beim Abrufen des Aussteller-Namens") - - if issuerResult and #issuerResult > 0 then - license.issued_by_name = extractPlayerName(issuerResult[1].charinfo) - else - license.issued_by_name = "System" - end + if issuerResult and #issuerResult > 0 then + license.issued_by_name = extractPlayerName(issuerResult[1].charinfo) else license.issued_by_name = "System" end - - -- Classes parsen - if license.classes then - local success, classes = pcall(json.decode, license.classes) - if success and type(classes) == "table" then - license.classes = classes - else - license.classes = {} - end + else + license.issued_by_name = "System" + end + + -- Classes parsen + if license.classes then + local success, classes = pcall(json.decode, license.classes) + if success and type(classes) == "table" then + license.classes = classes else license.classes = {} end - - table.insert(licenses, license) + else + license.classes = {} end + + -- is_active standardisieren + if license.is_active == nil then + license.is_active = 1 + end + + table.insert(licenses, license) end - - debugPrint("Gefundene Lizenzen: " .. #licenses) - return licenses end - debugPrint("Keine Lizenzen gefunden") - return {} + debugPrint("Verarbeitete Lizenzen: " .. #licenses) + return licenses end --- Lizenz in Datenbank speichern (KORRIGIERT - DateTime-Fix) +-- Lizenz in Datenbank speichern (KORRIGIERT) local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes) debugPrint("=== saveLicenseToDB START ===") debugPrint("CitizenID: " .. tostring(citizenid)) @@ -266,7 +310,7 @@ local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes) return false end - -- Spieler-Name für das name-Feld abrufen + -- Spieler-Name abrufen local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" local holderResult = safeDBOperation(function() return MySQL.query.await(holderQuery, {citizenid}) @@ -295,16 +339,14 @@ local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes) return MySQL.query.await(deactivateQuery, {citizenid, licenseType}) end, "Fehler beim Deaktivieren alter Lizenz") - -- Neue Lizenz einfügen (KORRIGIERT - DateTime als BIGINT oder korrektes Format) + -- Neue Lizenz einfügen local insertQuery = [[ INSERT INTO player_licenses (citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ]] - -- created_at als BIGINT (Unix Timestamp) oder als DATETIME - local createdAt = os.time() -- Unix Timestamp für BIGINT - -- Alternativ für DATETIME: local createdAt = os.date("%Y-%m-%d %H:%M:%S") + local createdAt = os.time() -- Unix Timestamp local insertData = { citizenid, @@ -313,7 +355,7 @@ local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes) issueDate, expireDate, issuedBy, - 1, + 1, -- is_active = 1 classesJson, createdAt } @@ -345,7 +387,7 @@ local function revokeLicenseInDB(citizenid, licenseType) debugPrint("CitizenID: " .. tostring(citizenid)) debugPrint("LicenseType: " .. tostring(licenseType)) - local query = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ? AND is_active = 1" + local query = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?" local result = safeDBOperation(function() return MySQL.query.await(query, {citizenid, licenseType}) @@ -390,7 +432,7 @@ CreateThread(function() end end) --- EVENT HANDLER: Lizenz anfordern +-- EVENT HANDLER: Lizenz anfordern (KORRIGIERT für Ausweis-Anzeige) RegisterNetEvent('license-system:server:requestLicense', function(targetId) local src = source debugPrint("=== Event: requestLicense ===") @@ -411,17 +453,22 @@ RegisterNetEvent('license-system:server:requestLicense', function(targetId) local citizenid = targetPlayer.PlayerData.citizenid - -- Erste verfügbare Lizenz finden + -- PRIORITÄT: Erst nach Ausweis suchen, dann andere Lizenzen + local licenseTypes = {"id_card", "driver_license", "weapon_license", "pilot_license"} local foundLicense = nil - for licenseType, _ in pairs(Config.LicenseTypes) do - local license = getLicenseFromDB(citizenid, licenseType) - - if license and license.is_active == 1 then - foundLicense = { - license = license, - config = Config.LicenseTypes[licenseType] - } - break + + for _, licenseType in ipairs(licenseTypes) do + if Config.LicenseTypes[licenseType] then + local license = getLicenseFromDB(citizenid, licenseType) + + if license then + foundLicense = { + license = license, + config = Config.LicenseTypes[licenseType] + } + debugPrint("Lizenz gefunden: " .. licenseType) + break + end end end @@ -429,16 +476,16 @@ RegisterNetEvent('license-system:server:requestLicense', function(targetId) debugPrint("Sende Lizenz an Client: " .. foundLicense.license.license_type) TriggerClientEvent('license-system:client:receiveLicense', src, foundLicense) else - debugPrint("Keine aktive Lizenz gefunden") + debugPrint("Keine Lizenz gefunden") TriggerClientEvent('license-system:client:receiveLicense', src, nil) end end) --- EVENT HANDLER: Eigene Lizenz anfordern +-- EVENT HANDLER: Eigene Lizenz anfordern (KORRIGIERT) RegisterNetEvent('license-system:server:requestMyLicense', function(licenseType) local src = source debugPrint("=== Event: requestMyLicense ===") - debugPrint("Source: " .. src .. ", LicenseType: " .. licenseType) + debugPrint("Source: " .. src .. ", LicenseType: " .. tostring(licenseType)) local Player = QBCore.Functions.GetPlayer(src) if not Player then @@ -448,9 +495,16 @@ RegisterNetEvent('license-system:server:requestMyLicense', function(licenseType) end local citizenid = Player.PlayerData.citizenid + + -- Falls kein spezifischer Typ angegeben, suche nach Ausweis + if not licenseType or licenseType == "" then + licenseType = "id_card" + debugPrint("Kein Lizenztyp angegeben, verwende: " .. licenseType) + end + local license = getLicenseFromDB(citizenid, licenseType) - if license and license.is_active == 1 then + if license then local licenseData = { license = license, config = Config.LicenseTypes[licenseType] @@ -518,7 +572,7 @@ RegisterNetEvent('license-system:server:issueLicense', function(targetId, licens local targetCitizenId = targetPlayer.PlayerData.citizenid local issuerCitizenId = issuerPlayer.PlayerData.citizenid - -- Prüfen ob Lizenz bereits existiert + -- Prüfen ob aktive Lizenz bereits existiert local existingLicense = getLicenseFromDB(targetCitizenId, licenseType) if existingLicense and existingLicense.is_active == 1 then @@ -613,12 +667,12 @@ RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licen end end) --- EXPORT FUNKTIONEN +-- EXPORT FUNKTIONEN (KORRIGIERT) exports('hasLicense', function(citizenid, licenseType) if not citizenid or not licenseType then return false end local license = getLicenseFromDB(citizenid, licenseType) - return license and license.is_active == 1 + return license ~= nil and (license.is_active == 1 or license.is_active == nil) end) exports('issueLicense', function(citizenid, licenseType, issuedBy, classes) @@ -646,9 +700,67 @@ exports('getPlayerLicense', function(citizenid, licenseType) return getLicenseFromDB(citizenid, licenseType) end) +-- DEBUG COMMAND: Lizenz manuell erstellen +RegisterCommand('createlicense', function(source, args, rawCommand) + if source == 0 then -- Console only + if #args < 2 then + print("Usage: createlicense ") + return + end + + local citizenid = args[1] + local licenseType = args[2] + + if not Config.LicenseTypes[licenseType] then + print("Unbekannter Lizenztyp: " .. licenseType) + return + end + + local success = saveLicenseToDB(citizenid, licenseType, 'console', {}) + + if success then + print("Lizenz erfolgreich erstellt: " .. licenseType .. " für " .. citizenid) + else + print("Fehler beim Erstellen der Lizenz") + end + end +end, true) + +-- DEBUG COMMAND: Lizenz prüfen +RegisterCommand('checklicense', function(source, args, rawCommand) + if source == 0 then -- Console only + if #args < 2 then + print("Usage: checklicense ") + return + end + + local citizenid = args[1] + local licenseType = args[2] + + local license = getLicenseFromDB(citizenid, licenseType) + + if license then + print("=== LIZENZ GEFUNDEN ===") + print("ID: " .. (license.id or "N/A")) + print("CitizenID: " .. (license.citizenid or "N/A")) + print("Typ: " .. (license.license_type or "N/A")) + print("Name: " .. (license.name or "N/A")) + print("Ausstellungsdatum: " .. (license.issue_date or "N/A")) + print("Ablaufdatum: " .. (license.expire_date or "N/A")) + print("Ausgestellt von: " .. (license.issued_by or "N/A")) + print("Aktiv: " .. tostring(license.is_active)) + print("Klassen: " .. (license.classes and json.encode(license.classes) or "[]")) + print("Erstellt am: " .. (license.created_at or "N/A")) + print("=====================") + else + print("Keine Lizenz gefunden für: " .. citizenid .. " / " .. licenseType) + end + end +end, true) + -- INITIALISIERUNG CreateThread(function() - debugPrint("License-System Server gestartet (Collation & DateTime Fix)") + debugPrint("License-System Server gestartet (Ausweis-Fix)") -- Warten bis QBCore geladen ist while not QBCore do @@ -689,7 +801,7 @@ RegisterCommand('licensestats', function(source, args, rawCommand) print("=== LICENSE SYSTEM STATS ===") print("Cache Entries: " .. cacheCount) - print("Config License Types: " .. (Config.LicenseTypes and #Config.LicenseTypes or 0)) + print("Config License Types: " .. (Config.LicenseTypes and table.count(Config.LicenseTypes) or 0)) print("============================") end end, true) @@ -706,4 +818,14 @@ RegisterCommand('licenseclearcache', function(source, args, rawCommand) end end, true) -debugPrint("License-System Server vollständig geladen (Collation & DateTime Fix)") +-- Hilfsfunktion für table.count +function table.count(t) + local count = 0 + for _ in pairs(t) do + count = count + 1 + end + return count +end + +debugPrint("License-System Server vollständig geladen (Ausweis-Fix)") +