1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_license/client/main.lua
2025-08-04 09:48:26 +02:00

1450 lines
47 KiB
Lua

local QBCore = exports['qb-core']:GetCoreObject()
-- Lokale Variablen
local isMenuOpen = false
local currentTarget = nil
local nearbyPlayers = {}
local isLicenseShowing = false
local pendingRequests = {}
local currentLicenseData = nil
local currentTargetId = nil
-- Hilfsfunktionen
local function debugPrint(message)
if Config.Debug then
print("^3[License-System Client] " .. message .. "^7")
end
end
local function showNotification(message, type)
QBCore.Functions.Notify(message, type or 'primary')
end
-- Nearby Players abrufen
local function getNearbyPlayers(radius)
radius = radius or 5.0
local players = {}
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
for _, playerId in ipairs(GetActivePlayers()) do
local targetPed = GetPlayerPed(playerId)
if targetPed ~= playerPed then
local targetCoords = GetEntityCoords(targetPed)
local distance = #(playerCoords - targetCoords)
if distance <= radius then
local serverId = GetPlayerServerId(playerId)
local playerName = GetPlayerName(playerId)
table.insert(players, {
id = serverId,
name = playerName,
distance = math.floor(distance * 100) / 100,
ped = targetPed
})
end
end
end
-- Nach Entfernung sortieren
table.sort(players, function(a, b)
return a.distance < b.distance
end)
debugPrint("Gefundene Spieler in der Nähe: " .. #players)
return players
end
-- Berechtigung prüfen
local function hasPermission()
local PlayerData = QBCore.Functions.GetPlayerData()
if not PlayerData or not PlayerData.job then return false end
local hasAuth = Config.AuthorizedJobs[PlayerData.job.name] or false
debugPrint("Berechtigung für Job " .. PlayerData.job.name .. ": " .. tostring(hasAuth))
return hasAuth
end
-- Lizenz anzeigen
local function showLicense(licenseData)
if not licenseData then
showNotification(Config.Notifications.license_not_found.message, Config.Notifications.license_not_found.type)
return
end
debugPrint("Zeige Lizenz: " .. licenseData.license.license_type)
SendNUIMessage({
action = 'showLicense',
data = licenseData
})
SetNuiFocus(true, true)
isLicenseShowing = true
end
-- Lizenz schließen
local function closeLicense()
SendNUIMessage({
action = 'hideLicense'
})
SetNuiFocus(false, false)
isLicenseShowing = false
debugPrint("Lizenz geschlossen")
end
-- Spieler-Lizenz anzeigen
local function showPlayerLicense(targetId)
debugPrint("=== showPlayerLicense START ===")
debugPrint("Sende Event: requestLicense für Spieler: " .. tostring(targetId))
TriggerServerEvent('license-system:server:requestLicense', targetId)
end
-- Eigene Lizenz anzeigen
local function showMyLicense(licenseType)
debugPrint("=== showMyLicense START ===")
debugPrint("Sende Event: requestMyLicense für Typ: " .. tostring(licenseType))
TriggerServerEvent('license-system:server:requestMyLicense', licenseType)
end
-- FORWARD DECLARATIONS (Funktionen die später definiert werden)
local confirmIssueLicense
local openIssueLicenseMenu
local openDriversLicenseClassMenu
local openRevokeLicenseMenu
local openPlayerLicenseMenu
local openLicenseMenu
local openManualLicenseEntry
local openReactivateLicenseMenu
-- Helper function to get license type options
local function getLicenseTypeOptions()
local options = {}
for licenseType, config in pairs(Config.LicenseTypes) do
table.insert(options, {
value = licenseType,
label = config.label
})
end
return options
end
-- Lizenz-Ausstellung bestätigen (FRÜH DEFINIERT)
confirmIssueLicense = function(targetId, targetName, licenseType, classes)
local config = Config.LicenseTypes[licenseType]
if not config then
showNotification('Unbekannter Lizenztyp!', 'error')
return
end
local classText = classes and table.concat(classes, ', ') or 'Keine'
local priceText = config.price and (config.price .. ' $') or 'Kostenlos'
debugPrint("Bestätige Lizenz-Ausstellung: " .. licenseType .. " für " .. targetName)
lib.registerContext({
id = 'confirm_issue_license',
title = 'Lizenz ausstellen bestätigen',
options = {
{
title = 'Spieler: ' .. targetName,
disabled = true,
icon = 'fas fa-user'
},
{
title = 'Lizenztyp: ' .. config.label,
disabled = true,
icon = config.icon
},
{
title = 'Klassen: ' .. classText,
disabled = true,
icon = 'fas fa-list'
},
{
title = 'Kosten: ' .. priceText,
disabled = true,
icon = 'fas fa-dollar-sign'
},
{
title = '─────────────────',
disabled = true
},
{
title = '✅ Bestätigen',
description = 'Lizenz jetzt ausstellen',
icon = 'fas fa-check',
onSelect = function()
debugPrint("Sende Lizenz-Ausstellung an Server...")
TriggerServerEvent('license-system:server:issueLicense', targetId, licenseType, classes)
lib.hideContext()
end
},
{
title = '❌ Abbrechen',
description = 'Vorgang abbrechen',
icon = 'fas fa-times',
onSelect = function()
openIssueLicenseMenu(targetId, targetName)
end
}
}
})
lib.showContext('confirm_issue_license')
end
-- Führerschein-Klassen Menü
openDriversLicenseClassMenu = function(targetId, targetName, licenseType)
local config = Config.LicenseTypes[licenseType]
if not config then
showNotification('Lizenz-Konfiguration nicht gefunden!', 'error')
return
end
local selectedClasses = {}
local function updateMenu()
local menuOptions = {}
if config.classes then
for _, class in ipairs(config.classes) do
local isSelected = false
for _, selected in ipairs(selectedClasses) do
if selected == class then
isSelected = true
break
end
end
local classDescriptions = {
['A'] = 'Motorräder',
['A1'] = 'Leichte Motorräder (bis 125ccm)',
['A2'] = 'Mittlere Motorräder (bis 35kW)',
['B'] = 'PKW (bis 3,5t)',
['BE'] = 'PKW mit Anhänger',
['C'] = 'LKW (über 3,5t)',
['CE'] = 'LKW mit Anhänger',
['D'] = 'Bus (über 8 Personen)',
['DE'] = 'Bus mit Anhänger'
}
table.insert(menuOptions, {
title = 'Klasse ' .. class .. (isSelected and '' or ''),
description = classDescriptions[class] or 'Keine Beschreibung',
icon = isSelected and 'fas fa-check-square' or 'far fa-square',
onSelect = function()
if isSelected then
-- Klasse entfernen
for i, selected in ipairs(selectedClasses) do
if selected == class then
table.remove(selectedClasses, i)
break
end
end
else
-- Klasse hinzufügen
table.insert(selectedClasses, class)
end
updateMenu()
end
})
end
end
table.insert(menuOptions, {
title = '─────────────────',
disabled = true
})
table.insert(menuOptions, {
title = 'Bestätigen (' .. #selectedClasses .. ' Klassen)',
description = 'Führerschein mit ausgewählten Klassen ausstellen',
icon = 'fas fa-check',
disabled = #selectedClasses == 0,
onSelect = function()
confirmIssueLicense(targetId, targetName, licenseType, selectedClasses)
end
})
table.insert(menuOptions, {
title = '← Zurück',
icon = 'fas fa-arrow-left',
onSelect = function()
openIssueLicenseMenu(targetId, targetName)
end
})
lib.registerContext({
id = 'drivers_license_classes',
title = 'Führerschein-Klassen: ' .. targetName,
options = menuOptions
})
lib.showContext('drivers_license_classes')
end
updateMenu()
end
-- Lizenz ausstellen Menü
openIssueLicenseMenu = function(targetId, targetName)
debugPrint("Öffne Lizenz-Ausstellungs-Menü für: " .. targetName)
local menuOptions = {}
for licenseType, config in pairs(Config.LicenseTypes) do
local priceText = config.price and (config.price .. ' $') or 'Kostenlos'
local validityText = config.validity_days and (config.validity_days .. ' Tage') or 'Unbegrenzt'
table.insert(menuOptions, {
title = config.label,
description = config.description or 'Keine Beschreibung verfügbar',
icon = config.icon,
onSelect = function()
if licenseType == 'drivers_license' and config.classes then
openDriversLicenseClassMenu(targetId, targetName, licenseType)
else
confirmIssueLicense(targetId, targetName, licenseType, nil)
end
end,
metadata = {
{label = 'Preis', value = priceText},
{label = 'Gültigkeitsdauer', value = validityText}
}
})
end
table.insert(menuOptions, {
title = '← Zurück',
icon = 'fas fa-arrow-left',
onSelect = function()
openPlayerLicenseMenu(targetId, targetName)
end
})
lib.registerContext({
id = 'issue_license',
title = 'Lizenz ausstellen: ' .. targetName,
options = menuOptions
})
lib.showContext('issue_license')
end
-- Lizenz entziehen Menü
openRevokeLicenseMenu = function(targetId, targetName, licenses)
debugPrint("Öffne Lizenz-Entziehungs-Menü für: " .. targetName)
local menuOptions = {}
for _, license in ipairs(licenses) do
if license.is_active == 1 then
local config = Config.LicenseTypes[license.license_type] or {
label = license.license_type,
icon = 'fas fa-id-card'
}
table.insert(menuOptions, {
title = config.label,
description = 'Diese Lizenz entziehen',
icon = config.icon,
onSelect = function()
lib.registerContext({
id = 'confirm_revoke_license',
title = 'Lizenz entziehen bestätigen',
options = {
{
title = 'Spieler: ' .. targetName,
disabled = true,
icon = 'fas fa-user'
},
{
title = 'Lizenztyp: ' .. config.label,
disabled = true,
icon = config.icon
},
{
title = '─────────────────',
disabled = true
},
{
title = '✅ Bestätigen',
description = 'Lizenz jetzt entziehen',
icon = 'fas fa-check',
onSelect = function()
debugPrint("Sende Lizenz-Entziehung an Server...")
TriggerServerEvent('license-system:server:revokeLicense', targetId, license.license_type)
lib.hideContext()
end
},
{
title = '❌ Abbrechen',
description = 'Vorgang abbrechen',
icon = 'fas fa-times',
onSelect = function()
openRevokeLicenseMenu(targetId, targetName, licenses)
end
}
}
})
lib.showContext('confirm_revoke_license')
end
})
end
end
if #menuOptions == 0 then
table.insert(menuOptions, {
title = 'Keine aktiven Lizenzen',
description = 'Dieser Spieler hat keine aktiven Lizenzen',
icon = 'fas fa-exclamation-triangle',
disabled = true
})
end
table.insert(menuOptions, {
title = '← Zurück',
icon = 'fas fa-arrow-left',
onSelect = function()
openPlayerLicenseMenu(targetId, targetName)
end
})
lib.registerContext({
id = 'revoke_license',
title = 'Lizenz entziehen: ' .. targetName,
options = menuOptions
})
lib.showContext('revoke_license')
end
-- Manual License Entry Menu
openManualLicenseEntry = function(targetId, targetName)
debugPrint("Öffne manuelle Lizenz-Eingabe für: " .. targetName)
local input = lib.inputDialog('Lizenz manuell ausstellen', {
{type = 'select', label = 'Lizenztyp', options = getLicenseTypeOptions()},
{type = 'input', label = 'Name', default = targetName},
{type = 'input', label = 'Geburtsdatum', description = 'Format: DD.MM.YYYY'},
{type = 'select', label = 'Geschlecht', options = {
{value = 'male', label = 'Männlich'},
{value = 'female', label = 'Weiblich'},
{value = 'other', label = 'Divers'}
}},
{type = 'date', label = 'Ausstellungsdatum', default = os.date("%Y-%m-%d")},
{type = 'date', label = 'Ablaufdatum', default = os.date("%Y-%m-%d", os.time() + 365*24*60*60)},
{type = 'checkbox', label = 'Foto aufnehmen?'}
})
if not input then return end
local licenseType = input[1]
local licenseData = {
name = input[2],
birthday = input[3],
gender = input[4],
issue_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[5] / 1000))),
expire_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[6] / 1000))),
license_type = licenseType
}
if input[7] then -- Take photo
TriggerEvent('license-system:client:openCamera', targetId, licenseData)
else
TriggerServerEvent('license-system:server:issueManualLicense', targetId, licenseData)
end
end
-- License Reactivation Menu
openReactivateLicenseMenu = function(targetId, targetName)
debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName)
-- Request all licenses including inactive ones
TriggerServerEvent('license-system:server:requestAllPlayerLicenses', targetId, true)
end
-- Spieler-Lizenz-Menü
openPlayerLicenseMenu = function(targetId, targetName)
debugPrint("=== openPlayerLicenseMenu START ===")
debugPrint("Sende Event: requestPlayerLicenses für: " .. targetName .. " (ID: " .. targetId .. ")")
TriggerServerEvent('license-system:server:requestPlayerLicenses', targetId)
end
-- Hauptmenü für Lizenz-System
openLicenseMenu = function()
debugPrint("Öffne Hauptmenü für Lizenz-System")
if not hasPermission() then
showNotification(Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
return
end
nearbyPlayers = getNearbyPlayers(5.0)
if #nearbyPlayers == 0 then
showNotification(Config.Notifications.no_players_nearby.message, Config.Notifications.no_players_nearby.type)
return
end
local menuOptions = {}
for _, player in ipairs(nearbyPlayers) do
table.insert(menuOptions, {
title = player.name,
description = 'Entfernung: ' .. player.distance .. 'm',
icon = 'fas fa-user',
onSelect = function()
openPlayerLicenseMenu(player.id, player.name)
end
})
end
lib.registerContext({
id = 'license_nearby_players',
title = 'Spieler in der Nähe (' .. #nearbyPlayers .. ')',
options = menuOptions
})
lib.showContext('license_nearby_players')
end
-- Eigene Lizenzen anzeigen
local function showMyLicenses()
debugPrint("Öffne Menü für eigene Lizenzen")
local menuOptions = {}
for licenseType, config in pairs(Config.LicenseTypes) do
table.insert(menuOptions, {
title = config.label,
description = 'Deine ' .. config.label .. ' anzeigen',
icon = config.icon,
onSelect = function()
showMyLicense(licenseType)
end
})
end
lib.registerContext({
id = 'my_licenses',
title = 'Meine Lizenzen',
options = menuOptions
})
lib.showContext('my_licenses')
end
-- EVENT HANDLER: Einzelne Lizenz erhalten
RegisterNetEvent('license-system:client:receiveLicense', function(licenseData)
debugPrint("=== Event: receiveLicense ===")
debugPrint("LicenseData-Typ: " .. type(licenseData))
if licenseData then
debugPrint("Lizenz-Daten erhalten: " .. licenseData.license.license_type)
showLicense(licenseData)
else
debugPrint("Keine Lizenz-Daten erhalten")
showNotification(Config.Notifications.license_not_found.message, Config.Notifications.license_not_found.type)
end
end)
-- EVENT HANDLER: Eigene Lizenz erhalten
RegisterNetEvent('license-system:client:receiveMyLicense', function(licenseData, licenseType)
debugPrint("=== Event: receiveMyLicense ===")
debugPrint("LicenseType: " .. tostring(licenseType))
debugPrint("LicenseData-Typ: " .. type(licenseData))
if licenseData then
debugPrint("Eigene Lizenz-Daten erhalten: " .. licenseData.license.license_type)
showLicense(licenseData)
else
debugPrint("Keine eigene Lizenz gefunden")
local config = Config.LicenseTypes[licenseType]
local licenseName = config and config.label or licenseType
showNotification('Du hast keine ' .. licenseName .. '!', 'error')
end
end)
-- EVENT HANDLER: Alle Spieler-Lizenzen erhalten
RegisterNetEvent('license-system:client:receivePlayerLicenses', function(licenses, targetId, targetName)
debugPrint("=== Event: receivePlayerLicenses ===")
debugPrint("Erhaltene Lizenzen: " .. #licenses)
debugPrint("TargetName: " .. tostring(targetName))
local menuOptions = {}
if licenses and #licenses > 0 then
for _, license in ipairs(licenses) do
local licenseConfig = Config.LicenseTypes[license.license_type] or {
label = license.license_type,
icon = 'fas fa-id-card',
color = '#667eea'
}
local statusIcon = (license.is_active == 1) and '' or ''
local statusText = (license.is_active == 1) and 'Gültig' or 'Ungültig'
local expireText = license.expire_date or 'Unbegrenzt'
table.insert(menuOptions, {
title = licenseConfig.label .. ' ' .. statusIcon,
description = 'Status: ' .. statusText .. ' | Gültig bis: ' .. expireText,
icon = licenseConfig.icon,
onSelect = function()
local licenseData = {
license = license,
config = licenseConfig
}
showLicense(licenseData)
end,
metadata = {
{label = 'Status', value = statusText},
{label = 'Ausgestellt', value = license.issue_date or 'Unbekannt'},
{label = 'Gültig bis', value = expireText},
{label = 'Aussteller', value = license.issued_by_name or 'System'}
}
})
end
else
table.insert(menuOptions, {
title = 'Keine Lizenzen gefunden',
description = 'Dieser Spieler hat keine Lizenzen',
icon = 'fas fa-exclamation-triangle',
disabled = true
})
end
-- Aktionen hinzufügen
table.insert(menuOptions, {
title = '─────────────────',
disabled = true
})
table.insert(menuOptions, {
title = 'Neue Lizenz ausstellen',
description = 'Eine neue Lizenz für diesen Spieler ausstellen',
icon = 'fas fa-plus',
onSelect = function()
openIssueLicenseMenu(targetId, targetName)
end
})
table.insert(menuOptions, {
title = 'Lizenz manuell ausstellen',
description = 'Lizenz mit benutzerdefinierten Daten ausstellen',
icon = 'fas fa-edit',
onSelect = function()
openManualLicenseEntry(targetId, targetName)
end
})
if licenses and #licenses > 0 then
table.insert(menuOptions, {
title = 'Lizenz entziehen',
description = 'Eine bestehende Lizenz entziehen',
icon = 'fas fa-minus',
onSelect = function()
openRevokeLicenseMenu(targetId, targetName, licenses)
end
})
end
table.insert(menuOptions, {
title = 'Lizenz reaktivieren',
description = 'Eine inaktive Lizenz wieder aktivieren',
icon = 'fas fa-redo',
onSelect = function()
openReactivateLicenseMenu(targetId, targetName)
end
})
table.insert(menuOptions, {
title = '← Zurück',
icon = 'fas fa-arrow-left',
onSelect = function()
openLicenseMenu()
end
})
lib.registerContext({
id = 'player_licenses',
title = 'Lizenzen: ' .. targetName,
options = menuOptions
})
lib.showContext('player_licenses')
end)
-- EVENT HANDLER: Alle Spieler-Lizenzen (inkl. inaktive) erhalten
RegisterNetEvent('license-system:client:receiveAllPlayerLicenses', function(licenses, targetId, targetName)
debugPrint("=== Event: receiveAllPlayerLicenses ===")
debugPrint("Erhaltene Lizenzen: " .. #licenses)
local menuOptions = {}
if licenses and #licenses > 0 then
for _, license in ipairs(licenses) do
if license.is_active == 0 then -- Only show inactive licenses
local licenseConfig = Config.LicenseTypes[license.license_type] or {
label = license.license_type,
icon = 'fas fa-id-card',
color = '#667eea'
}
table.insert(menuOptions, {
title = licenseConfig.label .. '',
description = 'Status: Ungültig | Ausgestellt: ' .. (license.issue_date or 'Unbekannt'),
icon = licenseConfig.icon,
onSelect = function()
-- Confirmation dialog
lib.registerContext({
id = 'confirm_reactivate_license',
title = 'Lizenz reaktivieren bestätigen',
options = {
{
title = 'Spieler: ' .. targetName,
disabled = true,
icon = 'fas fa-user'
},
{
title = 'Lizenztyp: ' .. licenseConfig.label,
disabled = true,
icon = licenseConfig.icon
},
{
title = '─────────────────',
disabled = true
},
{
title = '✅ Bestätigen',
description = 'Lizenz jetzt reaktivieren',
icon = 'fas fa-check',
onSelect = function()
debugPrint("Sende Lizenz-Reaktivierung an Server...")
TriggerServerEvent('license-system:server:reactivateLicense', targetId, license.license_type)
lib.hideContext()
end
},
{
title = '❌ Abbrechen',
description = 'Vorgang abbrechen',
icon = 'fas fa-times',
onSelect = function()
openReactivateLicenseMenu(targetId, targetName)
end
}
}
})
lib.showContext('confirm_reactivate_license')
end
})
end
end
end
if #menuOptions == 0 then
table.insert(menuOptions, {
title = 'Keine inaktiven Lizenzen',
description = 'Dieser Spieler hat keine inaktiven Lizenzen',
icon = 'fas fa-exclamation-triangle',
disabled = true
})
end
table.insert(menuOptions, {
title = '← Zurück',
icon = 'fas fa-arrow-left',
onSelect = function()
openPlayerLicenseMenu(targetId, targetName)
end
})
lib.registerContext({
id = 'reactivate_license',
title = 'Lizenz reaktivieren: ' .. targetName,
options = menuOptions
})
lib.showContext('reactivate_license')
end)
-- EVENT HANDLER: Berechtigung erhalten
RegisterNetEvent('license-system:client:receivePermission', function(hasAuth, licenseType)
debugPrint("=== Event: receivePermission ===")
debugPrint("Berechtigung für " .. licenseType .. ": " .. tostring(hasAuth))
if not hasAuth then
showNotification(Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
end
end)
-- EVENT HANDLER: Lizenz erfolgreich ausgestellt
RegisterNetEvent('license-system:client:licenseIssued', function(targetId, licenseType)
debugPrint("=== Event: licenseIssued ===")
debugPrint("Lizenz " .. licenseType .. " für Spieler " .. targetId .. " ausgestellt")
-- Menü aktualisieren
if lib.getOpenContextMenu() then
lib.hideContext()
Wait(100)
openLicenseMenu()
end
end)
-- EVENT HANDLER: Lizenz erfolgreich entzogen
RegisterNetEvent('license-system:client:licenseRevoked', function(targetId, licenseType)
debugPrint("=== Event: licenseRevoked ===")
debugPrint("Lizenz " .. licenseType .. " für Spieler " .. targetId .. " entzogen")
-- Menü aktualisieren
if lib.getOpenContextMenu() then
lib.hideContext()
Wait(100)
openLicenseMenu()
end
end)
-- EVENT HANDLER: Lizenz erfolgreich reaktiviert
RegisterNetEvent('license-system:client:licenseReactivated', function(targetId, licenseType)
debugPrint("=== Event: licenseReactivated ===")
debugPrint("Lizenz " .. licenseType .. " für Spieler " .. targetId .. " reaktiviert")
-- Menü aktualisieren
if lib.getOpenContextMenu() then
lib.hideContext()
Wait(100)
openLicenseMenu()
end
end)
-- EVENT HANDLER: Lizenz anzeigen (von anderen Spielern)
RegisterNetEvent('license-system:client:showLicense', function(targetId)
debugPrint("Event erhalten: showLicense für ID " .. tostring(targetId))
showPlayerLicense(targetId)
end)
-- EVENT HANDLER: Eigene Lizenz anzeigen
RegisterNetEvent('license-system:client:showMyLicense', function(licenseType)
debugPrint("Event erhalten: showMyLicense für Typ " .. tostring(licenseType))
showMyLicense(licenseType)
end)
-- EVENT HANDLER: Kamera öffnen
RegisterNetEvent('license-system:client:openCamera', function(targetId, licenseData)
debugPrint("Event erhalten: openCamera für Spieler " .. tostring(targetId))
-- Store the data temporarily
currentLicenseData = licenseData
currentTargetId = targetId
SendNUIMessage({
action = 'openCamera',
citizenid = QBCore.Functions.GetPlayerData().citizenid
})
end)
-- EVENT HANDLER: Menü aktualisieren
RegisterNetEvent('license-system:client:refreshMenu', function()
debugPrint("Event erhalten: refreshMenu")
if lib.getOpenContextMenu() then
lib.hideContext()
Wait(100)
openLicenseMenu()
end
end)
-- NUI CALLBACKS
RegisterNUICallback('closeLicense', function(data, cb)
debugPrint("NUI Callback: closeLicense")
closeLicense()
if cb and type(cb) == "function" then
cb('ok')
end
end)
RegisterNUICallback('savePhoto', function(data, cb)
debugPrint("NUI Callback: savePhoto")
if data.photo and currentLicenseData and currentTargetId then
-- Add photo to license data
currentLicenseData.photo_url = data.photo
-- Issue the license with the photo
TriggerServerEvent('license-system:server:issueManualLicense', currentTargetId, currentLicenseData)
-- Reset temporary data
currentLicenseData = nil
currentTargetId = nil
if cb and type(cb) == "function" then
cb('ok')
end
else
debugPrint("^1Fehler: Foto-Daten unvollständig oder keine aktuelle Lizenz-Ausstellung^7")
if cb and type(cb) == "function" then
cb('error')
end
end
end)
RegisterNUICallback('takePicture', function(data, cb)
debugPrint("NUI Callback: takePicture")
-- Hier könnte eine Kamera-Funktion implementiert werden
if cb and type(cb) == "function" then
cb('ok')
end
end)
-- COMMANDS
RegisterCommand(Config.Commands.license.name, function()
debugPrint("Command ausgeführt: " .. Config.Commands.license.name)
openLicenseMenu()
end, Config.Commands.license.restricted)
RegisterCommand(Config.Commands.mylicense.name, function()
debugPrint("Command ausgeführt: " .. Config.Commands.mylicense.name)
showMyLicenses()
end, Config.Commands.mylicense.restricted)
-- Zusätzliche Commands für schnellen Zugriff
RegisterCommand('ausweis', function()
debugPrint("Command ausgeführt: ausweis")
showMyLicense('id_card')
end, false)
RegisterCommand('führerschein', function()
debugPrint("Command ausgeführt: führerschein")
showMyLicense('drivers_license')
end, false)
RegisterCommand('waffenschein', function()
debugPrint("Command ausgeführt: waffenschein")
showMyLicense('weapon_license')
end, false)
RegisterCommand('pass', function()
debugPrint("Command ausgeführt: pass")
showMyLicense('passport')
end, false)
-- KEYBINDS
if Config.Keybinds and Config.Keybinds.open_license_menu then
RegisterKeyMapping(Config.Commands.license.name, Config.Keybinds.open_license_menu.description, 'keyboard', Config.Keybinds.open_license_menu.key)
end
if Config.Keybinds and Config.Keybinds.show_my_licenses then
RegisterKeyMapping(Config.Commands.mylicense.name, Config.Keybinds.show_my_licenses.description, 'keyboard', Config.Keybinds.show_my_licenses.key)
end
-- ESC-Taste zum Schließen der Lizenz
CreateThread(function()
while true do
Wait(0)
if isLicenseShowing then
if IsControlJustPressed(0, 322) then -- ESC-Taste
closeLicense()
end
else
Wait(500)
end
end
end)
-- CLEANUP UND INITIALISIERUNG
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
if isLicenseShowing then
closeLicense()
end
if lib.getOpenContextMenu() then
lib.hideContext()
end
-- Reset temporary data
currentLicenseData = nil
currentTargetId = nil
debugPrint("License-System Client gestoppt")
end
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
debugPrint("License-System Client gestartet (Event-basiert)")
-- Warten bis QBCore geladen ist
while not QBCore do
Wait(100)
end
debugPrint("QBCore erfolgreich geladen")
end
end)
-- Player laden Event
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
debugPrint("Spieler geladen - License-System bereit")
end)
-- Job Update Event
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo)
debugPrint("Job aktualisiert: " .. JobInfo.name)
end)
-- Initialisierung
CreateThread(function()
debugPrint("License-System Client Thread gestartet")
-- Warten bis Spieler gespawnt ist
while not NetworkIsPlayerActive(PlayerId()) do
Wait(100)
end
debugPrint("Spieler ist aktiv - System bereit")
end)
-- Zusätzliche Utility-Funktionen
local function requestLicenseWithTimeout(eventName, targetId, timeout)
timeout = timeout or 5000
local requestId = math.random(1000, 9999)
pendingRequests[requestId] = {
timestamp = GetGameTimer(),
timeout = timeout
}
debugPrint("Sende Request mit Timeout: " .. eventName .. " (ID: " .. requestId .. ")")
TriggerServerEvent(eventName, targetId, requestId)
-- Timeout-Handler
CreateThread(function()
Wait(timeout)
if pendingRequests[requestId] then
pendingRequests[requestId] = nil
debugPrint("^1Request Timeout: " .. eventName .. " (ID: " .. requestId .. ")^7")
showNotification('Anfrage-Timeout! Versuche es erneut.', 'error')
end
end)
return requestId
end
-- Erweiterte Error-Handling
local function safeExecute(func, errorMessage)
local success, error = pcall(func)
if not success then
debugPrint("^1Fehler: " .. (errorMessage or "Unbekannter Fehler") .. "^7")
debugPrint("^1Details: " .. tostring(error) .. "^7")
showNotification('Ein Fehler ist aufgetreten!', 'error')
end
return success
end
-- Performance-Monitoring
local performanceStats = {
menuOpens = 0,
licenseShows = 0,
errors = 0
}
CreateThread(function()
while true do
Wait(60000) -- Jede Minute
if Config.Debug then
debugPrint("=== Performance Stats ===")
debugPrint("Menü-Öffnungen: " .. performanceStats.menuOpens)
debugPrint("Lizenz-Anzeigen: " .. performanceStats.licenseShows)
debugPrint("Fehler: " .. performanceStats.errors)
end
end
end)
-- Stats aktualisieren
local originalOpenLicenseMenu = openLicenseMenu
openLicenseMenu = function()
performanceStats.menuOpens = performanceStats.menuOpens + 1
return originalOpenLicenseMenu()
end
local originalShowLicense = showLicense
showLicense = function(licenseData)
performanceStats.licenseShows = performanceStats.licenseShows + 1
return originalShowLicense(licenseData)
end
-- Erweiterte Fehlerbehandlung für Events
local function safeEventHandler(eventName, handler)
RegisterNetEvent(eventName, function(...)
local success, error = pcall(handler, ...)
if not success then
debugPrint("^1Fehler in Event " .. eventName .. ": " .. tostring(error) .. "^7")
performanceStats.errors = performanceStats.errors + 1
showNotification('Ein Fehler ist aufgetreten!', 'error')
end
end)
end
-- Zusätzliche Validierungen
local function validateLicenseData(licenseData)
if not licenseData then
debugPrint("^1Validierung fehlgeschlagen: licenseData ist nil^7")
return false
end
if not licenseData.license then
debugPrint("^1Validierung fehlgeschlagen: license-Objekt fehlt^7")
return false
end
if not licenseData.license.license_type then
debugPrint("^1Validierung fehlgeschlagen: license_type fehlt^7")
return false
end
return true
end
-- Erweiterte showLicense Funktion mit Validierung
local function showLicenseWithValidation(licenseData)
if not validateLicenseData(licenseData) then
showNotification('Ungültige Lizenz-Daten!', 'error')
return
end
debugPrint("Zeige Lizenz: " .. licenseData.license.license_type)
-- Zusätzliche Daten für NUI vorbereiten
local nuitData = {
license = licenseData.license,
config = licenseData.config or Config.LicenseTypes[licenseData.license.license_type],
timestamp = GetGameTimer(),
playerData = QBCore.Functions.GetPlayerData()
}
SendNUIMessage({
action = 'showLicense',
data = nuitData
})
SetNuiFocus(true, true)
isLicenseShowing = true
performanceStats.licenseShows = performanceStats.licenseShows + 1
end
-- Überschreibe die ursprüngliche showLicense Funktion
showLicense = showLicenseWithValidation
-- Erweiterte Menü-Funktionen mit Error-Handling
local function safeMenuAction(action, errorMessage)
return function(...)
local success, error = pcall(action, ...)
if not success then
debugPrint("^1Menü-Fehler: " .. (errorMessage or "Unbekannt") .. "^7")
debugPrint("^1Details: " .. tostring(error) .. "^7")
performanceStats.errors = performanceStats.errors + 1
showNotification('Menü-Fehler aufgetreten!', 'error')
-- Menü schließen bei Fehler
if lib.getOpenContextMenu() then
lib.hideContext()
end
end
end
end
-- Cache für Spieler-Daten
local playerCache = {}
local cacheTimeout = 30000 -- 30 Sekunden
local function getCachedPlayerData(playerId)
local now = GetGameTimer()
local cached = playerCache[playerId]
if cached and (now - cached.timestamp) < cacheTimeout then
debugPrint("Verwende gecachte Spieler-Daten für ID: " .. playerId)
return cached.data
end
return nil
end
local function setCachedPlayerData(playerId, data)
playerCache[playerId] = {
data = data,
timestamp = GetGameTimer()
}
debugPrint("Spieler-Daten gecacht für ID: " .. playerId)
end
-- Cache-Cleanup
CreateThread(function()
while true do
Wait(60000) -- Jede Minute
local now = GetGameTimer()
local cleaned = 0
for playerId, cached in pairs(playerCache) do
if (now - cached.timestamp) > cacheTimeout then
playerCache[playerId] = nil
cleaned = cleaned + 1
end
end
if cleaned > 0 then
debugPrint("Cache bereinigt: " .. cleaned .. " Einträge entfernt")
end
end
end)
-- Erweiterte Nearby-Players Funktion mit Cache
local function getNearbyPlayersWithCache(radius)
radius = radius or 5.0
local players = {}
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
for _, playerId in ipairs(GetActivePlayers()) do
local targetPed = GetPlayerPed(playerId)
if targetPed ~= playerPed then
local targetCoords = GetEntityCoords(targetPed)
local distance = #(playerCoords - targetCoords)
if distance <= radius then
local serverId = GetPlayerServerId(playerId)
local playerName = GetPlayerName(playerId)
-- Cache-Daten verwenden wenn verfügbar
local cachedData = getCachedPlayerData(serverId)
local playerData = cachedData or {
id = serverId,
name = playerName,
ped = targetPed
}
playerData.distance = math.floor(distance * 100) / 100
-- Daten cachen wenn nicht bereits gecacht
if not cachedData then
setCachedPlayerData(serverId, playerData)
end
table.insert(players, playerData)
end
end
end
-- Nach Entfernung sortieren
table.sort(players, function(a, b)
return a.distance < b.distance
end)
debugPrint("Gefundene Spieler in der Nähe: " .. #players)
return players
end
-- Überschreibe die ursprüngliche getNearbyPlayers Funktion
getNearbyPlayers = getNearbyPlayersWithCache
-- Erweiterte Notification-Funktion
local function showNotificationWithSound(message, type, sound)
QBCore.Functions.Notify(message, type or 'primary')
if sound and Config.Sounds and Config.Sounds[sound] then
PlaySoundFrontend(-1, Config.Sounds[sound].name, Config.Sounds[sound].set, true)
end
end
-- Erweiterte Event-Handler mit besserer Fehlerbehandlung
RegisterNetEvent('license-system:client:receiveLicenseWithValidation', function(licenseData)
debugPrint("=== Event: receiveLicenseWithValidation ===")
if not validateLicenseData(licenseData) then
showNotificationWithSound('Ungültige Lizenz-Daten erhalten!', 'error', 'error')
return
end
debugPrint("Gültige Lizenz-Daten erhalten: " .. licenseData.license.license_type)
showLicense(licenseData)
end)
-- Erweiterte Lizenz-Anzeige mit Animationen
local function showLicenseWithAnimation(licenseData)
if not validateLicenseData(licenseData) then
showNotification('Ungültige Lizenz-Daten!', 'error')
return
end
-- Animation starten
local playerPed = PlayerPedId()
-- Lizenz-Animation (falls gewünscht)
if Config.Animations and Config.Animations.show_license then
local anim = Config.Animations.show_license
RequestAnimDict(anim.dict)
while not HasAnimDictLoaded(anim.dict) do
Wait(10)
end
TaskPlayAnim(playerPed, anim.dict, anim.name, 8.0, -8.0, -1, anim.flag or 49, 0, false, false, false)
end
-- Lizenz anzeigen
showLicense(licenseData)
-- Animation nach kurzer Zeit stoppen
if Config.Animations and Config.Animations.show_license then
CreateThread(function()
Wait(2000)
ClearPedTasks(playerPed)
end)
end
end
-- Erweiterte Menü-Navigation
local menuHistory = {}
local function pushMenuHistory(menuId)
table.insert(menuHistory, menuId)
debugPrint("Menü-Historie: " .. menuId .. " hinzugefügt")
end
local function popMenuHistory()
if #menuHistory > 0 then
local lastMenu = table.remove(menuHistory)
debugPrint("Menü-Historie: " .. lastMenu .. " entfernt")
return lastMenu
end
return nil
end
local function clearMenuHistory()
menuHistory = {}
debugPrint("Menü-Historie geleert")
end
-- Erweiterte Cleanup-Funktion
local function cleanup()
debugPrint("Führe erweiterte Cleanup-Routine aus...")
-- NUI schließen
if isLicenseShowing then
closeLicense()
end
-- Menüs schließen
if lib.getOpenContextMenu() then
lib.hideContext()
end
-- Cache leeren
playerCache = {}
-- Historie leeren
clearMenuHistory()
-- Pending Requests leeren
pendingRequests = {}
-- Temporary data leeren
currentLicenseData = nil
currentTargetId = nil
-- Animationen stoppen
local playerPed = PlayerPedId()
if DoesEntityExist(playerPed) then
ClearPedTasks(playerPed)
end
debugPrint("Cleanup abgeschlossen")
end
-- Erweiterte Resource-Stop Handler
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
cleanup()
debugPrint("License-System Client gestoppt (erweitert)")
end
end)
-- Erweiterte Resource-Start Handler
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
debugPrint("License-System Client gestartet (erweitert)")
-- Initialisierung
CreateThread(function()
-- Warten bis QBCore geladen ist
while not QBCore do
Wait(100)
end
debugPrint("QBCore erfolgreich geladen (erweitert)")
-- Zusätzliche Initialisierung
Wait(1000)
-- Performance-Stats zurücksetzen
performanceStats = {
menuOpens = 0,
licenseShows = 0,
errors = 0
}
debugPrint("Erweiterte Initialisierung abgeschlossen")
end)
end
end)
-- Erweiterte Debug-Funktionen
local function debugPlayerInfo()
if not Config.Debug then return end
local PlayerData = QBCore.Functions.GetPlayerData()
debugPrint("=== PLAYER DEBUG INFO ===")
debugPrint("Name: " .. (PlayerData.charinfo and PlayerData.charinfo.firstname .. " " .. PlayerData.charinfo.lastname or "Unbekannt"))
debugPrint("Job: " .. (PlayerData.job and PlayerData.job.name or "Unbekannt"))
debugPrint("CitizenID: " .. (PlayerData.citizenid or "Unbekannt"))
debugPrint("Server ID: " .. GetPlayerServerId(PlayerId()))
debugPrint("========================")
end
-- Debug-Command
RegisterCommand('licensedebug', function()
if not Config.Debug then
showNotification('Debug-Modus ist deaktiviert!', 'error')
return
end
debugPlayerInfo()
debugPrint("=== SYSTEM DEBUG INFO ===")
debugPrint("Nearby Players: " .. #nearbyPlayers)
debugPrint("License Showing: " .. tostring(isLicenseShowing))
debugPrint("Menu Open: " .. tostring(lib.getOpenContextMenu() ~= nil))
debugPrint("Cached Players: " .. #playerCache)
debugPrint("Menu History: " .. #menuHistory)
debugPrint("Pending Requests: " .. #pendingRequests)
debugPrint("========================")
showNotification('Debug-Informationen in der Konsole ausgegeben!', 'success')
end, false)
-- Finaler Debug-Output
debugPrint("License-System Client vollständig geladen - Alle Funktionen verfügbar")