1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_license/server/main.lua
2025-08-04 08:12:36 +02:00

577 lines
22 KiB
Lua

local QBCore = exports['qb-core']:GetCoreObject()
-- Lokale Variablen
local licenseCache = {}
-- Hilfsfunktionen
local function debugPrint(message)
if Config.Debug then
print("^2[License-System Server] " .. message .. "^7")
end
end
local function safeCallback(cb, ...)
if cb and type(cb) == "function" then
cb(...)
return true
else
debugPrint("^1FEHLER: Callback ist keine Funktion! Typ: " .. type(cb) .. "^7")
return false
end
end
local function formatDate(timestamp)
if not timestamp then return nil end
return os.date("%d.%m.%Y", timestamp)
end
local function addDaysToDate(days)
return os.time() + (days * 24 * 60 * 60)
end
local function isLicenseExpired(expireDate)
if not expireDate then return false end
local expireTime = os.time({
year = tonumber(string.sub(expireDate, 7, 10)),
month = tonumber(string.sub(expireDate, 4, 5)),
day = tonumber(string.sub(expireDate, 1, 2))
})
return os.time() > expireTime
end
-- Spieler-Daten abrufen
local function getPlayerData(source)
local Player = QBCore.Functions.GetPlayer(source)
if not Player then return nil end
return {
citizenid = Player.PlayerData.citizenid,
name = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
firstname = Player.PlayerData.charinfo.firstname,
lastname = Player.PlayerData.charinfo.lastname,
birthday = Player.PlayerData.charinfo.birthdate,
gender = Player.PlayerData.charinfo.gender,
job = Player.PlayerData.job.name,
money = Player.PlayerData.money.cash
}
end
-- Berechtigung prüfen
local function hasPermission(source, licenseType)
local playerData = getPlayerData(source)
if not playerData then return false end
-- Admin-Check
if QBCore.Functions.HasPermission(source, 'admin') then
return true
end
-- Job-Check
if Config.AuthorizedJobs[playerData.job] then
return true
end
-- Spezifische Lizenz-Berechtigung
local licenseConfig = Config.LicenseTypes[licenseType]
if licenseConfig and licenseConfig.required_job then
return playerData.job == licenseConfig.required_job
end
return false
end
-- Benötigte Items prüfen
local function hasRequiredItems(source, licenseType)
local Player = QBCore.Functions.GetPlayer(source)
if not Player then return false end
local licenseConfig = Config.LicenseTypes[licenseType]
if not licenseConfig or not licenseConfig.required_items then return true end
for _, item in ipairs(licenseConfig.required_items) do
local hasItem = Player.Functions.GetItemByName(item)
if not hasItem or hasItem.amount < 1 then
return false
end
end
return true
end
-- Lizenz erstellen
local function createLicense(citizenid, licenseType, issuedBy, classes)
local licenseConfig = Config.LicenseTypes[licenseType]
if not licenseConfig then
debugPrint("^1Lizenz-Konfiguration nicht gefunden: " .. licenseType .. "^7")
return false
end
local issueDate = os.time()
local expireDate = nil
if licenseConfig.can_expire and licenseConfig.validity_days then
expireDate = addDaysToDate(licenseConfig.validity_days)
end
local licenseData = {
citizenid = citizenid,
license_type = licenseType,
issue_date = formatDate(issueDate),
expire_date = expireDate and formatDate(expireDate) or nil,
issued_by = issuedBy,
is_active = 1,
classes = classes and json.encode(classes) or '[]',
created_at = issueDate
}
debugPrint("Erstelle Lizenz: " .. licenseType .. " für " .. citizenid)
MySQL.Async.insert('INSERT INTO player_licenses (citizenid, license_type, issue_date, expire_date, issued_by, is_active, classes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', {
licenseData.citizenid,
licenseData.license_type,
licenseData.issue_date,
licenseData.expire_date,
licenseData.issued_by,
licenseData.is_active,
licenseData.classes,
licenseData.created_at
}, function(insertId)
if insertId then
debugPrint("Lizenz erfolgreich erstellt mit ID: " .. insertId)
-- Cache aktualisieren
if not licenseCache[citizenid] then
licenseCache[citizenid] = {}
end
licenseCache[citizenid][licenseType] = licenseData
else
debugPrint("^1Fehler beim Erstellen der Lizenz in der Datenbank!^7")
end
end)
return true
end
-- Aussteller-Name abrufen
local function getIssuerName(citizenid, callback)
if not citizenid then
callback('System')
return
end
MySQL.Async.fetchScalar('SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.firstname")), " ", JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.lastname"))) FROM players WHERE citizenid = ?', {
citizenid
}, function(issuerName)
callback(issuerName or 'Unbekannt')
end)
end
-- Callbacks
QBCore.Functions.CreateCallback('license-system:server:getLicense', function(source, cb, targetId)
debugPrint("getLicense Callback - Source: " .. source .. ", Target: " .. tostring(targetId))
if not cb or type(cb) ~= "function" then
debugPrint("^1FEHLER: Ungültiger Callback in getLicense!^7")
return
end
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
if not TargetPlayer then
debugPrint("^1Ziel-Spieler nicht gefunden: " .. tostring(targetId) .. "^7")
safeCallback(cb, nil)
return
end
local citizenid = TargetPlayer.PlayerData.citizenid
debugPrint("Suche Lizenz für CitizenID: " .. citizenid)
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1', {
citizenid
}, function(result)
if result and result[1] then
local license = result[1]
debugPrint("Lizenz gefunden: " .. license.license_type)
-- Spieler-Daten hinzufügen
license.name = TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname
license.birthday = TargetPlayer.PlayerData.charinfo.birthdate
license.gender = TargetPlayer.PlayerData.charinfo.gender
-- Aussteller-Name abrufen
getIssuerName(license.issued_by, function(issuerName)
license.issued_by_name = issuerName
local licenseData = {
license = license,
config = Config.LicenseTypes[license.license_type] or {
label = license.license_type,
icon = 'fas fa-id-card',
color = '#667eea'
}
}
safeCallback(cb, licenseData)
end)
else
debugPrint("Keine aktive Lizenz gefunden für: " .. citizenid)
safeCallback(cb, nil)
end
end)
end)
QBCore.Functions.CreateCallback('license-system:server:getMyLicense', function(source, cb, licenseType)
debugPrint("getMyLicense Callback - Source: " .. source .. ", Typ: " .. tostring(licenseType))
if not cb or type(cb) ~= "function" then
debugPrint("^1FEHLER: Ungültiger Callback in getMyLicense!^7")
return
end
local Player = QBCore.Functions.GetPlayer(source)
if not Player then
debugPrint("^1Spieler nicht gefunden: " .. source .. "^7")
safeCallback(cb, nil)
return
end
local citizenid = Player.PlayerData.citizenid
debugPrint("Suche eigene Lizenz - CitizenID: " .. citizenid .. ", Typ: " .. licenseType)
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1', {
citizenid,
licenseType
}, function(result)
if result and result[1] then
local license = result[1]
debugPrint("Eigene Lizenz gefunden: " .. license.license_type)
-- Spieler-Daten hinzufügen
license.name = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
license.birthday = Player.PlayerData.charinfo.birthdate
license.gender = Player.PlayerData.charinfo.gender
-- Aussteller-Name abrufen
getIssuerName(license.issued_by, function(issuerName)
license.issued_by_name = issuerName
local licenseData = {
license = license,
config = Config.LicenseTypes[license.license_type] or {
label = license.license_type,
icon = 'fas fa-id-card',
color = '#667eea'
}
}
safeCallback(cb, licenseData)
end)
else
debugPrint("Keine eigene Lizenz vom Typ " .. licenseType .. " gefunden")
safeCallback(cb, nil)
end
end)
end)
QBCore.Functions.CreateCallback('license-system:server:getPlayerLicenses', function(source, cb, targetId)
debugPrint("getPlayerLicenses Callback - Source: " .. source .. ", Target: " .. tostring(targetId))
if not cb or type(cb) ~= "function" then
debugPrint("^1FEHLER: Ungültiger Callback in getPlayerLicenses!^7")
return
end
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
if not TargetPlayer then
debugPrint("^1Ziel-Spieler nicht gefunden: " .. tostring(targetId) .. "^7")
safeCallback(cb, {})
return
end
local citizenid = TargetPlayer.PlayerData.citizenid
debugPrint("Suche alle Lizenzen für CitizenID: " .. citizenid)
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY created_at DESC', {
citizenid
}, function(result)
if result then
debugPrint("Gefundene Lizenzen: " .. #result)
-- Spieler-Daten zu jeder Lizenz hinzufügen
for i, license in ipairs(result) do
license.name = TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname
license.birthday = TargetPlayer.PlayerData.charinfo.birthdate
license.gender = TargetPlayer.PlayerData.charinfo.gender
license.issued_by_name = 'System' -- Wird später durch echten Namen ersetzt
end
safeCallback(cb, result)
else
debugPrint("Keine Lizenzen gefunden")
safeCallback(cb, {})
end
end)
end)
QBCore.Functions.CreateCallback('license-system:server:canIssueLicense', function(source, cb, licenseType)
debugPrint("canIssueLicense Callback - Source: " .. source .. ", Typ: " .. tostring(licenseType))
if not cb or type(cb) ~= "function" then
debugPrint("^1FEHLER: Ungültiger Callback in canIssueLicense!^7")
return
end
local hasAuth = hasPermission(source, licenseType)
debugPrint("Berechtigung für Lizenz " .. licenseType .. ": " .. tostring(hasAuth))
safeCallback(cb, hasAuth)
end)
-- Events
RegisterNetEvent('license-system:server:issueLicense', function(targetId, licenseType, classes)
local src = source
debugPrint("Event: issueLicense - Von: " .. src .. ", Für: " .. targetId .. ", Typ: " .. licenseType)
local Player = QBCore.Functions.GetPlayer(src)
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
if not Player or not TargetPlayer then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_players_nearby.message, Config.Notifications.no_players_nearby.type)
return
end
-- Berechtigung prüfen
if not hasPermission(src, licenseType) then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
-- Lizenz-Konfiguration prüfen
local licenseConfig = Config.LicenseTypes[licenseType]
if not licenseConfig then
TriggerClientEvent('QBCore:Notify', src, 'Unbekannter Lizenztyp!', 'error')
return
end
-- Benötigte Items prüfen
if not hasRequiredItems(targetId, licenseType) then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.missing_items.message, Config.Notifications.missing_items.type)
return
end
-- Geld prüfen (falls Kosten anfallen)
if licenseConfig.price > 0 then
if TargetPlayer.PlayerData.money.cash < licenseConfig.price then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.insufficient_funds.message, Config.Notifications.insufficient_funds.type)
return
end
-- Geld abziehen
TargetPlayer.Functions.RemoveMoney('cash', licenseConfig.price, 'license-fee')
debugPrint("Geld abgezogen: " .. licenseConfig.price .. "$ von " .. TargetPlayer.PlayerData.charinfo.firstname)
end
-- Alte Lizenz deaktivieren
MySQL.Async.execute('UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?', {
TargetPlayer.PlayerData.citizenid,
licenseType
}, function(affectedRows)
debugPrint("Alte Lizenzen deaktiviert: " .. affectedRows)
end)
-- Neue Lizenz erstellen
local success = createLicense(TargetPlayer.PlayerData.citizenid, licenseType, Player.PlayerData.citizenid, classes)
if success then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.license_granted.message, Config.Notifications.license_granted.type)
TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue ' .. licenseConfig.label .. ' erhalten!', 'success')
-- Log erstellen
debugPrint(Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname .. ' hat ' .. TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname .. ' eine ' .. licenseConfig.label .. ' ausgestellt')
else
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
end
end)
RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licenseType)
local src = source
debugPrint("Event: revokeLicense - Von: " .. src .. ", Für: " .. targetId .. ", Typ: " .. licenseType)
local Player = QBCore.Functions.GetPlayer(src)
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
if not Player or not TargetPlayer then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_players_nearby.message, Config.Notifications.no_players_nearby.type)
return
end
-- Berechtigung prüfen
if not hasPermission(src, licenseType) then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
-- Lizenz deaktivieren
MySQL.Async.execute('UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ? AND is_active = 1', {
TargetPlayer.PlayerData.citizenid,
licenseType
}, function(affectedRows)
if affectedRows > 0 then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.license_revoked.message, Config.Notifications.license_revoked.type)
TriggerClientEvent('QBCore:Notify', targetId, 'Deine ' .. (Config.LicenseTypes[licenseType] and Config.LicenseTypes[licenseType].label or licenseType) .. ' wurde entzogen!', 'error')
-- Cache aktualisieren
if licenseCache[TargetPlayer.PlayerData.citizenid] then
licenseCache[TargetPlayer.PlayerData.citizenid][licenseType] = nil
end
debugPrint(Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname .. ' hat ' .. TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname .. ' die ' .. (Config.LicenseTypes[licenseType] and Config.LicenseTypes[licenseType].label or licenseType) .. ' entzogen')
else
TriggerClientEvent('QBCore:Notify', src, 'Keine aktive Lizenz gefunden!', 'error')
end
end)
end)
RegisterNetEvent('license-system:server:savePhoto', function(citizenid, photoData)
local src = source
debugPrint("Event: savePhoto für CitizenID: " .. citizenid)
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
-- Foto in der Datenbank speichern
MySQL.Async.execute('UPDATE player_licenses SET photo_url = ? WHERE citizenid = ? AND is_active = 1', {
photoData,
citizenid
}, function(affectedRows)
if affectedRows > 0 then
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.photo_saved.message, Config.Notifications.photo_saved.type)
debugPrint("Foto gespeichert für: " .. citizenid)
else
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Speichern des Fotos!', 'error')
end
end)
end)
-- Admin-Kommandos
QBCore.Commands.Add('givelicense', 'Lizenz an Spieler vergeben', {
{name = 'id', help = 'Spieler ID'},
{name = 'type', help = 'Lizenztyp'},
{name = 'classes', help = 'Klassen (optional)'}
}, true, function(source, args)
local targetId = tonumber(args[1])
local licenseType = args[2]
local classes = args[3] and {args[3]} or nil
debugPrint("Admin-Command: givelicense - ID: " .. tostring(targetId) .. ", Typ: " .. tostring(licenseType))
if not targetId or not licenseType then
TriggerClientEvent('QBCore:Notify', source, 'Verwendung: /givelicense [id] [typ] [klassen]', 'error')
return
end
if not Config.LicenseTypes[licenseType] then
TriggerClientEvent('QBCore:Notify', source, 'Unbekannter Lizenztyp!', 'error')
return
end
TriggerEvent('license-system:server:issueLicense', targetId, licenseType, classes)
end, 'admin')
QBCore.Commands.Add('revokelicense', 'Lizenz entziehen', {
{name = 'id', help = 'Spieler ID'},
{name = 'type', help = 'Lizenztyp'}
}, true, function(source, args)
local targetId = tonumber(args[1])
local licenseType = args[2]
debugPrint("Admin-Command: revokelicense - ID: " .. tostring(targetId) .. ", Typ: " .. tostring(licenseType))
if not targetId or not licenseType then
TriggerClientEvent('QBCore:Notify', source, 'Verwendung: /revokelicense [id] [typ]', 'error')
return
end
TriggerEvent('license-system:server:revokeLicense', targetId, licenseType)
end, 'admin')
-- Cleanup-Task für abgelaufene Lizenzen
if Config.Database.auto_cleanup then
CreateThread(function()
while true do
Wait(24 * 60 * 60 * 1000) -- Einmal täglich
local cutoffDate = os.time() - (Config.Database.cleanup_days * 24 * 60 * 60)
MySQL.Async.execute('DELETE FROM player_licenses WHERE is_active = 0 AND created_at < ?', {
cutoffDate
}, function(affectedRows)
if affectedRows > 0 then
debugPrint("Cleanup: " .. affectedRows .. " alte Lizenzen gelöscht")
end
end)
end
end)
end
-- Lizenz-Ablauf-Checker
CreateThread(function()
while true do
Wait(60 * 60 * 1000) -- Jede Stunde
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE is_active = 1 AND expire_date IS NOT NULL', {}, function(result)
if result then
for _, license in ipairs(result) do
if isLicenseExpired(license.expire_date) then
-- Lizenz als abgelaufen markieren
MySQL.Async.execute('UPDATE player_licenses SET is_active = 0 WHERE id = ?', {
license.id
})
debugPrint("Lizenz abgelaufen: " .. license.license_type .. " für " .. license.citizenid)
end
end
end
end)
end
end)
-- Resource Start/Stop Events
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
debugPrint("License-System Server gestartet")
-- Datenbank-Tabelle erstellen falls nicht vorhanden
MySQL.Async.execute([[
CREATE TABLE IF NOT EXISTS player_licenses (
id INT AUTO_INCREMENT PRIMARY KEY,
citizenid VARCHAR(50) NOT NULL,
license_type VARCHAR(50) NOT NULL,
issue_date VARCHAR(20) NOT NULL,
expire_date VARCHAR(20) NULL,
issued_by VARCHAR(50) NULL,
is_active TINYINT(1) DEFAULT 1,
classes TEXT NULL,
photo_url TEXT NULL,
notes TEXT NULL,
created_at BIGINT NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_citizenid (citizenid),
INDEX idx_license_type (license_type),
INDEX idx_active (is_active)
)
]], {}, function(success)
if success then
debugPrint("Datenbank-Tabelle erfolgreich erstellt/überprüft")
else
debugPrint("^1Fehler beim Erstellen der Datenbank-Tabelle!^7")
end
end)
end
end)
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
debugPrint("License-System Server gestoppt")
-- Cache leeren
licenseCache = {}
end
end)