forked from Simnation/Main
ed
This commit is contained in:
parent
354019affc
commit
7932af55cb
3 changed files with 1311 additions and 715 deletions
|
@ -1,416 +1,595 @@
|
||||||
local QBCore = exports['qb-core']:GetCoreObject()
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
local playerLicenses = {}
|
|
||||||
|
-- Lokale Variablen
|
||||||
|
local isMenuOpen = false
|
||||||
|
local currentTarget = nil
|
||||||
local nearbyPlayers = {}
|
local nearbyPlayers = {}
|
||||||
|
|
||||||
-- Keybind registrieren
|
-- Hilfsfunktionen
|
||||||
RegisterKeyMapping(Config.Command, 'Lizenz-Menü öffnen', 'keyboard', Config.OpenKey)
|
local function debugPrint(message)
|
||||||
|
if Config.Debug then
|
||||||
|
print("^3[License-System Client] " .. message .. "^7")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Events
|
local function safeCallback(cb, ...)
|
||||||
RegisterNetEvent('license-system:client:openMenu', function()
|
if cb and type(cb) == "function" then
|
||||||
TriggerServerEvent('license-system:server:getLicenses')
|
cb(...)
|
||||||
end)
|
else
|
||||||
|
debugPrint("^1Callback ist keine Funktion!^7")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
RegisterNetEvent('license-system:client:receiveLicenses', function(licenses)
|
local function showNotification(message, type)
|
||||||
playerLicenses = licenses
|
QBCore.Functions.Notify(message, type or 'primary')
|
||||||
openMainMenu()
|
end
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:client:receiveNearbyPlayers', function(players)
|
-- Nearby Players abrufen
|
||||||
nearbyPlayers = {}
|
local function getNearbyPlayers(radius)
|
||||||
|
radius = radius or 5.0
|
||||||
|
local players = {}
|
||||||
local playerPed = PlayerPedId()
|
local playerPed = PlayerPedId()
|
||||||
local playerCoords = GetEntityCoords(playerPed)
|
local playerCoords = GetEntityCoords(playerPed)
|
||||||
|
|
||||||
for _, playerId in ipairs(players) do
|
for _, playerId in ipairs(GetActivePlayers()) do
|
||||||
if playerId ~= GetPlayerServerId(PlayerId()) then
|
local targetPed = GetPlayerPed(playerId)
|
||||||
local targetPed = GetPlayerPed(GetPlayerFromServerId(playerId))
|
if targetPed ~= playerPed then
|
||||||
if targetPed and targetPed ~= 0 then
|
local targetCoords = GetEntityCoords(targetPed)
|
||||||
local targetCoords = GetEntityCoords(targetPed)
|
local distance = #(playerCoords - targetCoords)
|
||||||
local distance = #(playerCoords - targetCoords)
|
|
||||||
|
|
||||||
if distance <= 5.0 then
|
if distance <= radius then
|
||||||
local targetPlayer = QBCore.Functions.GetPlayerData(playerId)
|
local serverId = GetPlayerServerId(playerId)
|
||||||
table.insert(nearbyPlayers, {
|
local playerName = GetPlayerName(playerId)
|
||||||
id = playerId,
|
|
||||||
name = targetPlayer and (targetPlayer.charinfo.firstname .. ' ' .. targetPlayer.charinfo.lastname) or 'Unbekannt'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:client:viewLicense', function(licenseData, issuerName)
|
table.insert(players, {
|
||||||
showLicenseOverlay(licenseData, issuerName)
|
id = serverId,
|
||||||
end)
|
name = playerName,
|
||||||
|
distance = math.floor(distance * 100) / 100,
|
||||||
-- Hauptmenü
|
ped = targetPed
|
||||||
function openMainMenu()
|
|
||||||
local options = {
|
|
||||||
{
|
|
||||||
title = 'Meine Lizenzen anzeigen',
|
|
||||||
description = 'Zeige alle deine Lizenzen an',
|
|
||||||
icon = 'fas fa-eye',
|
|
||||||
onSelect = function()
|
|
||||||
showMyLicenses()
|
|
||||||
end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title = 'Lizenz zeigen',
|
|
||||||
description = 'Zeige jemandem in der Nähe eine Lizenz',
|
|
||||||
icon = 'fas fa-share',
|
|
||||||
onSelect = function()
|
|
||||||
showLicenseToPlayer()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Admin-Optionen hinzufügen
|
|
||||||
local PlayerData = QBCore.Functions.GetPlayerData()
|
|
||||||
if PlayerData.job and Config.AuthorizedJobs[PlayerData.job.name] then
|
|
||||||
table.insert(options, {
|
|
||||||
title = 'Lizenz ausstellen',
|
|
||||||
description = 'Stelle eine neue Lizenz aus',
|
|
||||||
icon = 'fas fa-plus',
|
|
||||||
onSelect = function()
|
|
||||||
issueLicenseMenu()
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
table.insert(options, {
|
|
||||||
title = 'Lizenz verwalten',
|
|
||||||
description = 'Entziehe oder stelle Lizenzen wieder her',
|
|
||||||
icon = 'fas fa-cog',
|
|
||||||
onSelect = function()
|
|
||||||
manageLicensesMenu()
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'license_main_menu',
|
|
||||||
title = 'Lizenz-System',
|
|
||||||
options = options
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('license_main_menu')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Meine Lizenzen anzeigen
|
|
||||||
function showMyLicenses()
|
|
||||||
local options = {}
|
|
||||||
|
|
||||||
for _, license in ipairs(playerLicenses) do
|
|
||||||
local licenseConfig = Config.Licenses[license.license_type]
|
|
||||||
if licenseConfig then
|
|
||||||
local statusText = license.is_active and "✅ Aktiv" or "❌ Entzogen"
|
|
||||||
table.insert(options, {
|
|
||||||
title = licenseConfig.label,
|
|
||||||
description = 'Status: ' .. statusText .. ' | Ausgestellt: ' .. license.issue_date,
|
|
||||||
icon = licenseConfig.icon,
|
|
||||||
onSelect = function()
|
|
||||||
showLicenseOverlay(license)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #options == 0 then
|
|
||||||
options = {{
|
|
||||||
title = 'Keine Lizenzen vorhanden',
|
|
||||||
description = 'Du besitzt derzeit keine Lizenzen',
|
|
||||||
icon = 'fas fa-exclamation-triangle'
|
|
||||||
}}
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'my_licenses_menu',
|
|
||||||
title = 'Meine Lizenzen',
|
|
||||||
menu = 'license_main_menu',
|
|
||||||
options = options
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('my_licenses_menu')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz jemandem zeigen
|
|
||||||
function showLicenseToPlayer()
|
|
||||||
TriggerServerEvent('license-system:server:getNearbyPlayers')
|
|
||||||
|
|
||||||
Wait(500) -- Kurz warten für die Antwort
|
|
||||||
|
|
||||||
if #nearbyPlayers == 0 then
|
|
||||||
lib.notify({
|
|
||||||
title = 'Lizenz-System',
|
|
||||||
description = 'Keine Spieler in der Nähe gefunden',
|
|
||||||
type = 'error'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local licenseOptions = {}
|
|
||||||
for _, license in ipairs(playerLicenses) do
|
|
||||||
if license.is_active then
|
|
||||||
local licenseConfig = Config.Licenses[license.license_type]
|
|
||||||
if licenseConfig then
|
|
||||||
table.insert(licenseOptions, {
|
|
||||||
title = licenseConfig.label,
|
|
||||||
description = 'Ausgestellt: ' .. license.issue_date,
|
|
||||||
icon = licenseConfig.icon,
|
|
||||||
onSelect = function()
|
|
||||||
selectPlayerToShow(license)
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #licenseOptions == 0 then
|
-- Nach Entfernung sortieren
|
||||||
lib.notify({
|
table.sort(players, function(a, b)
|
||||||
title = 'Lizenz-System',
|
return a.distance < b.distance
|
||||||
description = 'Du hast keine aktiven Lizenzen zum Zeigen',
|
end)
|
||||||
type = 'error'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
return players
|
||||||
id = 'select_license_to_show',
|
|
||||||
title = 'Lizenz auswählen',
|
|
||||||
menu = 'license_main_menu',
|
|
||||||
options = licenseOptions
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('select_license_to_show')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function selectPlayerToShow(license)
|
-- Berechtigung prüfen
|
||||||
local playerOptions = {}
|
local function hasPermission()
|
||||||
|
|
||||||
for _, player in ipairs(nearbyPlayers) do
|
|
||||||
table.insert(playerOptions, {
|
|
||||||
title = player.name,
|
|
||||||
description = 'Zeige diesem Spieler deine Lizenz',
|
|
||||||
icon = 'fas fa-user',
|
|
||||||
onSelect = function()
|
|
||||||
TriggerServerEvent('license-system:server:showLicense', player.id, license)
|
|
||||||
lib.notify({
|
|
||||||
title = 'Lizenz-System',
|
|
||||||
description = 'Lizenz wurde ' .. player.name .. ' gezeigt',
|
|
||||||
type = 'success'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'select_player_to_show',
|
|
||||||
title = 'Spieler auswählen',
|
|
||||||
menu = 'select_license_to_show',
|
|
||||||
options = playerOptions
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('select_player_to_show')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz ausstellen
|
|
||||||
function issueLicenseMenu()
|
|
||||||
local PlayerData = QBCore.Functions.GetPlayerData()
|
local PlayerData = QBCore.Functions.GetPlayerData()
|
||||||
local authorizedLicenses = Config.AuthorizedJobs[PlayerData.job.name].canIssue
|
if not PlayerData or not PlayerData.job then return false end
|
||||||
|
|
||||||
local options = {}
|
return Config.AuthorizedJobs[PlayerData.job.name] or false
|
||||||
for _, licenseType in ipairs(authorizedLicenses) do
|
|
||||||
local licenseConfig = Config.Licenses[licenseType]
|
|
||||||
if licenseConfig then
|
|
||||||
table.insert(options, {
|
|
||||||
title = licenseConfig.label,
|
|
||||||
description = 'Stelle einen ' .. licenseConfig.label .. ' aus',
|
|
||||||
icon = licenseConfig.icon,
|
|
||||||
onSelect = function()
|
|
||||||
selectPlayerForLicense(licenseType)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'issue_license_menu',
|
|
||||||
title = 'Lizenz ausstellen',
|
|
||||||
menu = 'license_main_menu',
|
|
||||||
options = options
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('issue_license_menu')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function selectPlayerForLicense(licenseType)
|
-- Lizenz anzeigen
|
||||||
TriggerServerEvent('license-system:server:getNearbyPlayers')
|
local function showLicense(licenseData)
|
||||||
|
if not licenseData then
|
||||||
Wait(500)
|
showNotification(Config.Notifications.license_not_found.message, Config.Notifications.license_not_found.type)
|
||||||
|
|
||||||
if #nearbyPlayers == 0 then
|
|
||||||
lib.notify({
|
|
||||||
title = 'Lizenz-System',
|
|
||||||
description = 'Keine Spieler in der Nähe gefunden',
|
|
||||||
type = 'error'
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local playerOptions = {}
|
debugPrint("Zeige Lizenz: " .. licenseData.license.license_type)
|
||||||
for _, player in ipairs(nearbyPlayers) do
|
|
||||||
table.insert(playerOptions, {
|
|
||||||
title = player.name,
|
|
||||||
description = 'Stelle diesem Spieler eine Lizenz aus',
|
|
||||||
icon = 'fas fa-user',
|
|
||||||
onSelect = function()
|
|
||||||
openLicenseForm(player.id, licenseType)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'select_player_for_license',
|
|
||||||
title = 'Spieler auswählen',
|
|
||||||
menu = 'issue_license_menu',
|
|
||||||
options = playerOptions
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('select_player_for_license')
|
|
||||||
end
|
|
||||||
|
|
||||||
function openLicenseForm(targetId, licenseType)
|
|
||||||
local licenseConfig = Config.Licenses[licenseType]
|
|
||||||
local input = {}
|
|
||||||
|
|
||||||
if licenseConfig.fields.name then
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'input',
|
|
||||||
isRequired = true,
|
|
||||||
label = 'Name',
|
|
||||||
placeholder = 'Vor- und Nachname'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if licenseConfig.fields.birthday then
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'date',
|
|
||||||
isRequired = true,
|
|
||||||
label = 'Geburtsdatum'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if licenseConfig.fields.gender then
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'select',
|
|
||||||
label = 'Geschlecht',
|
|
||||||
options = Config.Genders
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if licenseConfig.fields.issue_date then
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'date',
|
|
||||||
isRequired = true,
|
|
||||||
label = 'Ausstellungsdatum',
|
|
||||||
default = os.date('%Y-%m-%d')
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if licenseConfig.fields.expire_date then
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'date',
|
|
||||||
isRequired = true,
|
|
||||||
label = 'Ablaufdatum'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if licenseConfig.fields.classes then
|
|
||||||
local classOptions = {}
|
|
||||||
for _, class in ipairs(Config.LicenseClasses) do
|
|
||||||
table.insert(classOptions, {
|
|
||||||
value = class,
|
|
||||||
label = 'Klasse ' .. class
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(input, {
|
|
||||||
type = 'multi-select',
|
|
||||||
label = 'Führerscheinklassen',
|
|
||||||
options = classOptions
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local dialog = lib.inputDialog('Lizenz ausstellen - ' .. licenseConfig.label, input)
|
|
||||||
|
|
||||||
if dialog then
|
|
||||||
local licenseData = {
|
|
||||||
name = dialog[1],
|
|
||||||
birthday = dialog[2],
|
|
||||||
gender = dialog[3],
|
|
||||||
issue_date = dialog[4] or os.date('%Y-%m-%d'),
|
|
||||||
expire_date = dialog[5],
|
|
||||||
classes = dialog[6]
|
|
||||||
}
|
|
||||||
|
|
||||||
TriggerServerEvent('license-system:server:issueLicense', targetId, licenseType, licenseData)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz-Overlay anzeigen
|
|
||||||
function showLicenseOverlay(licenseData, issuerName)
|
|
||||||
local licenseConfig = Config.Licenses[licenseData.license_type]
|
|
||||||
if not licenseConfig then return end
|
|
||||||
|
|
||||||
-- NUI öffnen
|
|
||||||
SetNuiFocus(true, true)
|
|
||||||
SendNUIMessage({
|
SendNUIMessage({
|
||||||
action = 'showLicense',
|
action = 'showLicense',
|
||||||
data = {
|
data = licenseData
|
||||||
license = licenseData,
|
})
|
||||||
config = licenseConfig,
|
|
||||||
issuer = issuerName
|
SetNuiFocus(true, true)
|
||||||
|
isMenuOpen = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Lizenz schließen
|
||||||
|
local function closeLicense()
|
||||||
|
SendNUIMessage({
|
||||||
|
action = 'hideLicense'
|
||||||
|
})
|
||||||
|
|
||||||
|
SetNuiFocus(false, false)
|
||||||
|
isMenuOpen = false
|
||||||
|
debugPrint("Lizenz geschlossen")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Spieler-Lizenz-Menü
|
||||||
|
local function openPlayerLicenseMenu(targetId, targetName)
|
||||||
|
debugPrint("Öffne Lizenz-Menü für Spieler: " .. targetName)
|
||||||
|
|
||||||
|
QBCore.Functions.TriggerCallback('license-system:server:getPlayerLicenses', function(licenses)
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
|
local statusIcon = license.is_active and '✅' or '❌'
|
||||||
|
local statusText = license.is_active and 'Gültig' or 'Ungültig'
|
||||||
|
local expireText = license.expire_date or 'Unbegrenzt'
|
||||||
|
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = licenseConfig.label,
|
||||||
|
description = 'Status: ' .. statusText .. ' | Gültig bis: ' .. expireText,
|
||||||
|
icon = licenseConfig.icon,
|
||||||
|
onSelect = function()
|
||||||
|
-- Lizenz anzeigen
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Aktionen hinzufügen
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = '─────────────────',
|
||||||
|
disabled = true
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Neue Lizenz ausstellen
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Lizenz entziehen
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = 'Lizenz entziehen',
|
||||||
|
description = 'Eine bestehende Lizenz entziehen',
|
||||||
|
icon = 'fas fa-minus',
|
||||||
|
onSelect = function()
|
||||||
|
openRevokeLicenseMenu(targetId, targetName, licenses)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
else
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = 'Keine Lizenzen gefunden',
|
||||||
|
description = 'Dieser Spieler hat keine Lizenzen',
|
||||||
|
icon = 'fas fa-exclamation-triangle',
|
||||||
|
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
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Zurück-Option
|
||||||
|
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, targetId)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Lizenz ausstellen Menü
|
||||||
|
local function openIssueLicenseMenu(targetId, targetName)
|
||||||
|
local menuOptions = {}
|
||||||
|
|
||||||
|
for licenseType, config in pairs(Config.LicenseTypes) do
|
||||||
|
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 = config.price .. ' $'},
|
||||||
|
{label = 'Gültigkeitsdauer', value = config.validity_days and (config.validity_days .. ' Tage') or 'Unbegrenzt'}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Führerschein-Klassen Menü
|
||||||
|
local function openDriversLicenseClassMenu(targetId, targetName, licenseType)
|
||||||
|
local config = Config.LicenseTypes[licenseType]
|
||||||
|
local selectedClasses = {}
|
||||||
|
|
||||||
|
local function updateMenu()
|
||||||
|
local menuOptions = {}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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-Ausstellung bestätigen
|
||||||
|
local function confirmIssueLicense(targetId, targetName, licenseType, classes)
|
||||||
|
local config = Config.LicenseTypes[licenseType]
|
||||||
|
local classText = classes and table.concat(classes, ', ') or 'Keine'
|
||||||
|
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'confirm_issue_license',
|
||||||
|
title = 'Lizenz ausstellen bestätigen',
|
||||||
|
options = {
|
||||||
|
{
|
||||||
|
title = 'Spieler: ' .. targetName,
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = 'Lizenztyp: ' .. config.label,
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = 'Klassen: ' .. classText,
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = 'Kosten: ' .. config.price .. ' $',
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '─────────────────',
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '✅ Bestätigen',
|
||||||
|
description = 'Lizenz jetzt ausstellen',
|
||||||
|
icon = 'fas fa-check',
|
||||||
|
onSelect = function()
|
||||||
|
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
|
end
|
||||||
|
|
||||||
|
-- Lizenz entziehen Menü
|
||||||
|
local function openRevokeLicenseMenu(targetId, targetName, licenses)
|
||||||
|
local menuOptions = {}
|
||||||
|
|
||||||
|
for _, license in ipairs(licenses) do
|
||||||
|
if license.is_active 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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = 'Lizenztyp: ' .. config.label,
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '─────────────────',
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '✅ Bestätigen',
|
||||||
|
description = 'Lizenz jetzt entziehen',
|
||||||
|
icon = 'fas fa-check',
|
||||||
|
onSelect = function()
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Hauptmenü für Lizenz-System
|
||||||
|
local function openLicenseMenu()
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
QBCore.Functions.TriggerCallback('license-system:server:getMyLicense', function(licenseData)
|
||||||
|
if licenseData then
|
||||||
|
showLicense(licenseData)
|
||||||
|
else
|
||||||
|
showNotification('Du hast keine ' .. config.label .. '!', 'error')
|
||||||
|
end
|
||||||
|
end, licenseType)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'my_licenses',
|
||||||
|
title = 'Meine Lizenzen',
|
||||||
|
options = menuOptions
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.showContext('my_licenses')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Events
|
||||||
|
RegisterNetEvent('license-system:client:showLicense', function(targetId)
|
||||||
|
QBCore.Functions.TriggerCallback('license-system:server:getLicense', function(licenseData)
|
||||||
|
showLicense(licenseData)
|
||||||
|
end, targetId)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:client:showMyLicense', function(licenseType)
|
||||||
|
QBCore.Functions.TriggerCallback('license-system:server:getMyLicense', function(licenseData)
|
||||||
|
showLicense(licenseData)
|
||||||
|
end, licenseType)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:client:openCamera', function()
|
||||||
|
SendNUIMessage({
|
||||||
|
action = 'openCamera'
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
-- NUI Callbacks
|
-- NUI Callbacks
|
||||||
RegisterNUICallback('closeLicense', function(data, cb)
|
RegisterNUICallback('closeLicense', function(data, cb)
|
||||||
SetNuiFocus(false, false)
|
closeLicense()
|
||||||
cb('ok')
|
safeCallback(cb, 'ok')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Lizenz verwalten (für autorisierte Jobs)
|
RegisterNUICallback('savePhoto', function(data, cb)
|
||||||
function manageLicensesMenu()
|
if data.photo and data.citizenid then
|
||||||
-- Hier würde die Verwaltung für das Entziehen/Wiederherstellen von Lizenzen implementiert
|
TriggerServerEvent('license-system:server:savePhoto', data.citizenid, data.photo)
|
||||||
-- Ähnlich wie die anderen Menüs, aber mit Suchfunktion nach Spielern
|
safeCallback(cb, 'ok')
|
||||||
lib.notify({
|
else
|
||||||
title = 'Lizenz-System',
|
safeCallback(cb, 'error')
|
||||||
description = 'Lizenz-Verwaltung wird implementiert...',
|
|
||||||
type = 'info'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Foto vom Spieler machen
|
|
||||||
RegisterNetEvent('license-system:client:takePlayerPhoto', function(targetId)
|
|
||||||
local targetPed = GetPlayerPed(GetPlayerFromServerId(targetId))
|
|
||||||
|
|
||||||
if targetPed and targetPed ~= 0 then
|
|
||||||
-- Mugshot erstellen
|
|
||||||
local mugshot = RegisterPedheadshot(targetPed)
|
|
||||||
|
|
||||||
-- Warten bis Mugshot geladen ist
|
|
||||||
while not IsPedheadshotReady(mugshot) do
|
|
||||||
Wait(100)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Mugshot-Textur holen
|
|
||||||
local mugshotTxd = GetPedheadshotTxdString(mugshot)
|
|
||||||
|
|
||||||
-- An Server senden
|
|
||||||
TriggerServerEvent('license-system:server:savePlayerPhoto', QBCore.Functions.GetPlayerData().citizenid, mugshotTxd)
|
|
||||||
|
|
||||||
-- Mugshot wieder freigeben
|
|
||||||
UnregisterPedheadshot(mugshot)
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Commands
|
||||||
|
RegisterCommand(Config.Commands.license.name, function()
|
||||||
|
openLicenseMenu()
|
||||||
|
end, Config.Commands.license.restricted)
|
||||||
|
|
||||||
|
RegisterCommand(Config.Commands.mylicense.name, function()
|
||||||
|
showMyLicenses()
|
||||||
|
end, Config.Commands.mylicense.restricted)
|
||||||
|
|
||||||
|
-- Keybinds
|
||||||
|
if 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.show_my_licenses then
|
||||||
|
RegisterKeyMapping(Config.Commands.mylicense.name, Config.Keybinds.show_my_licenses.description, 'keyboard', Config.Keybinds.show_my_licenses.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleanup
|
||||||
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
|
if GetCurrentResourceName() == resourceName then
|
||||||
|
if isMenuOpen then
|
||||||
|
closeLicense()
|
||||||
|
end
|
||||||
|
debugPrint("License-System Client gestoppt")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Initialisierung
|
||||||
|
CreateThread(function()
|
||||||
|
debugPrint("License-System Client gestartet")
|
||||||
|
end)
|
||||||
|
|
|
@ -1,107 +1,306 @@
|
||||||
Config = {}
|
Config = {}
|
||||||
|
|
||||||
-- Keybind zum Öffnen des Menüs
|
-- Allgemeine Einstellungen
|
||||||
Config.OpenKey = 'F6'
|
Config.Debug = true
|
||||||
|
Config.UseBackgroundImages = true
|
||||||
|
Config.MaxLicenseAge = 365 -- Tage bis Ablauf
|
||||||
|
Config.RenewalDays = 30 -- Tage vor Ablauf für Verlängerung
|
||||||
|
|
||||||
-- Command zum Öffnen
|
-- Berechtigte Jobs
|
||||||
Config.Command = 'licenses'
|
|
||||||
|
|
||||||
-- Jobs die Lizenzen ausstellen können
|
|
||||||
Config.AuthorizedJobs = {
|
Config.AuthorizedJobs = {
|
||||||
['police'] = {
|
['police'] = true,
|
||||||
canIssue = {'id_card', 'drivers_license', 'weapon_license'},
|
['sheriff'] = true,
|
||||||
canRevoke = {'drivers_license', 'weapon_license'},
|
['government'] = true,
|
||||||
canRestore = {'drivers_license', 'weapon_license'}
|
['judge'] = true,
|
||||||
},
|
['lawyer'] = true,
|
||||||
['admin'] = {
|
['ambulance'] = true, -- Für medizinische Lizenzen
|
||||||
canIssue = {'id_card', 'drivers_license', 'passport', 'business_license'},
|
['mechanic'] = true -- Für Fahrzeug-Lizenzen
|
||||||
canRevoke = {'drivers_license', 'business_license'},
|
|
||||||
canRestore = {'drivers_license', 'business_license'}
|
|
||||||
},
|
|
||||||
['driving_school'] = {
|
|
||||||
canIssue = {'drivers_license'},
|
|
||||||
canRevoke = {},
|
|
||||||
canRestore = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Verfügbare Lizenzen/Ausweise
|
-- Lizenz-Typen
|
||||||
Config.Licenses = {
|
Config.LicenseTypes = {
|
||||||
['id_card'] = {
|
['id_card'] = {
|
||||||
label = 'Personalausweis',
|
label = 'Personalausweis',
|
||||||
icon = 'fas fa-id-card',
|
icon = 'fas fa-id-card',
|
||||||
fields = {
|
color = '#667eea',
|
||||||
name = true,
|
price = 50,
|
||||||
birthday = true,
|
required_items = {},
|
||||||
gender = true,
|
can_expire = true,
|
||||||
issue_date = true,
|
validity_days = 3650, -- 10 Jahre
|
||||||
expire_date = true,
|
required_job = nil,
|
||||||
classes = false
|
description = 'Offizieller Personalausweis'
|
||||||
},
|
|
||||||
template = 'id_card'
|
|
||||||
},
|
},
|
||||||
['drivers_license'] = {
|
['drivers_license'] = {
|
||||||
label = 'Führerschein',
|
label = 'Führerschein',
|
||||||
icon = 'fas fa-car',
|
icon = 'fas fa-car',
|
||||||
fields = {
|
color = '#f093fb',
|
||||||
name = true,
|
price = 500,
|
||||||
birthday = true,
|
required_items = {'driving_test_certificate'},
|
||||||
gender = true,
|
can_expire = true,
|
||||||
issue_date = true,
|
validity_days = 5475, -- 15 Jahre
|
||||||
expire_date = true,
|
required_job = 'driving_school',
|
||||||
classes = true
|
description = 'Berechtigung zum Führen von Kraftfahrzeugen',
|
||||||
},
|
classes = {
|
||||||
template = 'drivers_license'
|
'A', 'A1', 'A2', 'B', 'BE', 'C', 'CE', 'D', 'DE'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
['weapon_license'] = {
|
['weapon_license'] = {
|
||||||
label = 'Waffenschein',
|
label = 'Waffenschein',
|
||||||
icon = 'fas fa-gun',
|
icon = 'fas fa-crosshairs',
|
||||||
fields = {
|
color = '#4facfe',
|
||||||
name = true,
|
price = 2500,
|
||||||
birthday = true,
|
required_items = {'weapon_course_certificate', 'psychological_evaluation'},
|
||||||
gender = true,
|
can_expire = true,
|
||||||
issue_date = true,
|
validity_days = 1095, -- 3 Jahre
|
||||||
expire_date = true,
|
required_job = 'police',
|
||||||
classes = false
|
description = 'Berechtigung zum Führen von Schusswaffen',
|
||||||
},
|
restrictions = {
|
||||||
template = 'weapon_license'
|
'Nur für registrierte Waffen',
|
||||||
|
'Regelmäßige Überprüfung erforderlich',
|
||||||
|
'Nicht übertragbar'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
['passport'] = {
|
['passport'] = {
|
||||||
label = 'Reisepass',
|
label = 'Reisepass',
|
||||||
icon = 'fas fa-passport',
|
icon = 'fas fa-passport',
|
||||||
fields = {
|
color = '#43e97b',
|
||||||
name = true,
|
price = 150,
|
||||||
birthday = true,
|
required_items = {'birth_certificate', 'id_card'},
|
||||||
gender = true,
|
can_expire = true,
|
||||||
issue_date = true,
|
validity_days = 3650, -- 10 Jahre
|
||||||
expire_date = true,
|
required_job = 'government',
|
||||||
classes = false
|
description = 'Internationales Reisedokument'
|
||||||
},
|
|
||||||
template = 'passport'
|
|
||||||
},
|
},
|
||||||
['business_license'] = {
|
['business_license'] = {
|
||||||
label = 'Gewerbeschein',
|
label = 'Gewerbeschein',
|
||||||
icon = 'fas fa-briefcase',
|
icon = 'fas fa-briefcase',
|
||||||
fields = {
|
color = '#fa709a',
|
||||||
name = true,
|
price = 1000,
|
||||||
birthday = false,
|
required_items = {'business_plan', 'tax_certificate'},
|
||||||
gender = false,
|
can_expire = true,
|
||||||
issue_date = true,
|
validity_days = 1825, -- 5 Jahre
|
||||||
expire_date = true,
|
required_job = 'government',
|
||||||
classes = false
|
description = 'Berechtigung zur Ausübung eines Gewerbes'
|
||||||
},
|
},
|
||||||
template = 'business_license'
|
['pilot_license'] = {
|
||||||
|
label = 'Pilotenlizenz',
|
||||||
|
icon = 'fas fa-plane',
|
||||||
|
color = '#667eea',
|
||||||
|
price = 5000,
|
||||||
|
required_items = {'flight_hours_log', 'medical_certificate'},
|
||||||
|
can_expire = true,
|
||||||
|
validity_days = 730, -- 2 Jahre
|
||||||
|
required_job = 'airport',
|
||||||
|
description = 'Berechtigung zum Führen von Luftfahrzeugen'
|
||||||
|
},
|
||||||
|
['boat_license'] = {
|
||||||
|
label = 'Bootsführerschein',
|
||||||
|
icon = 'fas fa-ship',
|
||||||
|
color = '#00f2fe',
|
||||||
|
price = 800,
|
||||||
|
required_items = {'boat_course_certificate'},
|
||||||
|
can_expire = true,
|
||||||
|
validity_days = 1825, -- 5 Jahre
|
||||||
|
required_job = 'harbor',
|
||||||
|
description = 'Berechtigung zum Führen von Wasserfahrzeugen'
|
||||||
|
},
|
||||||
|
['medical_license'] = {
|
||||||
|
label = 'Approbation',
|
||||||
|
icon = 'fas fa-user-md',
|
||||||
|
color = '#ff6b6b',
|
||||||
|
price = 0, -- Kostenlos für Ärzte
|
||||||
|
required_items = {'medical_degree', 'medical_exam'},
|
||||||
|
can_expire = false,
|
||||||
|
validity_days = nil,
|
||||||
|
required_job = 'ambulance',
|
||||||
|
description = 'Berechtigung zur Ausübung der Heilkunde'
|
||||||
|
},
|
||||||
|
['hunting_license'] = {
|
||||||
|
label = 'Jagdschein',
|
||||||
|
icon = 'fas fa-crosshairs',
|
||||||
|
color = '#8b5a3c',
|
||||||
|
price = 300,
|
||||||
|
required_items = {'hunting_course_certificate'},
|
||||||
|
can_expire = true,
|
||||||
|
validity_days = 1095, -- 3 Jahre
|
||||||
|
required_job = 'ranger',
|
||||||
|
description = 'Berechtigung zur Ausübung der Jagd'
|
||||||
|
},
|
||||||
|
['fishing_license'] = {
|
||||||
|
label = 'Angelschein',
|
||||||
|
icon = 'fas fa-fish',
|
||||||
|
color = '#4ecdc4',
|
||||||
|
price = 50,
|
||||||
|
required_items = {},
|
||||||
|
can_expire = true,
|
||||||
|
validity_days = 365, -- 1 Jahr
|
||||||
|
required_job = nil,
|
||||||
|
description = 'Berechtigung zum Angeln in öffentlichen Gewässern'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Führerscheinklassen
|
-- Standorte für Lizenz-Ausgabe
|
||||||
Config.LicenseClasses = {
|
Config.LicenseLocations = {
|
||||||
'A', 'B', 'C', 'Boot', 'Flugzeug'
|
['city_hall'] = {
|
||||||
|
label = 'Rathaus',
|
||||||
|
coords = vector3(-544.85, -204.13, 38.22),
|
||||||
|
blip = {
|
||||||
|
sprite = 419,
|
||||||
|
color = 2,
|
||||||
|
scale = 0.8
|
||||||
|
},
|
||||||
|
available_licenses = {
|
||||||
|
'id_card', 'passport', 'business_license'
|
||||||
|
},
|
||||||
|
ped = {
|
||||||
|
model = 'a_m_m_business_01',
|
||||||
|
coords = vector4(-544.85, -204.13, 37.22, 180.0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['driving_school'] = {
|
||||||
|
label = 'Fahrschule',
|
||||||
|
coords = vector3(-829.22, -1209.58, 7.33),
|
||||||
|
blip = {
|
||||||
|
sprite = 225,
|
||||||
|
color = 46,
|
||||||
|
scale = 0.8
|
||||||
|
},
|
||||||
|
available_licenses = {
|
||||||
|
'drivers_license'
|
||||||
|
},
|
||||||
|
ped = {
|
||||||
|
model = 'a_m_y_business_02',
|
||||||
|
coords = vector4(-829.22, -1209.58, 6.33, 90.0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['police_station'] = {
|
||||||
|
label = 'Polizeiwache',
|
||||||
|
coords = vector3(441.07, -979.76, 30.69),
|
||||||
|
blip = {
|
||||||
|
sprite = 60,
|
||||||
|
color = 29,
|
||||||
|
scale = 0.8
|
||||||
|
},
|
||||||
|
available_licenses = {
|
||||||
|
'weapon_license'
|
||||||
|
},
|
||||||
|
ped = {
|
||||||
|
model = 's_m_y_cop_01',
|
||||||
|
coords = vector4(441.07, -979.76, 29.69, 270.0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
['hospital'] = {
|
||||||
|
label = 'Krankenhaus',
|
||||||
|
coords = vector3(307.7, -1433.4, 29.9),
|
||||||
|
blip = {
|
||||||
|
sprite = 61,
|
||||||
|
color = 1,
|
||||||
|
scale = 0.8
|
||||||
|
},
|
||||||
|
available_licenses = {
|
||||||
|
'medical_license'
|
||||||
|
},
|
||||||
|
ped = {
|
||||||
|
model = 's_m_m_doctor_01',
|
||||||
|
coords = vector4(307.7, -1433.4, 28.9, 180.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Geschlechter
|
-- Kommandos
|
||||||
Config.Genders = {
|
Config.Commands = {
|
||||||
{value = 'male', label = 'Männlich'},
|
['license'] = {
|
||||||
{value = 'female', label = 'Weiblich'},
|
name = 'lizenz',
|
||||||
{value = 'other', label = 'Divers'}
|
help = 'Lizenz-System öffnen',
|
||||||
|
restricted = true -- Nur für berechtigte Jobs
|
||||||
|
},
|
||||||
|
['mylicense'] = {
|
||||||
|
name = 'meinelizenz',
|
||||||
|
help = 'Eigene Lizenzen anzeigen',
|
||||||
|
restricted = false -- Für alle Spieler
|
||||||
|
},
|
||||||
|
['givelicense'] = {
|
||||||
|
name = 'givelicense',
|
||||||
|
help = 'Lizenz an Spieler vergeben',
|
||||||
|
restricted = true,
|
||||||
|
admin_only = true
|
||||||
|
},
|
||||||
|
['revokelicense'] = {
|
||||||
|
name = 'revokelicense',
|
||||||
|
help = 'Lizenz entziehen',
|
||||||
|
restricted = true,
|
||||||
|
admin_only = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Keybinds
|
||||||
|
Config.Keybinds = {
|
||||||
|
['open_license_menu'] = {
|
||||||
|
key = 'F6',
|
||||||
|
command = 'lizenz',
|
||||||
|
description = 'Lizenz-System öffnen'
|
||||||
|
},
|
||||||
|
['show_my_licenses'] = {
|
||||||
|
key = 'F7',
|
||||||
|
command = 'meinelizenz',
|
||||||
|
description = 'Meine Lizenzen anzeigen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Benachrichtigungen
|
||||||
|
Config.Notifications = {
|
||||||
|
['no_permission'] = {
|
||||||
|
message = 'Du hast keine Berechtigung!',
|
||||||
|
type = 'error'
|
||||||
|
},
|
||||||
|
['no_players_nearby'] = {
|
||||||
|
message = 'Keine Spieler in der Nähe!',
|
||||||
|
type = 'error'
|
||||||
|
},
|
||||||
|
['license_not_found'] = {
|
||||||
|
message = 'Keine Lizenz gefunden!',
|
||||||
|
type = 'error'
|
||||||
|
},
|
||||||
|
['license_expired'] = {
|
||||||
|
message = 'Diese Lizenz ist abgelaufen!',
|
||||||
|
type = 'warning'
|
||||||
|
},
|
||||||
|
['license_expires_soon'] = {
|
||||||
|
message = 'Diese Lizenz läuft bald ab!',
|
||||||
|
type = 'warning'
|
||||||
|
},
|
||||||
|
['license_granted'] = {
|
||||||
|
message = 'Lizenz erfolgreich ausgestellt!',
|
||||||
|
type = 'success'
|
||||||
|
},
|
||||||
|
['license_revoked'] = {
|
||||||
|
message = 'Lizenz wurde entzogen!',
|
||||||
|
type = 'info'
|
||||||
|
},
|
||||||
|
['photo_saved'] = {
|
||||||
|
message = 'Foto gespeichert!',
|
||||||
|
type = 'success'
|
||||||
|
},
|
||||||
|
['insufficient_funds'] = {
|
||||||
|
message = 'Nicht genügend Geld!',
|
||||||
|
type = 'error'
|
||||||
|
},
|
||||||
|
['missing_items'] = {
|
||||||
|
message = 'Benötigte Gegenstände fehlen!',
|
||||||
|
type = 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Sounds
|
||||||
|
Config.Sounds = {
|
||||||
|
['card_flip'] = 'sounds/card_flip.mp3',
|
||||||
|
['camera_shutter'] = 'sounds/camera_shutter.mp3',
|
||||||
|
['notification'] = 'sounds/notification.mp3'
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Datenbank-Einstellungen
|
||||||
|
Config.Database = {
|
||||||
|
['table_name'] = 'player_licenses',
|
||||||
|
['auto_cleanup'] = true, -- Alte Lizenzen automatisch löschen
|
||||||
|
['cleanup_days'] = 365 -- Nach wie vielen Tagen löschen
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,312 +1,530 @@
|
||||||
local QBCore = exports['qb-core']:GetCoreObject()
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
|
||||||
-- Datenbank Setup
|
|
||||||
MySQL.ready(function()
|
|
||||||
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,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
birthday VARCHAR(20),
|
|
||||||
gender VARCHAR(20),
|
|
||||||
issue_date VARCHAR(20) NOT NULL,
|
|
||||||
expire_date VARCHAR(20),
|
|
||||||
classes TEXT,
|
|
||||||
issued_by VARCHAR(50) NOT NULL,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
INDEX(citizenid),
|
|
||||||
INDEX(license_type)
|
|
||||||
)
|
|
||||||
]])
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Command registrieren
|
|
||||||
QBCore.Commands.Add(Config.Command, 'Öffne das Lizenz-Menü', {}, false, function(source, args)
|
|
||||||
TriggerClientEvent('license-system:client:openMenu', source)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Events
|
|
||||||
RegisterNetEvent('license-system:server:getLicenses', function()
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
if not Player then return end
|
|
||||||
|
|
||||||
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ?', {
|
|
||||||
Player.PlayerData.citizenid
|
|
||||||
}, function(result)
|
|
||||||
TriggerClientEvent('license-system:client:receiveLicenses', src, result)
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:getNearbyPlayers', function()
|
|
||||||
local src = source
|
|
||||||
TriggerClientEvent('license-system:client:receiveNearbyPlayers', src, QBCore.Functions.GetPlayers())
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:showLicense', function(targetId, licenseData)
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
if not Player then return end
|
|
||||||
|
|
||||||
TriggerClientEvent('license-system:client:viewLicense', targetId, licenseData, Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname)
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:issueLicense', function(targetId, licenseType, licenseData)
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
|
|
||||||
if not Player or not TargetPlayer then return end
|
|
||||||
|
|
||||||
-- Job-Berechtigung prüfen
|
|
||||||
local playerJob = Player.PlayerData.job.name
|
|
||||||
if not Config.AuthorizedJobs[playerJob] or not hasPermission(Config.AuthorizedJobs[playerJob].canIssue, licenseType) then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz in Datenbank speichern
|
|
||||||
MySQL.Async.execute('INSERT INTO player_licenses (citizenid, license_type, name, birthday, gender, issue_date, expire_date, classes, issued_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', {
|
|
||||||
TargetPlayer.PlayerData.citizenid,
|
|
||||||
licenseType,
|
|
||||||
licenseData.name,
|
|
||||||
licenseData.birthday,
|
|
||||||
licenseData.gender,
|
|
||||||
licenseData.issue_date,
|
|
||||||
licenseData.expire_date,
|
|
||||||
json.encode(licenseData.classes or {}),
|
|
||||||
Player.PlayerData.citizenid
|
|
||||||
}, function(insertId)
|
|
||||||
if insertId then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt!', 'success')
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. Config.Licenses[licenseType].label, 'success')
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licenseId)
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
|
|
||||||
if not Player then return end
|
|
||||||
|
|
||||||
-- Job-Berechtigung prüfen
|
|
||||||
local playerJob = Player.PlayerData.job.name
|
|
||||||
if not Config.AuthorizedJobs[playerJob] then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE WHERE id = ?', {
|
|
||||||
licenseId
|
|
||||||
}, function(affectedRows)
|
|
||||||
if affectedRows > 0 then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich entzogen!', 'success')
|
|
||||||
if targetId then
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, 'Eine deiner Lizenzen wurde entzogen!', 'error')
|
|
||||||
end
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:restoreLicense', function(targetId, licenseId)
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
|
|
||||||
if not Player then return end
|
|
||||||
|
|
||||||
-- Job-Berechtigung prüfen
|
|
||||||
local playerJob = Player.PlayerData.job.name
|
|
||||||
if not Config.AuthorizedJobs[playerJob] then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
MySQL.Async.execute('UPDATE player_licenses SET is_active = TRUE WHERE id = ?', {
|
|
||||||
licenseId
|
|
||||||
}, function(affectedRows)
|
|
||||||
if affectedRows > 0 then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich wiederhergestellt!', 'success')
|
|
||||||
if targetId then
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, 'Eine deiner Lizenzen wurde wiederhergestellt!', 'success')
|
|
||||||
end
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Wiederherstellen der Lizenz!', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Hilfsfunktionen
|
-- Hilfsfunktionen
|
||||||
function hasPermission(permissions, licenseType)
|
local function debugPrint(message)
|
||||||
for _, permission in ipairs(permissions) do
|
if Config.Debug then
|
||||||
if permission == licenseType then
|
print("^2[License-System] " .. message .. "^7")
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function safeCallback(cb, ...)
|
||||||
|
if cb and type(cb) == "function" then
|
||||||
|
cb(...)
|
||||||
|
else
|
||||||
|
debugPrint("^1Callback ist keine Funktion!^7")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function formatDate(date)
|
||||||
|
if not date then return nil end
|
||||||
|
return os.date("%d.%m.%Y", date)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addDaysToDate(days)
|
||||||
|
return os.time() + (days * 24 * 60 * 60)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isLicenseExpired(expireDate)
|
||||||
|
if not expireDate then return false end
|
||||||
|
return os.time() > expireDate
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getDaysUntilExpiry(expireDate)
|
||||||
|
if not expireDate then return nil end
|
||||||
|
local diff = expireDate - os.time()
|
||||||
|
return math.ceil(diff / (24 * 60 * 60))
|
||||||
|
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,
|
||||||
|
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
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Erweiterte Funktionen für Lizenzverwaltung
|
-- Benötigte Items prüfen
|
||||||
|
local function hasRequiredItems(source, licenseType)
|
||||||
|
local Player = QBCore.Functions.GetPlayer(source)
|
||||||
|
if not Player then return false end
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:searchPlayer', function(searchTerm)
|
local licenseConfig = Config.LicenseTypes[licenseType]
|
||||||
local src = source
|
if not licenseConfig or not licenseConfig.required_items then return true end
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
|
|
||||||
if not Player then return 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
|
||||||
|
|
||||||
-- Job-Berechtigung prüfen
|
return true
|
||||||
local playerJob = Player.PlayerData.job.name
|
end
|
||||||
if not Config.AuthorizedJobs[playerJob] then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
-- Lizenz erstellen
|
||||||
|
local function createLicense(citizenid, licenseType, issuedBy, classes)
|
||||||
|
local licenseConfig = Config.LicenseTypes[licenseType]
|
||||||
|
if not licenseConfig then 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 = true,
|
||||||
|
classes = classes and json.encode(classes) or '[]',
|
||||||
|
created_at = issueDate
|
||||||
|
}
|
||||||
|
|
||||||
|
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 erstellt: " .. licenseType .. " für " .. citizenid)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
debugPrint("^1Fehler beim Erstellen der Lizenz!^7")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Callbacks
|
||||||
|
QBCore.Functions.CreateCallback('license-system:server:getLicense', function(source, cb, targetId)
|
||||||
|
debugPrint("getLicense aufgerufen für Spieler: " .. tostring(targetId))
|
||||||
|
|
||||||
|
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
|
||||||
|
if not TargetPlayer then
|
||||||
|
debugPrint("^1Ziel-Spieler nicht gefunden!^7")
|
||||||
|
safeCallback(cb, nil)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spieler suchen
|
local citizenid = TargetPlayer.PlayerData.citizenid
|
||||||
MySQL.Async.fetchAll('SELECT citizenid, charinfo FROM players WHERE JSON_EXTRACT(charinfo, "$.firstname") LIKE ? OR JSON_EXTRACT(charinfo, "$.lastname") LIKE ? LIMIT 10', {
|
|
||||||
'%' .. searchTerm .. '%',
|
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND is_active = TRUE ORDER BY created_at DESC LIMIT 1', {
|
||||||
'%' .. searchTerm .. '%'
|
citizenid
|
||||||
}, function(result)
|
}, function(result)
|
||||||
local players = {}
|
if result and result[1] then
|
||||||
for _, player in ipairs(result) do
|
local license = result[1]
|
||||||
local charinfo = json.decode(player.charinfo)
|
|
||||||
table.insert(players, {
|
-- Spieler-Daten hinzufügen
|
||||||
citizenid = player.citizenid,
|
license.name = TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname
|
||||||
name = charinfo.firstname .. ' ' .. charinfo.lastname
|
license.birthday = TargetPlayer.PlayerData.charinfo.birthdate
|
||||||
})
|
license.gender = TargetPlayer.PlayerData.charinfo.gender
|
||||||
|
|
||||||
|
-- Aussteller-Name abrufen
|
||||||
|
if license.issued_by then
|
||||||
|
MySQL.Async.fetchScalar('SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.firstname")), " ", JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.lastname"))) FROM players WHERE citizenid = ?', {
|
||||||
|
license.issued_by
|
||||||
|
}, function(issuerName)
|
||||||
|
license.issued_by_name = issuerName or 'Unbekannt'
|
||||||
|
|
||||||
|
local licenseData = {
|
||||||
|
license = license,
|
||||||
|
config = Config.LicenseTypes[license.license_type] or {
|
||||||
|
label = license.license_type,
|
||||||
|
icon = 'fas fa-id-card'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("Lizenz gefunden: " .. license.license_type)
|
||||||
|
safeCallback(cb, licenseData)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
license.issued_by_name = 'System'
|
||||||
|
|
||||||
|
local licenseData = {
|
||||||
|
license = license,
|
||||||
|
config = Config.LicenseTypes[license.license_type] or {
|
||||||
|
label = license.license_type,
|
||||||
|
icon = 'fas fa-id-card'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("Lizenz gefunden: " .. license.license_type)
|
||||||
|
safeCallback(cb, licenseData)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
debugPrint("Keine Lizenz gefunden für: " .. citizenid)
|
||||||
|
safeCallback(cb, nil)
|
||||||
end
|
end
|
||||||
TriggerClientEvent('license-system:client:receiveSearchResults', src, players)
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNetEvent('license-system:server:getPlayerLicenses', function(citizenid)
|
QBCore.Functions.CreateCallback('license-system:server:getMyLicense', function(source, cb, licenseType)
|
||||||
local src = source
|
debugPrint("getMyLicense aufgerufen für Typ: " .. tostring(licenseType))
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
|
|
||||||
if not Player then return end
|
local Player = QBCore.Functions.GetPlayer(source)
|
||||||
|
if not Player then
|
||||||
-- Job-Berechtigung prüfen
|
safeCallback(cb, nil)
|
||||||
local playerJob = Player.PlayerData.job.name
|
|
||||||
if not Config.AuthorizedJobs[playerJob] then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local citizenid = Player.PlayerData.citizenid
|
||||||
|
|
||||||
|
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND license_type = ? AND is_active = TRUE ORDER BY created_at DESC LIMIT 1', {
|
||||||
|
citizenid,
|
||||||
|
licenseType
|
||||||
|
}, function(result)
|
||||||
|
if result and result[1] then
|
||||||
|
local license = result[1]
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
if license.issued_by then
|
||||||
|
MySQL.Async.fetchScalar('SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.firstname")), " ", JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.lastname"))) FROM players WHERE citizenid = ?', {
|
||||||
|
license.issued_by
|
||||||
|
}, function(issuerName)
|
||||||
|
license.issued_by_name = issuerName or 'Unbekannt'
|
||||||
|
|
||||||
|
local licenseData = {
|
||||||
|
license = license,
|
||||||
|
config = Config.LicenseTypes[license.license_type] or {
|
||||||
|
label = license.license_type,
|
||||||
|
icon = 'fas fa-id-card'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safeCallback(cb, licenseData)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
license.issued_by_name = 'System'
|
||||||
|
|
||||||
|
local licenseData = {
|
||||||
|
license = license,
|
||||||
|
config = Config.LicenseTypes[license.license_type] or {
|
||||||
|
label = license.license_type,
|
||||||
|
icon = 'fas fa-id-card'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safeCallback(cb, licenseData)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
debugPrint("Keine Lizenz vom Typ " .. licenseType .. " gefunden")
|
||||||
|
safeCallback(cb, nil)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
QBCore.Functions.CreateCallback('license-system:server:getPlayerLicenses', function(source, cb, targetId)
|
||||||
|
debugPrint("getPlayerLicenses aufgerufen für Spieler: " .. tostring(targetId))
|
||||||
|
|
||||||
|
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
|
||||||
|
if not TargetPlayer then
|
||||||
|
safeCallback(cb, {})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local citizenid = TargetPlayer.PlayerData.citizenid
|
||||||
|
|
||||||
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY created_at DESC', {
|
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY created_at DESC', {
|
||||||
citizenid
|
citizenid
|
||||||
}, function(result)
|
}, function(result)
|
||||||
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, result, citizenid)
|
if result then
|
||||||
|
-- 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
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("Gefundene Lizenzen: " .. #result)
|
||||||
|
safeCallback(cb, result)
|
||||||
|
else
|
||||||
|
debugPrint("Keine Lizenzen gefunden")
|
||||||
|
safeCallback(cb, {})
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Lizenz-Historie hinzufügen
|
QBCore.Functions.CreateCallback('license-system:server:canIssueLicense', function(source, cb, licenseType)
|
||||||
function addLicenseHistory(licenseId, action, performedBy, performedByName, reason)
|
local hasAuth = hasPermission(source, licenseType)
|
||||||
MySQL.Async.execute('INSERT INTO license_history (license_id, action, performed_by, performed_by_name, reason) VALUES (?, ?, ?, ?, ?)', {
|
safeCallback(cb, hasAuth)
|
||||||
licenseId,
|
end)
|
||||||
action,
|
|
||||||
performedBy,
|
|
||||||
performedByName,
|
|
||||||
reason or ''
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Erweiterte Revoke-Funktion mit Grund
|
-- Events
|
||||||
RegisterNetEvent('license-system:server:revokeLicenseWithReason', function(licenseId, reason)
|
RegisterNetEvent('license-system:server:issueLicense', function(targetId, licenseType, classes)
|
||||||
|
local src = source
|
||||||
|
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')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Alte Lizenz deaktivieren
|
||||||
|
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE WHERE citizenid = ? AND license_type = ?', {
|
||||||
|
TargetPlayer.PlayerData.citizenid,
|
||||||
|
licenseType
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
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 = FALSE WHERE citizenid = ? AND license_type = ? AND is_active = TRUE', {
|
||||||
|
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]?.label or licenseType) .. ' wurde entzogen!', 'error')
|
||||||
|
|
||||||
|
-- Log erstellen
|
||||||
|
debugPrint(Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname .. ' hat ' .. TargetPlayer.PlayerData.charinfo.firstname .. ' ' .. TargetPlayer.PlayerData.charinfo.lastname .. ' die ' .. (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
|
local src = source
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
|
||||||
if not Player then return end
|
if not Player then return end
|
||||||
|
|
||||||
local playerJob = Player.PlayerData.job.name
|
-- Foto in der Datenbank speichern
|
||||||
if not Config.AuthorizedJobs[playerJob] then
|
MySQL.Async.execute('UPDATE player_licenses SET photo_url = ? WHERE citizenid = ? AND is_active = TRUE', {
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung für diese Aktion!', 'error')
|
photoData,
|
||||||
return
|
citizenid
|
||||||
end
|
|
||||||
|
|
||||||
local playerName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
|
|
||||||
|
|
||||||
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE, revoked_by = ?, revoked_by_name = ?, revoked_date = NOW(), revoked_reason = ? WHERE id = ?', {
|
|
||||||
Player.PlayerData.citizenid,
|
|
||||||
playerName,
|
|
||||||
reason,
|
|
||||||
licenseId
|
|
||||||
}, function(affectedRows)
|
}, function(affectedRows)
|
||||||
if affectedRows > 0 then
|
if affectedRows > 0 then
|
||||||
addLicenseHistory(licenseId, 'revoked', Player.PlayerData.citizenid, playerName, reason)
|
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.photo_saved.message, Config.Notifications.photo_saved.type)
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich entzogen!', 'success')
|
debugPrint("Foto gespeichert für: " .. citizenid)
|
||||||
else
|
else
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error')
|
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Speichern des Fotos!', 'error')
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Automatische Lizenz-Überprüfung (läuft alle 24 Stunden)
|
-- 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
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
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 = FALSE 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()
|
CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
Wait(24 * 60 * 60 * 1000) -- 24 Stunden
|
Wait(60 * 60 * 1000) -- Jede Stunde
|
||||||
|
|
||||||
-- Abgelaufene Lizenzen deaktivieren
|
MySQL.Async.fetchAll('SELECT * FROM player_licenses WHERE is_active = TRUE AND expire_date IS NOT NULL', {}, function(result)
|
||||||
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE WHERE expire_date < CURDATE() AND is_active = TRUE', {}, function(affectedRows)
|
if result then
|
||||||
if affectedRows > 0 then
|
for _, license in ipairs(result) do
|
||||||
print('^3[License-System]^7 ' .. affectedRows .. ' abgelaufene Lizenzen wurden deaktiviert.')
|
local expireTime = os.time({
|
||||||
|
year = tonumber(string.sub(license.expire_date, 7, 10)),
|
||||||
|
month = tonumber(string.sub(license.expire_date, 4, 5)),
|
||||||
|
day = tonumber(string.sub(license.expire_date, 1, 2))
|
||||||
|
})
|
||||||
|
|
||||||
|
if isLicenseExpired(expireTime) then
|
||||||
|
-- Lizenz als abgelaufen markieren
|
||||||
|
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE WHERE id = ?', {
|
||||||
|
license.id
|
||||||
|
})
|
||||||
|
|
||||||
|
debugPrint("Lizenz abgelaufen: " .. license.license_type .. " für " .. license.citizenid)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Export-Funktionen für andere Ressourcen
|
-- Resource Start/Stop Events
|
||||||
exports('hasLicense', function(citizenid, licenseType)
|
AddEventHandler('onResourceStart', function(resourceName)
|
||||||
local result = MySQL.Sync.fetchScalar('SELECT COUNT(*) FROM player_licenses WHERE citizenid = ? AND license_type = ? AND is_active = TRUE', {
|
if GetCurrentResourceName() == resourceName then
|
||||||
citizenid, licenseType
|
debugPrint("License-System Server gestartet")
|
||||||
})
|
|
||||||
return result > 0
|
|
||||||
end)
|
|
||||||
|
|
||||||
exports('getLicenses', function(citizenid)
|
-- Datenbank-Tabelle erstellen falls nicht vorhanden
|
||||||
return MySQL.Sync.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND is_active = TRUE', {
|
MySQL.Async.execute([[
|
||||||
citizenid
|
CREATE TABLE IF NOT EXISTS player_licenses (
|
||||||
})
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
end)
|
citizenid VARCHAR(50) NOT NULL,
|
||||||
|
license_type VARCHAR(50) NOT NULL,
|
||||||
exports('hasLicenseClass', function(citizenid, class)
|
issue_date VARCHAR(20) NOT NULL,
|
||||||
local result = MySQL.Sync.fetchAll('SELECT classes FROM player_licenses WHERE citizenid = ? AND license_type = "drivers_license" AND is_active = TRUE', {
|
expire_date VARCHAR(20) NULL,
|
||||||
citizenid
|
issued_by VARCHAR(50) NULL,
|
||||||
})
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
classes TEXT NULL,
|
||||||
for _, license in ipairs(result) do
|
photo_url TEXT NULL,
|
||||||
if license.classes then
|
notes TEXT NULL,
|
||||||
local classes = json.decode(license.classes)
|
created_at BIGINT NOT NULL,
|
||||||
for _, licenseClass in ipairs(classes) do
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
if licenseClass == class then
|
INDEX idx_citizenid (citizenid),
|
||||||
return true
|
INDEX idx_license_type (license_type),
|
||||||
end
|
INDEX idx_active (is_active)
|
||||||
end
|
)
|
||||||
end
|
]])
|
||||||
end
|
|
||||||
return false
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Spieler-Foto aus QBCore holen
|
|
||||||
RegisterNetEvent('license-system:server:getPlayerPhoto', function(targetId)
|
|
||||||
local src = source
|
|
||||||
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
|
|
||||||
if TargetPlayer then
|
|
||||||
-- Mugshot vom Spieler machen
|
|
||||||
TriggerClientEvent('license-system:client:takePlayerPhoto', src, targetId)
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Foto-URL speichern
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
RegisterNetEvent('license-system:server:savePlayerPhoto', function(citizenid, photoUrl)
|
if GetCurrentResourceName() == resourceName then
|
||||||
MySQL.Async.execute('UPDATE player_licenses SET photo_url = ? WHERE citizenid = ? AND id = ?', {
|
debugPrint("License-System Server gestoppt")
|
||||||
photoUrl,
|
end
|
||||||
citizenid,
|
|
||||||
-- Hier die aktuelle Lizenz-ID
|
|
||||||
})
|
|
||||||
end)
|
end)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue