1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_license/server/main.lua

1175 lines
41 KiB
Lua
Raw Normal View History

2025-08-04 08:53:16 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
-- Lokale Variablen
local licenseCache = {}
local cacheTimeout = 300000 -- 5 Minuten
2025-08-04 09:05:35 +02:00
-- Debug-Funktion
2025-08-04 08:53:16 +02:00
local function debugPrint(message)
if Config.Debug then
print("^3[License-System Server] " .. message .. "^7")
end
end
-- Spieler-Name abrufen
local function getPlayerName(src)
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return "Unbekannt" end
local charinfo = Player.PlayerData.charinfo
if charinfo and charinfo.firstname and charinfo.lastname then
return charinfo.firstname .. " " .. charinfo.lastname
end
return "Unbekannt"
end
2025-08-04 09:05:35 +02:00
-- 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
2025-08-04 09:15:25 +02:00
-- Cache-Funktionen (KORRIGIERT - Aggressive Cache-Invalidierung)
2025-08-04 08:53:16 +02:00
local function getCachedLicense(citizenid, licenseType)
local cacheKey = citizenid .. "_" .. licenseType
local cached = licenseCache[cacheKey]
if cached and (os.time() * 1000 - cached.timestamp) < cacheTimeout then
debugPrint("Cache-Hit für: " .. cacheKey)
return cached.data
end
return nil
end
local function setCachedLicense(citizenid, licenseType, data)
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = {
data = data,
timestamp = os.time() * 1000
}
debugPrint("Lizenz gecacht: " .. cacheKey)
end
2025-08-04 09:15:25 +02:00
-- Cache invalidieren (ERWEITERT)
local function invalidateCache(citizenid, licenseType)
if licenseType then
-- Spezifische Lizenz invalidieren
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = nil
debugPrint("Cache invalidiert für: " .. cacheKey)
else
-- Alle Lizenzen des Spielers invalidieren
for key, _ in pairs(licenseCache) do
if string.find(key, citizenid .. "_") then
licenseCache[key] = nil
debugPrint("Cache invalidiert für: " .. key)
end
end
end
end
2025-08-04 09:05:35 +02:00
-- Spieler-Name aus JSON extrahieren
2025-08-04 08:53:16 +02:00
local function extractPlayerName(charinfo_json)
if not charinfo_json then return "Unbekannt" end
local success, charinfo = pcall(json.decode, charinfo_json)
if success and charinfo and charinfo.firstname and charinfo.lastname then
return charinfo.firstname .. " " .. charinfo.lastname
end
return "Unbekannt"
end
2025-08-04 09:05:35 +02:00
-- Sichere DB-Operation
2025-08-04 08:56:54 +02:00
local function safeDBOperation(operation, errorMessage, maxRetries)
maxRetries = maxRetries or 3
local retries = 0
while retries < maxRetries do
retries = retries + 1
local success, result = pcall(operation)
if success then
return result
else
debugPrint("^3DB-Retry " .. retries .. "/" .. maxRetries .. ": " .. tostring(result) .. "^7")
if retries >= maxRetries then
debugPrint("^1DB-Fehler: " .. (errorMessage or "Unbekannt") .. "^7")
debugPrint("^1Details: " .. tostring(result) .. "^7")
return nil
end
2025-08-04 09:05:35 +02:00
Wait(1000)
2025-08-04 08:56:54 +02:00
end
end
return nil
end
2025-08-04 09:15:25 +02:00
-- Lizenz-Status prüfen (NEUE FUNKTION)
local function isLicenseActive(license)
if not license then return false end
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
-- is_active prüfen (1 = aktiv, 0 = inaktiv, nil = aktiv per default)
local isActive = license.is_active
if isActive == nil then
isActive = 1 -- Default: aktiv
end
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
if isActive ~= 1 then
debugPrint("Lizenz inaktiv (is_active = " .. tostring(isActive) .. ")")
return false
end
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
-- Ablaufdatum prüfen (falls vorhanden)
if license.expire_date and license.expire_date ~= "" then
local expireDate = license.expire_date
local currentDate = os.date("%d.%m.%Y")
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
-- Einfache Datumsvergleich (DD.MM.YYYY)
local function parseDate(dateStr)
local day, month, year = dateStr:match("(%d+)%.(%d+)%.(%d+)")
if day and month and year then
return os.time({year = tonumber(year), month = tonumber(month), day = tonumber(day)})
end
return nil
end
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
local expireTimestamp = parseDate(expireDate)
local currentTimestamp = parseDate(currentDate)
if expireTimestamp and currentTimestamp and expireTimestamp < currentTimestamp then
debugPrint("Lizenz abgelaufen: " .. expireDate .. " < " .. currentDate)
return false
2025-08-04 08:56:54 +02:00
end
2025-08-04 09:05:35 +02:00
end
2025-08-04 09:15:25 +02:00
debugPrint("Lizenz ist aktiv und gültig")
return true
end
-- KORRIGIERTE Lizenz-Abfrage (Ohne Cache für frische Daten)
local function getLicenseFromDB(citizenid, licenseType, skipCache)
debugPrint("=== getLicenseFromDB START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
debugPrint("LicenseType: " .. tostring(licenseType))
debugPrint("SkipCache: " .. tostring(skipCache))
-- Cache prüfen (nur wenn nicht übersprungen)
if not skipCache then
local cached = getCachedLicense(citizenid, licenseType)
if cached then
debugPrint("Lizenz aus Cache geladen")
return cached
end
end
-- Direkte DB-Abfrage (VEREINFACHT - nur aktive Lizenzen)
local query = [[
SELECT * FROM player_licenses
WHERE citizenid = ? AND license_type = ?
ORDER BY created_at DESC
LIMIT 1
]]
local result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid, licenseType})
end, "Fehler beim Abrufen der Lizenz")
if not result or #result == 0 then
2025-08-04 09:05:35 +02:00
debugPrint("^1Keine Lizenz in DB gefunden für " .. citizenid .. " / " .. licenseType .. "^7")
return nil
end
2025-08-04 09:15:25 +02:00
local license = result[1]
debugPrint("Rohe Lizenz-Daten aus DB:")
debugPrint("ID: " .. tostring(license.id))
debugPrint("is_active: " .. tostring(license.is_active))
debugPrint("created_at: " .. tostring(license.created_at))
2025-08-04 09:05:35 +02:00
-- 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")
2025-08-04 08:56:54 +02:00
2025-08-04 09:05:35 +02:00
if issuerResult and #issuerResult > 0 then
license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
2025-08-04 08:56:54 +02:00
else
2025-08-04 08:53:16 +02:00
license.issued_by_name = "System"
end
2025-08-04 09:05:35 +02:00
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
2025-08-04 08:53:16 +02:00
else
license.classes = {}
end
2025-08-04 09:05:35 +02:00
else
license.classes = {}
2025-08-04 08:53:16 +02:00
end
2025-08-04 09:15:25 +02:00
-- is_active normalisieren (WICHTIG!)
2025-08-04 09:05:35 +02:00
if license.is_active == nil then
license.is_active = 1
2025-08-04 09:15:25 +02:00
debugPrint("is_active war nil, auf 1 gesetzt")
end
-- Status prüfen
local isActive = isLicenseActive(license)
debugPrint("Lizenz-Status-Prüfung: " .. tostring(isActive))
if not isActive then
debugPrint("Lizenz ist nicht aktiv/gültig")
return nil
2025-08-04 09:05:35 +02:00
end
debugPrint("Lizenz erfolgreich geladen: " .. license.license_type .. " (Active: " .. tostring(license.is_active) .. ")")
2025-08-04 09:15:25 +02:00
-- In Cache speichern (nur wenn aktiv)
if not skipCache then
setCachedLicense(citizenid, licenseType, license)
end
2025-08-04 09:05:35 +02:00
return license
2025-08-04 08:53:16 +02:00
end
2025-08-04 08:56:54 +02:00
-- Alle Lizenzen eines Spielers abrufen (KORRIGIERT)
2025-08-04 08:53:16 +02:00
local function getAllPlayerLicenses(citizenid)
debugPrint("=== getAllPlayerLicenses START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
2025-08-04 09:15:25 +02:00
-- Alle Lizenzen abrufen (ohne is_active Filter)
local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
local result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid})
end, "Fehler beim Abrufen aller Lizenzen")
2025-08-04 09:05:35 +02:00
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")
2025-08-04 08:53:16 +02:00
2025-08-04 09:05:35 +02:00
if issuerResult and #issuerResult > 0 then
license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
2025-08-04 08:56:54 +02:00
else
2025-08-04 08:53:16 +02:00
license.issued_by_name = "System"
end
2025-08-04 09:05:35 +02:00
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
2025-08-04 08:53:16 +02:00
else
license.classes = {}
end
2025-08-04 09:05:35 +02:00
else
license.classes = {}
end
2025-08-04 09:15:25 +02:00
-- is_active normalisieren
2025-08-04 09:05:35 +02:00
if license.is_active == nil then
license.is_active = 1
2025-08-04 08:53:16 +02:00
end
2025-08-04 09:05:35 +02:00
2025-08-04 09:15:25 +02:00
-- Status prüfen und nur aktive Lizenzen hinzufügen
if isLicenseActive(license) then
table.insert(licenses, license)
debugPrint("Aktive Lizenz hinzugefügt: " .. license.license_type)
else
debugPrint("Inaktive Lizenz übersprungen: " .. license.license_type)
end
2025-08-04 08:53:16 +02:00
end
end
2025-08-04 09:15:25 +02:00
debugPrint("Verarbeitete aktive Lizenzen: " .. #licenses)
2025-08-04 09:05:35 +02:00
return licenses
2025-08-04 08:53:16 +02:00
end
2025-08-04 09:15:25 +02:00
-- Lizenz in Datenbank speichern (KORRIGIERT - Explizite is_active Setzung)
2025-08-04 08:53:16 +02:00
local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes)
debugPrint("=== saveLicenseToDB START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
debugPrint("LicenseType: " .. tostring(licenseType))
debugPrint("IssuedBy: " .. tostring(issuedBy))
local config = Config.LicenseTypes[licenseType]
if not config then
debugPrint("^1Fehler: Unbekannter Lizenztyp: " .. licenseType .. "^7")
return false
end
2025-08-04 09:15:25 +02:00
-- Cache für diesen Spieler komplett invalidieren
invalidateCache(citizenid)
2025-08-04 09:05:35 +02:00
-- Spieler-Name abrufen
2025-08-04 08:53:16 +02:00
local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
2025-08-04 08:56:54 +02:00
local holderResult = safeDBOperation(function()
return MySQL.query.await(holderQuery, {citizenid})
end, "Fehler beim Abrufen des Spieler-Namens")
2025-08-04 08:53:16 +02:00
2025-08-04 08:56:54 +02:00
local holderName = "Unbekannt"
2025-08-04 08:53:16 +02:00
if holderResult and #holderResult > 0 and holderResult[1].charinfo then
holderName = extractPlayerName(holderResult[1].charinfo)
end
-- Datum berechnen
local issueDate = os.date("%d.%m.%Y")
local expireDate = nil
if config.validity_days then
local expireTimestamp = os.time() + (config.validity_days * 24 * 60 * 60)
expireDate = os.date("%d.%m.%Y", expireTimestamp)
end
-- Classes zu JSON konvertieren
local classesJson = json.encode(classes or {})
2025-08-04 09:15:25 +02:00
-- WICHTIG: Alte Lizenz explizit deaktivieren
2025-08-04 08:53:16 +02:00
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
2025-08-04 09:15:25 +02:00
local deactivateResult = safeDBOperation(function()
2025-08-04 08:56:54 +02:00
return MySQL.query.await(deactivateQuery, {citizenid, licenseType})
end, "Fehler beim Deaktivieren alter Lizenz")
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
debugPrint("Alte Lizenzen deaktiviert: " .. tostring(deactivateResult ~= nil))
-- Neue Lizenz einfügen (EXPLIZIT is_active = 1)
2025-08-04 08:53:16 +02:00
local insertQuery = [[
INSERT INTO player_licenses
(citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, created_at)
2025-08-04 09:15:25 +02:00
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)
2025-08-04 08:53:16 +02:00
]]
2025-08-04 09:05:35 +02:00
local createdAt = os.time() -- Unix Timestamp
2025-08-04 08:56:54 +02:00
2025-08-04 08:53:16 +02:00
local insertData = {
citizenid,
licenseType,
holderName,
issueDate,
expireDate,
issuedBy,
2025-08-04 09:15:25 +02:00
-- is_active = 1 ist direkt in der Query
2025-08-04 08:53:16 +02:00
classesJson,
2025-08-04 08:56:54 +02:00
createdAt
2025-08-04 08:53:16 +02:00
}
debugPrint("Führe INSERT-Query aus...")
debugPrint("Daten: " .. json.encode(insertData))
2025-08-04 08:56:54 +02:00
local result = safeDBOperation(function()
return MySQL.insert.await(insertQuery, insertData)
end, "Fehler beim Speichern der Lizenz")
2025-08-04 08:53:16 +02:00
2025-08-04 08:56:54 +02:00
if result then
2025-08-04 08:53:16 +02:00
debugPrint("Lizenz erfolgreich gespeichert. ID: " .. result)
2025-08-04 09:15:25 +02:00
-- Cache komplett invalidieren (sicherstellen dass neue Daten geladen werden)
invalidateCache(citizenid)
-- Sofort neue Lizenz aus DB laden um zu verifizieren
Wait(100) -- Kurz warten
local newLicense = getLicenseFromDB(citizenid, licenseType, true) -- Skip Cache
if newLicense then
debugPrint("Neue Lizenz erfolgreich verifiziert: is_active = " .. tostring(newLicense.is_active))
else
debugPrint("^3Warnung: Neue Lizenz konnte nicht verifiziert werden^7")
end
2025-08-04 08:53:16 +02:00
return true
else
2025-08-04 08:56:54 +02:00
debugPrint("^1Fehler beim Speichern der Lizenz^7")
2025-08-04 08:53:16 +02:00
return false
end
end
2025-08-04 09:15:25 +02:00
-- Lizenz entziehen (KORRIGIERT)
2025-08-04 08:53:16 +02:00
local function revokeLicenseInDB(citizenid, licenseType)
debugPrint("=== revokeLicenseInDB START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
debugPrint("LicenseType: " .. tostring(licenseType))
2025-08-04 09:15:25 +02:00
local query = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ? AND is_active = 1"
2025-08-04 08:53:16 +02:00
2025-08-04 08:56:54 +02:00
local result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid, licenseType})
end, "Fehler beim Entziehen der Lizenz")
if result then
2025-08-04 08:53:16 +02:00
debugPrint("Lizenz erfolgreich entzogen")
-- Cache invalidieren
2025-08-04 09:15:25 +02:00
invalidateCache(citizenid, licenseType)
2025-08-04 08:53:16 +02:00
return true
else
2025-08-04 08:56:54 +02:00
debugPrint("^1Fehler beim Entziehen der Lizenz^7")
2025-08-04 08:53:16 +02:00
return false
end
end
2025-08-04 08:56:54 +02:00
-- Cache bereinigen
local function cleanupCache()
local now = os.time() * 1000
local cleaned = 0
for key, cached in pairs(licenseCache) do
if (now - cached.timestamp) > cacheTimeout then
licenseCache[key] = nil
cleaned = cleaned + 1
end
end
if cleaned > 0 then
debugPrint("Cache bereinigt: " .. cleaned .. " Einträge")
end
2025-08-04 08:53:16 +02:00
end
2025-08-04 08:56:54 +02:00
-- Cache-Cleanup Thread
CreateThread(function()
while true do
Wait(300000) -- 5 Minuten
cleanupCache()
end
end)
2025-08-04 09:15:25 +02:00
-- EVENT HANDLER: Lizenz anfordern
2025-08-04 08:53:16 +02:00
RegisterNetEvent('license-system:server:requestLicense', function(targetId)
local src = source
debugPrint("=== Event: requestLicense ===")
debugPrint("Source: " .. src .. ", Target: " .. targetId)
if not hasPermission(src) then
debugPrint("Keine Berechtigung für Spieler: " .. src)
TriggerClientEvent('license-system:client:receiveLicense', src, nil)
return
end
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
if not targetPlayer then
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
TriggerClientEvent('license-system:client:receiveLicense', src, nil)
return
end
local citizenid = targetPlayer.PlayerData.citizenid
2025-08-04 09:05:35 +02:00
-- PRIORITÄT: Erst nach Ausweis suchen, dann andere Lizenzen
local licenseTypes = {"id_card", "driver_license", "weapon_license", "pilot_license"}
2025-08-04 08:53:16 +02:00
local foundLicense = nil
2025-08-04 09:05:35 +02:00
for _, licenseType in ipairs(licenseTypes) do
if Config.LicenseTypes[licenseType] then
2025-08-04 09:15:25 +02:00
local license = getLicenseFromDB(citizenid, licenseType, true) -- Skip Cache für frische Daten
2025-08-04 09:05:35 +02:00
if license then
foundLicense = {
license = license,
config = Config.LicenseTypes[licenseType]
}
debugPrint("Lizenz gefunden: " .. licenseType)
break
end
2025-08-04 08:53:16 +02:00
end
end
if foundLicense then
debugPrint("Sende Lizenz an Client: " .. foundLicense.license.license_type)
TriggerClientEvent('license-system:client:receiveLicense', src, foundLicense)
else
2025-08-04 09:05:35 +02:00
debugPrint("Keine Lizenz gefunden")
2025-08-04 08:53:16 +02:00
TriggerClientEvent('license-system:client:receiveLicense', src, nil)
end
end)
2025-08-04 09:05:35 +02:00
-- EVENT HANDLER: Eigene Lizenz anfordern (KORRIGIERT)
2025-08-04 08:53:16 +02:00
RegisterNetEvent('license-system:server:requestMyLicense', function(licenseType)
local src = source
debugPrint("=== Event: requestMyLicense ===")
2025-08-04 09:05:35 +02:00
debugPrint("Source: " .. src .. ", LicenseType: " .. tostring(licenseType))
2025-08-04 08:53:16 +02:00
local Player = QBCore.Functions.GetPlayer(src)
if not Player then
debugPrint("Spieler nicht gefunden: " .. src)
TriggerClientEvent('license-system:client:receiveMyLicense', src, nil, licenseType)
return
end
local citizenid = Player.PlayerData.citizenid
2025-08-04 09:05:35 +02:00
-- Falls kein spezifischer Typ angegeben, suche nach Ausweis
if not licenseType or licenseType == "" then
licenseType = "id_card"
debugPrint("Kein Lizenztyp angegeben, verwende: " .. licenseType)
end
2025-08-04 09:15:25 +02:00
local license = getLicenseFromDB(citizenid, licenseType, true) -- Skip Cache für frische Daten
2025-08-04 08:53:16 +02:00
2025-08-04 09:05:35 +02:00
if license then
2025-08-04 08:53:16 +02:00
local licenseData = {
license = license,
config = Config.LicenseTypes[licenseType]
}
debugPrint("Sende eigene Lizenz an Client: " .. licenseType)
TriggerClientEvent('license-system:client:receiveMyLicense', src, licenseData, licenseType)
else
debugPrint("Keine aktive eigene Lizenz gefunden: " .. licenseType)
TriggerClientEvent('license-system:client:receiveMyLicense', src, nil, licenseType)
end
end)
-- EVENT HANDLER: Alle Spieler-Lizenzen anfordern
RegisterNetEvent('license-system:server:requestPlayerLicenses', function(targetId)
local src = source
debugPrint("=== Event: requestPlayerLicenses ===")
debugPrint("Source: " .. src .. ", Target: " .. targetId)
if not hasPermission(src) then
debugPrint("Keine Berechtigung für Spieler: " .. src)
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, {}, targetId, "Unbekannt")
return
end
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
if not targetPlayer then
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, {}, targetId, "Unbekannt")
return
end
local citizenid = targetPlayer.PlayerData.citizenid
local targetName = getPlayerName(targetId)
2025-08-04 08:56:54 +02:00
local licenses = getAllPlayerLicenses(citizenid)
2025-08-04 08:53:16 +02:00
debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, licenses, targetId, targetName)
end)
2025-08-04 09:15:25 +02:00
-- EVENT HANDLER: Lizenz ausstellen (KORRIGIERT)
2025-08-04 08:53:16 +02:00
RegisterNetEvent('license-system:server:issueLicense', function(targetId, licenseType, classes)
local src = source
debugPrint("=== Event: issueLicense ===")
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
if not hasPermission(src) then
debugPrint("Keine Berechtigung für Spieler: " .. src)
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
if not targetPlayer then
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
return
end
local issuerPlayer = QBCore.Functions.GetPlayer(src)
if not issuerPlayer then
debugPrint("Aussteller nicht gefunden: " .. src)
return
end
local targetCitizenId = targetPlayer.PlayerData.citizenid
local issuerCitizenId = issuerPlayer.PlayerData.citizenid
2025-08-04 09:15:25 +02:00
-- Prüfen ob aktive Lizenz bereits existiert (Skip Cache)
local existingLicense = getLicenseFromDB(targetCitizenId, licenseType, true)
2025-08-04 08:53:16 +02:00
2025-08-04 09:15:25 +02:00
if existingLicense then
2025-08-04 08:53:16 +02:00
debugPrint("Lizenz bereits vorhanden und aktiv")
TriggerClientEvent('QBCore:Notify', src, 'Spieler hat bereits eine aktive ' .. (Config.LicenseTypes[licenseType].label or licenseType) .. '!', 'error')
return
end
-- Kosten prüfen
local config = Config.LicenseTypes[licenseType]
if config.price and config.price > 0 then
if issuerPlayer.PlayerData.money.cash < config.price then
debugPrint("Nicht genug Geld für Lizenz-Ausstellung")
TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Bargeld! Benötigt: $' .. config.price, 'error')
return
end
-- Geld abziehen
issuerPlayer.Functions.RemoveMoney('cash', config.price, 'license-issued')
TriggerClientEvent('QBCore:Notify', src, 'Lizenz-Gebühr bezahlt: $' .. config.price, 'success')
end
-- Lizenz in Datenbank speichern
2025-08-04 08:56:54 +02:00
local success = saveLicenseToDB(targetCitizenId, licenseType, issuerCitizenId, classes)
2025-08-04 08:53:16 +02:00
if success then
local targetName = getPlayerName(targetId)
local issuerName = getPlayerName(src)
debugPrint("Lizenz erfolgreich ausgestellt")
-- Benachrichtigungen
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success')
TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success')
-- Events senden
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseType)
TriggerClientEvent('license-system:client:refreshMenu', src)
-- Log
debugPrint("Lizenz " .. licenseType .. " ausgestellt von " .. issuerName .. " für " .. targetName)
else
debugPrint("^1Fehler beim Ausstellen der Lizenz^7")
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
end
end)
-- EVENT HANDLER: Lizenz entziehen
RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licenseType)
local src = source
debugPrint("=== Event: revokeLicense ===")
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
if not hasPermission(src) then
debugPrint("Keine Berechtigung für Spieler: " .. src)
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
if not targetPlayer then
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
return
end
local targetCitizenId = targetPlayer.PlayerData.citizenid
-- Lizenz entziehen
2025-08-04 08:56:54 +02:00
local success = revokeLicenseInDB(targetCitizenId, licenseType)
2025-08-04 08:53:16 +02:00
if success then
local targetName = getPlayerName(targetId)
local issuerName = getPlayerName(src)
local config = Config.LicenseTypes[licenseType]
debugPrint("Lizenz erfolgreich entzogen")
-- Benachrichtigungen
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich entzogen von ' .. targetName, 'success')
TriggerClientEvent('QBCore:Notify', targetId, 'Deine Lizenz wurde entzogen: ' .. (config.label or licenseType), 'error')
-- Events senden
TriggerClientEvent('license-system:client:licenseRevoked', src, targetId, licenseType)
TriggerClientEvent('license-system:client:refreshMenu', src)
-- Log
debugPrint("Lizenz " .. licenseType .. " entzogen von " .. issuerName .. " für " .. targetName)
else
debugPrint("^1Fehler beim Entziehen der Lizenz^7")
2025-08-04 09:15:25 +02:00
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!',
'error')
2025-08-04 08:53:16 +02:00
end
end)
2025-08-04 09:05:35 +02:00
-- EXPORT FUNKTIONEN (KORRIGIERT)
2025-08-04 08:53:16 +02:00
exports('hasLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return false end
2025-08-04 09:15:25 +02:00
local license = getLicenseFromDB(citizenid, licenseType, true) -- Skip Cache für aktuelle Daten
return license ~= nil
2025-08-04 08:53:16 +02:00
end)
exports('issueLicense', function(citizenid, licenseType, issuedBy, classes)
if not citizenid or not licenseType then return false end
issuedBy = issuedBy or 'system'
2025-08-04 08:56:54 +02:00
return saveLicenseToDB(citizenid, licenseType, issuedBy, classes)
2025-08-04 08:53:16 +02:00
end)
exports('revokeLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return false end
2025-08-04 08:56:54 +02:00
return revokeLicenseInDB(citizenid, licenseType)
2025-08-04 08:53:16 +02:00
end)
exports('getPlayerLicenses', function(citizenid)
if not citizenid then return {} end
2025-08-04 08:56:54 +02:00
return getAllPlayerLicenses(citizenid)
2025-08-04 08:53:16 +02:00
end)
exports('getPlayerLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return nil end
2025-08-04 09:15:25 +02:00
return getLicenseFromDB(citizenid, licenseType, true) -- Skip Cache
2025-08-04 08:53:16 +02:00
end)
2025-08-04 09:05:35 +02:00
-- DEBUG COMMAND: Lizenz manuell erstellen
RegisterCommand('createlicense', function(source, args, rawCommand)
if source == 0 then -- Console only
if #args < 2 then
print("Usage: createlicense <citizenid> <license_type>")
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)
2025-08-04 09:15:25 +02:00
-- DEBUG COMMAND: Lizenz prüfen (ERWEITERT)
2025-08-04 09:05:35 +02:00
RegisterCommand('checklicense', function(source, args, rawCommand)
if source == 0 then -- Console only
if #args < 2 then
print("Usage: checklicense <citizenid> <license_type>")
return
end
local citizenid = args[1]
local licenseType = args[2]
2025-08-04 09:15:25 +02:00
-- Direkte DB-Abfrage ohne Cache
local query = "SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? ORDER BY created_at DESC"
local result = MySQL.query.await(query, {citizenid, licenseType})
if result and #result > 0 then
print("=== ALLE LIZENZEN GEFUNDEN ===")
for i, license in ipairs(result) do
print("--- Lizenz " .. i .. " ---")
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 (DB): " .. tostring(license.is_active))
print("Klassen: " .. (license.classes or "[]"))
print("Erstellt am: " .. (license.created_at or "N/A"))
print("---")
end
print("===============================")
-- Zusätzlich: Lizenz über Funktion prüfen
local license = getLicenseFromDB(citizenid, licenseType, true)
if license then
print("=== AKTIVE LIZENZ (über Funktion) ===")
print("Typ: " .. license.license_type)
print("Aktiv: " .. tostring(license.is_active))
print("Status-Check: " .. tostring(isLicenseActive(license)))
print("====================================")
else
print("=== KEINE AKTIVE LIZENZ (über Funktion) ===")
end
2025-08-04 09:05:35 +02:00
else
print("Keine Lizenz gefunden für: " .. citizenid .. " / " .. licenseType)
end
end
end, true)
2025-08-04 09:15:25 +02:00
-- DEBUG COMMAND: Alle Lizenzen eines Spielers anzeigen
RegisterCommand('checkalllicenses', function(source, args, rawCommand)
if source == 0 then -- Console only
if #args < 1 then
print("Usage: checkalllicenses <citizenid>")
return
end
local citizenid = args[1]
-- Direkte DB-Abfrage
local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
local result = MySQL.query.await(query, {citizenid})
if result and #result > 0 then
print("=== ALLE LIZENZEN FÜR " .. citizenid .. " ===")
for i, license in ipairs(result) do
print(i .. ". " .. license.license_type .. " | Aktiv: " .. tostring(license.is_active) .. " | ID: " .. license.id)
end
print("=========================================")
-- Über Funktion
local activeLicenses = getAllPlayerLicenses(citizenid)
print("=== AKTIVE LIZENZEN (über Funktion) ===")
for i, license in ipairs(activeLicenses) do
print(i .. ". " .. license.license_type .. " | Aktiv: " .. tostring(license.is_active))
end
print("======================================")
else
print("Keine Lizenzen gefunden für: " .. citizenid)
end
end
end, true)
-- DEBUG COMMAND: Lizenz-Status forciert aktualisieren
RegisterCommand('fixlicense', function(source, args, rawCommand)
if source == 0 then -- Console only
if #args < 2 then
print("Usage: fixlicense <citizenid> <license_type>")
return
end
local citizenid = args[1]
local licenseType = args[2]
-- Neueste Lizenz auf aktiv setzen
local query = [[
UPDATE player_licenses
SET is_active = 1
WHERE citizenid = ? AND license_type = ? AND id = (
SELECT id FROM (
SELECT id FROM player_licenses
WHERE citizenid = ? AND license_type = ?
ORDER BY created_at DESC LIMIT 1
) as temp
)
]]
local result = MySQL.query.await(query, {citizenid, licenseType, citizenid, licenseType})
if result then
print("Lizenz-Status aktualisiert für: " .. citizenid .. " / " .. licenseType)
-- Cache invalidieren
invalidateCache(citizenid, licenseType)
-- Prüfen
local license = getLicenseFromDB(citizenid, licenseType, true)
if license then
print("Lizenz ist jetzt aktiv: " .. tostring(license.is_active))
else
print("Lizenz konnte nicht geladen werden")
end
else
print("Fehler beim Aktualisieren der Lizenz")
end
end
end, true)
2025-08-04 08:53:16 +02:00
-- INITIALISIERUNG
CreateThread(function()
2025-08-04 09:15:25 +02:00
debugPrint("License-System Server gestartet (Status-Fix)")
2025-08-04 08:53:16 +02:00
-- Warten bis QBCore geladen ist
while not QBCore do
Wait(100)
end
debugPrint("QBCore erfolgreich geladen")
-- Datenbank-Verbindung testen
local testResult = safeDBOperation(function()
return MySQL.query.await("SELECT 1 as test")
end, "Datenbank-Verbindungstest")
if testResult then
debugPrint("Datenbank-Verbindung erfolgreich")
else
debugPrint("^1Datenbank-Verbindung fehlgeschlagen^7")
end
debugPrint("License-System Server vollständig initialisiert")
end)
-- CLEANUP
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
debugPrint("License-System Server gestoppt")
licenseCache = {}
end
end)
-- DEBUG COMMANDS
RegisterCommand('licensestats', function(source, args, rawCommand)
if source == 0 then -- Console only
local cacheCount = 0
for _ in pairs(licenseCache) do
cacheCount = cacheCount + 1
end
print("=== LICENSE SYSTEM STATS ===")
print("Cache Entries: " .. cacheCount)
2025-08-04 09:05:35 +02:00
print("Config License Types: " .. (Config.LicenseTypes and table.count(Config.LicenseTypes) or 0))
2025-08-04 08:53:16 +02:00
print("============================")
end
end, true)
RegisterCommand('licenseclearcache', function(source, args, rawCommand)
if source == 0 then -- Console only
local oldCount = 0
for _ in pairs(licenseCache) do
oldCount = oldCount + 1
end
licenseCache = {}
print("License-Cache geleert. Entfernte Einträge: " .. oldCount)
end
end, true)
2025-08-04 09:05:35 +02:00
-- Hilfsfunktion für table.count
function table.count(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
2025-08-04 09:24:53 +02:00
-- Neuer Event-Handler für benutzerdefinierte Lizenzen
RegisterNetEvent('license-system:server:issueCustomLicense', function(targetId, licenseType, customData, classes)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
if not Player or not TargetPlayer then
debugPrint("Spieler nicht gefunden: " .. src .. " -> " .. targetId)
return
end
-- Berechtigung prüfen
if not isAuthorized(Player.PlayerData.job.name) then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
-- Lizenz-Konfiguration prüfen
local config = Config.LicenseTypes[licenseType]
if not config then
debugPrint("Unbekannter Lizenztyp: " .. licenseType)
return
end
debugPrint("Erstelle benutzerdefinierte Lizenz: " .. licenseType .. " für " .. TargetPlayer.PlayerData.citizenid)
-- Benutzerdefinierte Daten validieren und bereinigen
local validatedData = {}
for _, field in ipairs(config.custom_fields or {}) do
local value = customData[field.name]
-- Pflichtfeld-Prüfung
if field.required and (not value or value == "") then
TriggerClientEvent('QBCore:Notify', src, "Feld '" .. field.label .. "' ist erforderlich", "error")
return
end
-- Wert bereinigen und validieren
if value and value ~= "" then
value = string.gsub(value, "'", "''") -- SQL-Injection Schutz
-- Typ-spezifische Validierung
if field.type == "url" and not string.match(value, "^https?://") then
TriggerClientEvent('QBCore:Notify', src, "Ungültige URL in Feld '" .. field.label .. "'", "error")
return
end
if field.type == "date" and not string.match(value, "^%d%d%.%d%d%.%d%d%d%d$") then
TriggerClientEvent('QBCore:Notify', src, "Ungültiges Datum in Feld '" .. field.label .. "'", "error")
return
end
validatedData[field.name] = value
end
end
-- Klassen validieren
local validatedClasses = {}
if config.classes and classes then
for _, class in ipairs(classes) do
if config.classes[class.key] then
table.insert(validatedClasses, class)
end
end
end
-- Lizenz in Datenbank speichern
local success = saveCustomLicenseToDB(
TargetPlayer.PlayerData.citizenid,
licenseType,
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname,
validatedData,
validatedClasses
)
if success then
debugPrint("Benutzerdefinierte Lizenz erfolgreich gespeichert")
-- Cache invalidieren
invalidateCache(TargetPlayer.PlayerData.citizenid, licenseType)
-- Benachrichtigungen
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.license_issued.message, Config.Notifications.license_issued.type)
TriggerClientEvent('QBCore:Notify', targetId, "Du hast eine " .. config.label .. " erhalten!", "success")
-- Events
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseType)
TriggerClientEvent('license-system:client:refreshMenu', src)
-- Log
debugPrint("Lizenz ausgestellt: " .. licenseType .. " von " .. Player.PlayerData.name .. " an " .. TargetPlayer.PlayerData.name)
else
debugPrint("Fehler beim Speichern der benutzerdefinierten Lizenz")
TriggerClientEvent('QBCore:Notify', src, "Fehler beim Ausstellen der Lizenz", "error")
end
end)
-- Funktion zum Speichern benutzerdefinierter Lizenzen
function saveCustomLicenseToDB(citizenid, licenseType, issuedBy, customData, classes)
local config = Config.LicenseTypes[licenseType]
if not config then return false end
-- Ablaufdatum berechnen
local expireDate = nil
if config.validity_days then
expireDate = os.date('%Y-%m-%d %H:%M:%S', os.time() + (config.validity_days * 24 * 60 * 60))
end
-- Holder-Name aus Custom-Data oder Standard
local holderName = customData.holder_name or "Unbekannt"
return safeDBOperation(function()
-- Alte Lizenzen deaktivieren
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
MySQL.query.await(deactivateQuery, {citizenid, licenseType})
-- Neue Lizenz einfügen
local insertQuery = [[
INSERT INTO player_licenses
(citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, custom_data, holder_name)
VALUES (?, ?, ?, NOW(), ?, ?, 1, ?, ?, ?)
]]
local result = MySQL.insert.await(insertQuery, {
citizenid,
licenseType,
config.label,
expireDate,
issuedBy,
json.encode(classes or {}),
json.encode(customData or {}),
holderName
})
return result and result > 0
end, "Benutzerdefinierte Lizenz speichern")
end
-- Erweiterte Lizenz-Abruf-Funktion
function getLicenseFromDB(citizenid, licenseType, skipCache)
-- Cache prüfen
local cacheKey = citizenid .. "_" .. licenseType
if not skipCache and licenseCache[cacheKey] then
debugPrint("Lizenz aus Cache geladen: " .. licenseType)
return licenseCache[cacheKey]
end
local license = safeDBOperation(function()
local query = [[
SELECT *,
CASE
WHEN expire_date IS NULL THEN 1
WHEN expire_date > NOW() THEN 1
ELSE 0
END as is_valid
FROM player_licenses
WHERE citizenid = ? AND license_type = ? AND is_active = 1
ORDER BY created_at DESC
LIMIT 1
]]
local result = MySQL.query.await(query, {citizenid, licenseType})
return result and result[1] or nil
end, "Lizenz abrufen")
if license then
-- Custom-Data und Classes parsen
if license.custom_data then
license.custom_data_parsed = json.decode(license.custom_data)
end
if license.classes then
license.classes_parsed = json.decode(license.classes)
end
-- Cache speichern
licenseCache[cacheKey] = license
debugPrint("Lizenz in Cache gespeichert: " .. licenseType)
end
return license
end
debugPrint("License-System Server erweitert geladen (Custom License Support)")