1
0
Fork 0
forked from Simnation/Main

Update main.lua

This commit is contained in:
Nordi98 2025-08-04 08:56:54 +02:00
parent ecd6dddfde
commit 23aa5c33e7

View file

@ -24,32 +24,6 @@ local function getPlayerName(src)
return "Unbekannt"
end
-- Spieler-Daten abrufen
local function getPlayerData(src)
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return nil end
return {
citizenid = Player.PlayerData.citizenid,
name = getPlayerName(src),
charinfo = Player.PlayerData.charinfo,
job = Player.PlayerData.job
}
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
-- Cache-Funktionen
local function getCachedLicense(citizenid, licenseType)
local cacheKey = citizenid .. "_" .. licenseType
@ -72,6 +46,325 @@ 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)
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
-- Sichere Datenbankoperationen mit Retry-Mechanismus
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
Wait(1000) -- 1 Sekunde warten vor Retry
end
end
return nil
end
-- Lizenz aus Datenbank abrufen (KORRIGIERT - Collation-sicher)
local function getLicenseFromDB(citizenid, licenseType)
debugPrint("=== getLicenseFromDB START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
debugPrint("LicenseType: " .. tostring(licenseType))
-- Cache prüfen
local cached = getCachedLicense(citizenid, licenseType)
if cached then
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
]]
local result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid, licenseType})
end, "Fehler beim Abrufen der Lizenz")
if result and #result > 0 then
local license = result[1]
-- 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")
if holderResult and #holderResult > 0 then
license.holder_name = extractPlayerName(holderResult[1].charinfo)
else
license.holder_name = "Unbekannt"
end
-- 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
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.classes = {}
end
debugPrint("Lizenz aus DB geladen: " .. license.license_type)
-- In Cache speichern
setCachedLicense(citizenid, licenseType, license)
return license
end
debugPrint("Keine Lizenz in DB gefunden")
return nil
end
-- Alle Lizenzen eines Spielers abrufen (KORRIGIERT)
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
]]
local result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid})
end, "Fehler beim Abrufen aller Lizenzen")
if result and #result > 0 then
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")
if issuerResult and #issuerResult > 0 then
license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
else
license.issued_by_name = "System"
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
else
license.classes = {}
end
table.insert(licenses, license)
end
end
debugPrint("Gefundene Lizenzen: " .. #licenses)
return licenses
end
debugPrint("Keine Lizenzen gefunden")
return {}
end
-- Lizenz in Datenbank speichern (KORRIGIERT - DateTime-Fix)
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
-- Spieler-Name für das name-Feld 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 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 {})
-- Alte Lizenz deaktivieren
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
safeDBOperation(function()
return MySQL.query.await(deactivateQuery, {citizenid, licenseType})
end, "Fehler beim Deaktivieren alter Lizenz")
-- Neue Lizenz einfügen (KORRIGIERT - DateTime als BIGINT oder korrektes Format)
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 insertData = {
citizenid,
licenseType,
holderName,
issueDate,
expireDate,
issuedBy,
1,
classesJson,
createdAt
}
debugPrint("Führe INSERT-Query aus...")
debugPrint("Daten: " .. json.encode(insertData))
local result = safeDBOperation(function()
return MySQL.insert.await(insertQuery, insertData)
end, "Fehler beim Speichern der Lizenz")
if result then
debugPrint("Lizenz erfolgreich gespeichert. ID: " .. result)
-- Cache invalidieren
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = nil
return true
else
debugPrint("^1Fehler beim Speichern der Lizenz^7")
return false
end
end
-- Lizenz entziehen
local function revokeLicenseInDB(citizenid, licenseType)
debugPrint("=== revokeLicenseInDB START ===")
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 result = safeDBOperation(function()
return MySQL.query.await(query, {citizenid, licenseType})
end, "Fehler beim Entziehen der Lizenz")
if result then
debugPrint("Lizenz erfolgreich entzogen")
-- Cache invalidieren
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = nil
return true
else
debugPrint("^1Fehler beim Entziehen der Lizenz^7")
return false
end
end
-- Cache bereinigen
local function cleanupCache()
local now = os.time() * 1000
@ -97,260 +390,6 @@ CreateThread(function()
end
end)
-- Spieler-Name aus JSON extrahieren (MariaDB-kompatibel)
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
-- Lizenz aus Datenbank abrufen (KORRIGIERT für MariaDB)
local function getLicenseFromDB(citizenid, licenseType)
debugPrint("=== getLicenseFromDB START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
debugPrint("LicenseType: " .. tostring(licenseType))
-- Cache prüfen
local cached = getCachedLicense(citizenid, licenseType)
if cached then
return cached
end
-- MariaDB-kompatible Query ohne JSON-Operatoren
local query = [[
SELECT pl.*,
p.charinfo as holder_charinfo,
pi.charinfo as issued_by_charinfo
FROM player_licenses pl
LEFT JOIN players p ON p.citizenid = pl.citizenid
LEFT JOIN players pi ON pi.citizenid = pl.issued_by
WHERE pl.citizenid = ? AND pl.license_type = ?
ORDER BY pl.created_at DESC
LIMIT 1
]]
local result = MySQL.query.await(query, {citizenid, licenseType})
if result and #result > 0 then
local license = result[1]
-- Namen aus JSON extrahieren
license.holder_name = extractPlayerName(license.holder_charinfo)
license.issued_by_name = extractPlayerName(license.issued_by_charinfo)
-- Fallback für issued_by_name
if license.issued_by_name == "Unbekannt" then
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.classes = {}
end
-- Cleanup - charinfo nicht mehr benötigt
license.holder_charinfo = nil
license.issued_by_charinfo = nil
debugPrint("Lizenz aus DB geladen: " .. license.license_type)
-- In Cache speichern
setCachedLicense(citizenid, licenseType, license)
return license
end
debugPrint("Keine Lizenz in DB gefunden")
return nil
end
-- Alle Lizenzen eines Spielers abrufen (KORRIGIERT für MariaDB)
local function getAllPlayerLicenses(citizenid)
debugPrint("=== getAllPlayerLicenses START ===")
debugPrint("CitizenID: " .. tostring(citizenid))
-- MariaDB-kompatible Query
local query = [[
SELECT pl.*,
p.charinfo as holder_charinfo,
pi.charinfo as issued_by_charinfo
FROM player_licenses pl
LEFT JOIN players p ON p.citizenid = pl.citizenid
LEFT JOIN players pi ON pi.citizenid = pl.issued_by
WHERE pl.citizenid = ?
ORDER BY pl.license_type, pl.created_at DESC
]]
local result = MySQL.query.await(query, {citizenid})
if result and #result > 0 then
local licenses = {}
local seenTypes = {}
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
-- Namen aus JSON extrahieren
license.holder_name = extractPlayerName(license.holder_charinfo)
license.issued_by_name = extractPlayerName(license.issued_by_charinfo)
-- Fallback für issued_by_name
if license.issued_by_name == "Unbekannt" then
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.classes = {}
end
-- Cleanup
license.holder_charinfo = nil
license.issued_by_charinfo = nil
table.insert(licenses, license)
end
end
debugPrint("Gefundene Lizenzen: " .. #licenses)
return licenses
end
debugPrint("Keine Lizenzen gefunden")
return {}
end
-- Lizenz in Datenbank speichern (KORRIGIERT)
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
-- Spieler-Name für das name-Feld abrufen (MariaDB-kompatibel)
local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
local holderResult = MySQL.query.await(holderQuery, {citizenid})
local holderName = "Unbekannt"
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 {})
-- Alte Lizenz 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, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
]]
local insertData = {
citizenid,
licenseType,
holderName,
issueDate,
expireDate,
issuedBy,
1,
classesJson,
os.time()
}
debugPrint("Führe INSERT-Query aus...")
debugPrint("Daten: " .. json.encode(insertData))
local success, result = pcall(MySQL.insert.await, insertQuery, insertData)
if success and result then
debugPrint("Lizenz erfolgreich gespeichert. ID: " .. result)
-- Cache invalidieren
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = nil
return true
else
debugPrint("^1Fehler beim Speichern der Lizenz: " .. tostring(result) .. "^7")
return false
end
end
-- Lizenz entziehen
local function revokeLicenseInDB(citizenid, licenseType)
debugPrint("=== revokeLicenseInDB START ===")
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 success, result = pcall(MySQL.query.await, query, {citizenid, licenseType})
if success and result then
debugPrint("Lizenz erfolgreich entzogen")
-- Cache invalidieren
local cacheKey = citizenid .. "_" .. licenseType
licenseCache[cacheKey] = nil
return true
else
debugPrint("^1Fehler beim Entziehen der Lizenz: " .. tostring(result) .. "^7")
return false
end
end
-- Erweiterte Fehlerbehandlung für Datenbankoperationen
local function safeDBOperation(operation, errorMessage)
local success, result = pcall(operation)
if not success then
debugPrint("^1DB-Fehler: " .. (errorMessage or "Unbekannt") .. "^7")
debugPrint("^1Details: " .. tostring(result) .. "^7")
return nil
end
return result
end
-- EVENT HANDLER: Lizenz anfordern
RegisterNetEvent('license-system:server:requestLicense', function(targetId)
local src = source
@ -375,9 +414,7 @@ RegisterNetEvent('license-system:server:requestLicense', function(targetId)
-- Erste verfügbare Lizenz finden
local foundLicense = nil
for licenseType, _ in pairs(Config.LicenseTypes) do
local license = safeDBOperation(function()
return getLicenseFromDB(citizenid, licenseType)
end, "Fehler beim Abrufen der Lizenz")
local license = getLicenseFromDB(citizenid, licenseType)
if license and license.is_active == 1 then
foundLicense = {
@ -411,9 +448,7 @@ RegisterNetEvent('license-system:server:requestMyLicense', function(licenseType)
end
local citizenid = Player.PlayerData.citizenid
local license = safeDBOperation(function()
return getLicenseFromDB(citizenid, licenseType)
end, "Fehler beim Abrufen der eigenen Lizenz")
local license = getLicenseFromDB(citizenid, licenseType)
if license and license.is_active == 1 then
local licenseData = {
@ -449,10 +484,7 @@ RegisterNetEvent('license-system:server:requestPlayerLicenses', function(targetI
local citizenid = targetPlayer.PlayerData.citizenid
local targetName = getPlayerName(targetId)
local licenses = safeDBOperation(function()
return getAllPlayerLicenses(citizenid)
end, "Fehler beim Abrufen aller Spieler-Lizenzen") or {}
local licenses = getAllPlayerLicenses(citizenid)
debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, licenses, targetId, targetName)
@ -487,9 +519,7 @@ RegisterNetEvent('license-system:server:issueLicense', function(targetId, licens
local issuerCitizenId = issuerPlayer.PlayerData.citizenid
-- Prüfen ob Lizenz bereits existiert
local existingLicense = safeDBOperation(function()
return getLicenseFromDB(targetCitizenId, licenseType)
end, "Fehler beim Prüfen bestehender Lizenz")
local existingLicense = getLicenseFromDB(targetCitizenId, licenseType)
if existingLicense and existingLicense.is_active == 1 then
debugPrint("Lizenz bereits vorhanden und aktiv")
@ -512,9 +542,7 @@ RegisterNetEvent('license-system:server:issueLicense', function(targetId, licens
end
-- Lizenz in Datenbank speichern
local success = safeDBOperation(function()
return saveLicenseToDB(targetCitizenId, licenseType, issuerCitizenId, classes)
end, "Fehler beim Speichern der Lizenz")
local success = saveLicenseToDB(targetCitizenId, licenseType, issuerCitizenId, classes)
if success then
local targetName = getPlayerName(targetId)
@ -560,9 +588,7 @@ RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licen
local targetCitizenId = targetPlayer.PlayerData.citizenid
-- Lizenz entziehen
local success = safeDBOperation(function()
return revokeLicenseInDB(targetCitizenId, licenseType)
end, "Fehler beim Entziehen der Lizenz")
local success = revokeLicenseInDB(targetCitizenId, licenseType)
if success then
local targetName = getPlayerName(targetId)
@ -587,26 +613,11 @@ RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licen
end
end)
-- EVENT HANDLER: Foto speichern
RegisterNetEvent('license-system:server:savePhoto', function(citizenid, photoData)
local src = source
debugPrint("=== Event: savePhoto ===")
debugPrint("Source: " .. src .. ", CitizenID: " .. citizenid)
-- Hier könnte das Foto in der Datenbank oder im Dateisystem gespeichert werden
debugPrint("Foto-Daten erhalten für: " .. citizenid)
TriggerClientEvent('QBCore:Notify', src, 'Foto gespeichert!', 'success')
end)
-- EXPORT FUNKTIONEN
exports('hasLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return false end
local license = safeDBOperation(function()
return getLicenseFromDB(citizenid, licenseType)
end, "Fehler beim Prüfen der Lizenz")
local license = getLicenseFromDB(citizenid, licenseType)
return license and license.is_active == 1
end)
@ -614,38 +625,30 @@ exports('issueLicense', function(citizenid, licenseType, issuedBy, classes)
if not citizenid or not licenseType then return false end
issuedBy = issuedBy or 'system'
return safeDBOperation(function()
return saveLicenseToDB(citizenid, licenseType, issuedBy, classes)
end, "Fehler beim Ausstellen der Lizenz") or false
return saveLicenseToDB(citizenid, licenseType, issuedBy, classes)
end)
exports('revokeLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return false end
return safeDBOperation(function()
return revokeLicenseInDB(citizenid, licenseType)
end, "Fehler beim Entziehen der Lizenz") or false
return revokeLicenseInDB(citizenid, licenseType)
end)
exports('getPlayerLicenses', function(citizenid)
if not citizenid then return {} end
return safeDBOperation(function()
return getAllPlayerLicenses(citizenid)
end, "Fehler beim Abrufen der Spieler-Lizenzen") or {}
return getAllPlayerLicenses(citizenid)
end)
exports('getPlayerLicense', function(citizenid, licenseType)
if not citizenid or not licenseType then return nil end
return safeDBOperation(function()
return getLicenseFromDB(citizenid, licenseType)
end, "Fehler beim Abrufen der Lizenz")
return getLicenseFromDB(citizenid, licenseType)
end)
-- INITIALISIERUNG
CreateThread(function()
debugPrint("License-System Server gestartet (MariaDB-kompatibel)")
debugPrint("License-System Server gestartet (Collation & DateTime Fix)")
-- Warten bis QBCore geladen ist
while not QBCore do
@ -703,20 +706,4 @@ RegisterCommand('licenseclearcache', function(source, args, rawCommand)
end
end, true)
-- Erweiterte Logging-Funktion
local function logLicenseAction(action, src, targetId, licenseType, details)
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local srcName = getPlayerName(src)
local targetName = targetId and getPlayerName(targetId) or "N/A"
local logMessage = string.format(
"[%s] %s | Quelle: %s (%s) | Ziel: %s (%s) | Typ: %s | Details: %s",
timestamp, action, srcName, src, targetName, targetId or "N/A", licenseType or "N/A", details or "N/A"
)
debugPrint("LOG: " .. logMessage)
-- Hier könnte das Log in eine Datei oder Datenbank geschrieben werden
end
debugPrint("License-System Server vollständig geladen (MariaDB-kompatibel)")
debugPrint("License-System Server vollständig geladen (Collation & DateTime Fix)")