1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-08-04 06:14:47 +02:00
parent a44c08fe7e
commit 4f3cd097a2
7 changed files with 1208 additions and 0 deletions

View 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

View 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'}
}

View 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'
}

View 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>

View 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();
}
});

View 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;
}

View 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)