forked from Simnation/Main
ed
This commit is contained in:
parent
a44c08fe7e
commit
4f3cd097a2
7 changed files with 1208 additions and 0 deletions
391
resources/[tools]/nordi_license/client/main.lua
Normal file
391
resources/[tools]/nordi_license/client/main.lua
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
local playerLicenses = {}
|
||||||
|
local nearbyPlayers = {}
|
||||||
|
|
||||||
|
-- Keybind registrieren
|
||||||
|
RegisterKeyMapping(Config.Command, 'Lizenz-Menü öffnen', 'keyboard', Config.OpenKey)
|
||||||
|
|
||||||
|
-- Events
|
||||||
|
RegisterNetEvent('license-system:client:openMenu', function()
|
||||||
|
TriggerServerEvent('license-system:server:getLicenses')
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:client:receiveLicenses', function(licenses)
|
||||||
|
playerLicenses = licenses
|
||||||
|
openMainMenu()
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:client:receiveNearbyPlayers', function(players)
|
||||||
|
nearbyPlayers = {}
|
||||||
|
local playerPed = PlayerPedId()
|
||||||
|
local playerCoords = GetEntityCoords(playerPed)
|
||||||
|
|
||||||
|
for _, playerId in ipairs(players) do
|
||||||
|
if playerId ~= GetPlayerServerId(PlayerId()) then
|
||||||
|
local targetPed = GetPlayerPed(GetPlayerFromServerId(playerId))
|
||||||
|
if targetPed and targetPed ~= 0 then
|
||||||
|
local targetCoords = GetEntityCoords(targetPed)
|
||||||
|
local distance = #(playerCoords - targetCoords)
|
||||||
|
|
||||||
|
if distance <= 5.0 then
|
||||||
|
local targetPlayer = QBCore.Functions.GetPlayerData(playerId)
|
||||||
|
table.insert(nearbyPlayers, {
|
||||||
|
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)
|
||||||
|
showLicenseOverlay(licenseData, issuerName)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Hauptmenü
|
||||||
|
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
|
||||||
|
|
||||||
|
if #licenseOptions == 0 then
|
||||||
|
lib.notify({
|
||||||
|
title = 'Lizenz-System',
|
||||||
|
description = 'Du hast keine aktiven Lizenzen zum Zeigen',
|
||||||
|
type = 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'select_license_to_show',
|
||||||
|
title = 'Lizenz auswählen',
|
||||||
|
menu = 'license_main_menu',
|
||||||
|
options = licenseOptions
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.showContext('select_license_to_show')
|
||||||
|
end
|
||||||
|
|
||||||
|
function selectPlayerToShow(license)
|
||||||
|
local playerOptions = {}
|
||||||
|
|
||||||
|
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 authorizedLicenses = Config.AuthorizedJobs[PlayerData.job.name].canIssue
|
||||||
|
|
||||||
|
local options = {}
|
||||||
|
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
|
||||||
|
|
||||||
|
function selectPlayerForLicense(licenseType)
|
||||||
|
TriggerServerEvent('license-system:server:getNearbyPlayers')
|
||||||
|
|
||||||
|
Wait(500)
|
||||||
|
|
||||||
|
if #nearbyPlayers == 0 then
|
||||||
|
lib.notify({
|
||||||
|
title = 'Lizenz-System',
|
||||||
|
description = 'Keine Spieler in der Nähe gefunden',
|
||||||
|
type = 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local playerOptions = {}
|
||||||
|
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({
|
||||||
|
action = 'showLicense',
|
||||||
|
data = {
|
||||||
|
license = licenseData,
|
||||||
|
config = licenseConfig,
|
||||||
|
issuer = issuerName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- NUI Callbacks
|
||||||
|
RegisterNUICallback('closeLicense', function(data, cb)
|
||||||
|
SetNuiFocus(false, false)
|
||||||
|
cb('ok')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Lizenz verwalten (für autorisierte Jobs)
|
||||||
|
function manageLicensesMenu()
|
||||||
|
-- Hier würde die Verwaltung für das Entziehen/Wiederherstellen von Lizenzen implementiert
|
||||||
|
-- Ähnlich wie die anderen Menüs, aber mit Suchfunktion nach Spielern
|
||||||
|
lib.notify({
|
||||||
|
title = 'Lizenz-System',
|
||||||
|
description = 'Lizenz-Verwaltung wird implementiert...',
|
||||||
|
type = 'info'
|
||||||
|
})
|
||||||
|
end
|
107
resources/[tools]/nordi_license/config.lua
Normal file
107
resources/[tools]/nordi_license/config.lua
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
Config = {}
|
||||||
|
|
||||||
|
-- Keybind zum Öffnen des Menüs
|
||||||
|
Config.OpenKey = 'F6'
|
||||||
|
|
||||||
|
-- Command zum Öffnen
|
||||||
|
Config.Command = 'licenses'
|
||||||
|
|
||||||
|
-- Jobs die Lizenzen ausstellen können
|
||||||
|
Config.AuthorizedJobs = {
|
||||||
|
['police'] = {
|
||||||
|
canIssue = {'id_card', 'drivers_license', 'weapon_license'},
|
||||||
|
canRevoke = {'drivers_license', 'weapon_license'},
|
||||||
|
canRestore = {'drivers_license', 'weapon_license'}
|
||||||
|
},
|
||||||
|
['government'] = {
|
||||||
|
canIssue = {'id_card', 'drivers_license', 'passport', 'business_license'},
|
||||||
|
canRevoke = {'drivers_license', 'business_license'},
|
||||||
|
canRestore = {'drivers_license', 'business_license'}
|
||||||
|
},
|
||||||
|
['driving_school'] = {
|
||||||
|
canIssue = {'drivers_license'},
|
||||||
|
canRevoke = {},
|
||||||
|
canRestore = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Verfügbare Lizenzen/Ausweise
|
||||||
|
Config.Licenses = {
|
||||||
|
['id_card'] = {
|
||||||
|
label = 'Personalausweis',
|
||||||
|
icon = 'fas fa-id-card',
|
||||||
|
fields = {
|
||||||
|
name = true,
|
||||||
|
birthday = true,
|
||||||
|
gender = true,
|
||||||
|
issue_date = true,
|
||||||
|
expire_date = true,
|
||||||
|
classes = false
|
||||||
|
},
|
||||||
|
template = 'id_card'
|
||||||
|
},
|
||||||
|
['drivers_license'] = {
|
||||||
|
label = 'Führerschein',
|
||||||
|
icon = 'fas fa-car',
|
||||||
|
fields = {
|
||||||
|
name = true,
|
||||||
|
birthday = true,
|
||||||
|
gender = true,
|
||||||
|
issue_date = true,
|
||||||
|
expire_date = true,
|
||||||
|
classes = true
|
||||||
|
},
|
||||||
|
template = 'drivers_license'
|
||||||
|
},
|
||||||
|
['weapon_license'] = {
|
||||||
|
label = 'Waffenschein',
|
||||||
|
icon = 'fas fa-gun',
|
||||||
|
fields = {
|
||||||
|
name = true,
|
||||||
|
birthday = true,
|
||||||
|
gender = true,
|
||||||
|
issue_date = true,
|
||||||
|
expire_date = true,
|
||||||
|
classes = false
|
||||||
|
},
|
||||||
|
template = 'weapon_license'
|
||||||
|
},
|
||||||
|
['passport'] = {
|
||||||
|
label = 'Reisepass',
|
||||||
|
icon = 'fas fa-passport',
|
||||||
|
fields = {
|
||||||
|
name = true,
|
||||||
|
birthday = true,
|
||||||
|
gender = true,
|
||||||
|
issue_date = true,
|
||||||
|
expire_date = true,
|
||||||
|
classes = false
|
||||||
|
},
|
||||||
|
template = 'passport'
|
||||||
|
},
|
||||||
|
['business_license'] = {
|
||||||
|
label = 'Gewerbeschein',
|
||||||
|
icon = 'fas fa-briefcase',
|
||||||
|
fields = {
|
||||||
|
name = true,
|
||||||
|
birthday = false,
|
||||||
|
gender = false,
|
||||||
|
issue_date = true,
|
||||||
|
expire_date = true,
|
||||||
|
classes = false
|
||||||
|
},
|
||||||
|
template = 'business_license'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Führerscheinklassen
|
||||||
|
Config.LicenseClasses = {
|
||||||
|
'A', 'B', 'C', 'Boot', 'Flugzeug'
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Geschlechter
|
||||||
|
Config.Genders = {
|
||||||
|
{value = 'male', label = 'Männlich'},
|
||||||
|
{value = 'female', label = 'Weiblich'},
|
||||||
|
{value = 'other', label = 'Divers'}
|
||||||
|
}
|
35
resources/[tools]/nordi_license/fxmanifest.lua
Normal file
35
resources/[tools]/nordi_license/fxmanifest.lua
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
fx_version 'cerulean'
|
||||||
|
game 'gta5'
|
||||||
|
|
||||||
|
author 'YourName'
|
||||||
|
description 'License System for QBCore'
|
||||||
|
version '1.0.0'
|
||||||
|
|
||||||
|
shared_scripts {
|
||||||
|
'@ox_lib/init.lua',
|
||||||
|
'config.lua'
|
||||||
|
}
|
||||||
|
|
||||||
|
client_scripts {
|
||||||
|
'client/main.lua'
|
||||||
|
}
|
||||||
|
|
||||||
|
server_scripts {
|
||||||
|
'@oxmysql/lib/MySQL.lua',
|
||||||
|
'server/main.lua'
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_page 'html/index.html'
|
||||||
|
|
||||||
|
files {
|
||||||
|
'html/index.html',
|
||||||
|
'html/style.css',
|
||||||
|
'html/script.js',
|
||||||
|
'html/images/*.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
'qb-core',
|
||||||
|
'ox_lib',
|
||||||
|
'oxmysql'
|
||||||
|
}
|
60
resources/[tools]/nordi_license/html/index.html
Normal file
60
resources/[tools]/nordi_license/html/index.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>License System</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="license-container" class="hidden">
|
||||||
|
<div class="license-card" id="license-card">
|
||||||
|
<div class="license-header">
|
||||||
|
<div class="license-title"></div>
|
||||||
|
<div class="license-logo">
|
||||||
|
<i class="license-icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="license-content">
|
||||||
|
<div class="license-photo">
|
||||||
|
<i class="fas fa-user"></i>
|
||||||
|
</div>
|
||||||
|
<div class="license-info">
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">Name:</span>
|
||||||
|
<span class="value" id="license-name"></span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">Geburtsdatum:</span>
|
||||||
|
<span class="value" id="license-birthday"></span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">Geschlecht:</span>
|
||||||
|
<span class="value" id="license-gender"></span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">Ausgestellt:</span>
|
||||||
|
<span class="value" id="license-issue"></span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">Gültig bis:</span>
|
||||||
|
<span class="value" id="license-expire"></span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row" id="license-classes-row">
|
||||||
|
<span class="label">Klassen:</span>
|
||||||
|
<span class="value" id="license-classes"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="license-footer">
|
||||||
|
<div class="license-status" id="license-status"></div>
|
||||||
|
<button class="close-btn" onclick="closeLicense()">
|
||||||
|
<i class="fas fa-times"></i> Schließen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
103
resources/[tools]/nordi_license/html/script.js
Normal file
103
resources/[tools]/nordi_license/html/script.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
let currentLicense = null;
|
||||||
|
|
||||||
|
// Event Listener für Nachrichten von FiveM
|
||||||
|
window.addEventListener('message', function(event) {
|
||||||
|
const data = event.data;
|
||||||
|
|
||||||
|
switch(data.action) {
|
||||||
|
case 'showLicense':
|
||||||
|
showLicense(data.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lizenz anzeigen
|
||||||
|
function showLicense(data) {
|
||||||
|
currentLicense = data;
|
||||||
|
const container = document.getElementById('license-container');
|
||||||
|
const card = document.getElementById('license-card');
|
||||||
|
|
||||||
|
// Lizenztyp-spezifische Klasse hinzufügen
|
||||||
|
card.className = 'license-card ' + data.license.license_type;
|
||||||
|
|
||||||
|
// Header befüllen
|
||||||
|
document.querySelector('.license-title').textContent = data.config.label;
|
||||||
|
document.querySelector('.license-icon').className = 'license-icon ' + data.config.icon;
|
||||||
|
|
||||||
|
// Lizenzinformationen befüllen
|
||||||
|
document.getElementById('license-name').textContent = data.license.name || 'N/A';
|
||||||
|
document.getElementById('license-birthday').textContent = data.license.birthday || 'N/A';
|
||||||
|
document.getElementById('license-gender').textContent = formatGender(data.license.gender) || 'N/A';
|
||||||
|
document.getElementById('license-issue').textContent = data.license.issue_date || 'N/A';
|
||||||
|
document.getElementById('license-expire').textContent = data.license.expire_date || 'N/A';
|
||||||
|
|
||||||
|
// Klassen anzeigen (nur bei Führerschein)
|
||||||
|
const classesRow = document.getElementById('license-classes-row');
|
||||||
|
if (data.license.classes && data.license.classes !== '[]') {
|
||||||
|
try {
|
||||||
|
const classes = JSON.parse(data.license.classes);
|
||||||
|
if (classes && classes.length > 0) {
|
||||||
|
document.getElementById('license-classes').textContent = classes.join(', ');
|
||||||
|
classesRow.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
classesRow.style.display = 'none';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
classesRow.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
classesRow.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status anzeigen
|
||||||
|
const statusElement = document.getElementById('license-status');
|
||||||
|
if (data.license.is_active) {
|
||||||
|
statusElement.textContent = '✅ Gültig';
|
||||||
|
statusElement.className = 'license-status active';
|
||||||
|
} else {
|
||||||
|
statusElement.textContent = '❌ Ungültig';
|
||||||
|
statusElement.className = 'license-status inactive';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container anzeigen
|
||||||
|
container.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geschlecht formatieren
|
||||||
|
function formatGender(gender) {
|
||||||
|
const genderMap = {
|
||||||
|
'male': 'Männlich',
|
||||||
|
'female': 'Weiblich',
|
||||||
|
'other': 'Divers'
|
||||||
|
};
|
||||||
|
return genderMap[gender] || gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lizenz schließen
|
||||||
|
function closeLicense() {
|
||||||
|
const container = document.getElementById('license-container');
|
||||||
|
container.classList.add('hidden');
|
||||||
|
|
||||||
|
// Callback an FiveM senden
|
||||||
|
fetch(`https://${GetParentResourceName()}/closeLicense`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESC-Taste zum Schließen
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeLicense();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Klick außerhalb der Karte zum Schließen
|
||||||
|
document.getElementById('license-container').addEventListener('click', function(event) {
|
||||||
|
if (event.target === this) {
|
||||||
|
closeLicense();
|
||||||
|
}
|
||||||
|
});
|
220
resources/[tools]/nordi_license/html/style.css
Normal file
220
resources/[tools]/nordi_license/html/style.css
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
background: transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#license-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||||
|
width: 450px;
|
||||||
|
min-height: 300px;
|
||||||
|
color: white;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="75" cy="75" r="1" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-logo {
|
||||||
|
font-size: 32px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-content {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-photo {
|
||||||
|
width: 100px;
|
||||||
|
height: 120px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 48px;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.9;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
text-align: right;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-footer {
|
||||||
|
padding: 20px;
|
||||||
|
border-top: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-status {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-status.active {
|
||||||
|
background: rgba(76, 175, 80, 0.3);
|
||||||
|
color: #4CAF50;
|
||||||
|
border: 1px solid #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-status.inactive {
|
||||||
|
background: rgba(244, 67, 54, 0.3);
|
||||||
|
color: #F44336;
|
||||||
|
border: 1px solid #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spezielle Styles für verschiedene Lizenztypen */
|
||||||
|
.license-card.id_card {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card.drivers_license {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card.weapon_license {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card.passport {
|
||||||
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card.business_license {
|
||||||
|
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.license-card {
|
||||||
|
width: 90vw;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-photo {
|
||||||
|
width: 80px;
|
||||||
|
height: 100px;
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation für das Erscheinen */
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50px) scale(0.9);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-card {
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
}
|
292
resources/[tools]/nordi_license/server/main.lua
Normal file
292
resources/[tools]/nordi_license/server/main.lua
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
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
|
||||||
|
function hasPermission(permissions, licenseType)
|
||||||
|
for _, permission in ipairs(permissions) do
|
||||||
|
if permission == licenseType then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Erweiterte Funktionen für Lizenzverwaltung
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:server:searchPlayer', function(searchTerm)
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Spieler suchen
|
||||||
|
MySQL.Async.fetchAll('SELECT citizenid, charinfo FROM players WHERE JSON_EXTRACT(charinfo, "$.firstname") LIKE ? OR JSON_EXTRACT(charinfo, "$.lastname") LIKE ? LIMIT 10', {
|
||||||
|
'%' .. searchTerm .. '%',
|
||||||
|
'%' .. searchTerm .. '%'
|
||||||
|
}, function(result)
|
||||||
|
local players = {}
|
||||||
|
for _, player in ipairs(result) do
|
||||||
|
local charinfo = json.decode(player.charinfo)
|
||||||
|
table.insert(players, {
|
||||||
|
citizenid = player.citizenid,
|
||||||
|
name = charinfo.firstname .. ' ' .. charinfo.lastname
|
||||||
|
})
|
||||||
|
end
|
||||||
|
TriggerClientEvent('license-system:client:receiveSearchResults', src, players)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('license-system:server:getPlayerLicenses', function(citizenid)
|
||||||
|
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.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY created_at DESC', {
|
||||||
|
citizenid
|
||||||
|
}, function(result)
|
||||||
|
TriggerClientEvent('license-system:client:receivePlayerLicenses', src, result, citizenid)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Lizenz-Historie hinzufügen
|
||||||
|
function addLicenseHistory(licenseId, action, performedBy, performedByName, reason)
|
||||||
|
MySQL.Async.execute('INSERT INTO license_history (license_id, action, performed_by, performed_by_name, reason) VALUES (?, ?, ?, ?, ?)', {
|
||||||
|
licenseId,
|
||||||
|
action,
|
||||||
|
performedBy,
|
||||||
|
performedByName,
|
||||||
|
reason or ''
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Erweiterte Revoke-Funktion mit Grund
|
||||||
|
RegisterNetEvent('license-system:server:revokeLicenseWithReason', function(licenseId, reason)
|
||||||
|
local src = source
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
|
||||||
|
if not Player then return end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
if affectedRows > 0 then
|
||||||
|
addLicenseHistory(licenseId, 'revoked', Player.PlayerData.citizenid, playerName, reason)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich entzogen!', 'success')
|
||||||
|
else
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Automatische Lizenz-Überprüfung (läuft alle 24 Stunden)
|
||||||
|
CreateThread(function()
|
||||||
|
while true do
|
||||||
|
Wait(24 * 60 * 60 * 1000) -- 24 Stunden
|
||||||
|
|
||||||
|
-- Abgelaufene Lizenzen deaktivieren
|
||||||
|
MySQL.Async.execute('UPDATE player_licenses SET is_active = FALSE WHERE expire_date < CURDATE() AND is_active = TRUE', {}, function(affectedRows)
|
||||||
|
if affectedRows > 0 then
|
||||||
|
print('^3[License-System]^7 ' .. affectedRows .. ' abgelaufene Lizenzen wurden deaktiviert.')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Export-Funktionen für andere Ressourcen
|
||||||
|
exports('hasLicense', function(citizenid, licenseType)
|
||||||
|
local result = MySQL.Sync.fetchScalar('SELECT COUNT(*) FROM player_licenses WHERE citizenid = ? AND license_type = ? AND is_active = TRUE', {
|
||||||
|
citizenid, licenseType
|
||||||
|
})
|
||||||
|
return result > 0
|
||||||
|
end)
|
||||||
|
|
||||||
|
exports('getLicenses', function(citizenid)
|
||||||
|
return MySQL.Sync.fetchAll('SELECT * FROM player_licenses WHERE citizenid = ? AND is_active = TRUE', {
|
||||||
|
citizenid
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
exports('hasLicenseClass', function(citizenid, class)
|
||||||
|
local result = MySQL.Sync.fetchAll('SELECT classes FROM player_licenses WHERE citizenid = ? AND license_type = "drivers_license" AND is_active = TRUE', {
|
||||||
|
citizenid
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, license in ipairs(result) do
|
||||||
|
if license.classes then
|
||||||
|
local classes = json.decode(license.classes)
|
||||||
|
for _, licenseClass in ipairs(classes) do
|
||||||
|
if licenseClass == class then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end)
|
Loading…
Add table
Add a link
Reference in a new issue