188 lines
7.1 KiB
Lua
188 lines
7.1 KiB
Lua
-- ═══════════════════════════════════════════════════════════════
|
||
-- MercyV Bike – Server
|
||
-- ═══════════════════════════════════════════════════════════════
|
||
|
||
local ESX = nil
|
||
|
||
Citizen.CreateThread(function()
|
||
if Config.NewESX then
|
||
ESX = exports['es_extended']:getSharedObject()
|
||
end
|
||
end)
|
||
|
||
local function GetIdentifier(src)
|
||
local xp = ESX and ESX.GetPlayerFromId(src)
|
||
return xp and xp.identifier or nil
|
||
end
|
||
|
||
local function IsAdmin(src)
|
||
if IsPlayerAceAllowed(src, Config.AdminAce) then return true end
|
||
if ESX then
|
||
local xp = ESX.GetPlayerFromId(src)
|
||
if xp then
|
||
local g = xp.getGroup()
|
||
if g == "admin" or g == "superadmin" then return true end
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- ── Tabelle erstellen ──────────────────────────────────────────
|
||
|
||
AddEventHandler('onResourceStart', function(res)
|
||
if res ~= GetCurrentResourceName() then return end
|
||
Wait(1000)
|
||
MySQL.query.await([[
|
||
CREATE TABLE IF NOT EXISTS `mercyv_bike_claims` (
|
||
`identifier` VARCHAR(120) NOT NULL,
|
||
`bike_model` VARCHAR(60) NOT NULL,
|
||
`claimed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`identifier`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||
]])
|
||
print('^2[mercyv-bike]^0 Tabelle bereit.')
|
||
end)
|
||
|
||
-- ── NPC-Position synchronisieren ──────────────────────────────
|
||
|
||
local NPCData = nil -- wird aus DB oder Config geladen
|
||
|
||
AddEventHandler('onResourceStart', function(res)
|
||
if res ~= GetCurrentResourceName() then return end
|
||
Wait(1500)
|
||
-- NPC-Position aus DB laden falls gespeichert
|
||
local r = MySQL.query.await("SELECT * FROM mercyv_bike_npc LIMIT 1")
|
||
if r and r[1] then
|
||
NPCData = r[1]
|
||
else
|
||
NPCData = {
|
||
model = Config.NPC.model,
|
||
x = Config.NPC.x,
|
||
y = Config.NPC.y,
|
||
z = Config.NPC.z,
|
||
heading = Config.NPC.heading,
|
||
}
|
||
end
|
||
-- NPC-Tabelle anlegen falls nicht vorhanden
|
||
MySQL.query.await([[
|
||
CREATE TABLE IF NOT EXISTS `mercyv_bike_npc` (
|
||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||
`model` VARCHAR(100) DEFAULT 'a_m_m_beach_01',
|
||
`x` FLOAT DEFAULT 0,
|
||
`y` FLOAT DEFAULT 0,
|
||
`z` FLOAT DEFAULT 0,
|
||
`heading` FLOAT DEFAULT 0
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||
]])
|
||
TriggerClientEvent('mercyv-bike:syncNPC', -1, NPCData)
|
||
print('^2[mercyv-bike]^0 NPC-Daten geladen.')
|
||
end)
|
||
|
||
-- ── Client ready ───────────────────────────────────────────────
|
||
|
||
RegisterNetEvent('mercyv-bike:clientReady', function()
|
||
local src = source
|
||
Citizen.CreateThread(function()
|
||
Wait(500)
|
||
TriggerClientEvent('mercyv-bike:syncNPC', src, NPCData)
|
||
Wait(200)
|
||
TriggerClientEvent('mercyv-bike:setAdminStatus', src, IsAdmin(src))
|
||
end)
|
||
end)
|
||
|
||
RegisterNetEvent('mercyv-bike:checkAdmin', function()
|
||
TriggerClientEvent('mercyv-bike:setAdminStatus', source, IsAdmin(source))
|
||
end)
|
||
|
||
-- ── Fahrrad abholen ────────────────────────────────────────────
|
||
|
||
RegisterNetEvent('mercyv-bike:claim', function(bikeModel)
|
||
local src = source
|
||
local identifier = GetIdentifier(src)
|
||
if not identifier then return end
|
||
|
||
-- Prüfen ob bereits eines abgeholt
|
||
local existing = MySQL.query.await(
|
||
'SELECT identifier FROM mercyv_bike_claims WHERE identifier = ?',
|
||
{ identifier }
|
||
)
|
||
if existing and existing[1] then
|
||
Config.ServerNotification(src, Config.Notify.ALREADY_CLAIMED, "error")
|
||
return
|
||
end
|
||
|
||
-- Eintragen
|
||
MySQL.insert(
|
||
'INSERT INTO mercyv_bike_claims (identifier, bike_model) VALUES (?, ?)',
|
||
{ identifier, bikeModel }
|
||
)
|
||
|
||
-- Spawn-Signal
|
||
TriggerClientEvent('mercyv-bike:doSpawn', src, bikeModel)
|
||
Config.ServerNotification(src, Config.Notify.CLAIMED, "success")
|
||
print(string.format('[mercyv-bike] %s hat %s erhalten.', identifier, bikeModel))
|
||
end)
|
||
|
||
-- ── Prüfen ob Spieler bereits eines hat ───────────────────────
|
||
|
||
RegisterNetEvent('mercyv-bike:checkClaim', function()
|
||
local src = source
|
||
local identifier = GetIdentifier(src)
|
||
if not identifier then
|
||
TriggerClientEvent('mercyv-bike:claimStatus', src, false)
|
||
return
|
||
end
|
||
local r = MySQL.query.await(
|
||
'SELECT bike_model FROM mercyv_bike_claims WHERE identifier = ?',
|
||
{ identifier }
|
||
)
|
||
TriggerClientEvent('mercyv-bike:claimStatus', src, r and r[1] ~= nil, r and r[1] and r[1].bike_model)
|
||
end)
|
||
|
||
-- ── Admin: NPC-Position speichern ─────────────────────────────
|
||
|
||
RegisterNetEvent('mercyv-bike:saveNPC', function(data)
|
||
local src = source
|
||
if not IsAdmin(src) then
|
||
Config.ServerNotification(src, Config.Notify.NO_ACCESS, "error")
|
||
return
|
||
end
|
||
|
||
NPCData = data
|
||
|
||
-- In DB speichern
|
||
local existing = MySQL.query.await('SELECT id FROM mercyv_bike_npc LIMIT 1')
|
||
if existing and existing[1] then
|
||
MySQL.update('UPDATE mercyv_bike_npc SET model=?, x=?, y=?, z=?, heading=? WHERE id=?',
|
||
{ data.model, data.x, data.y, data.z, data.heading, existing[1].id })
|
||
else
|
||
MySQL.insert('INSERT INTO mercyv_bike_npc (model, x, y, z, heading) VALUES (?, ?, ?, ?, ?)',
|
||
{ data.model, data.x, data.y, data.z, data.heading })
|
||
end
|
||
|
||
TriggerClientEvent('mercyv-bike:syncNPC', -1, NPCData)
|
||
Config.ServerNotification(src, "NPC-Position gespeichert.", "success")
|
||
end)
|
||
|
||
-- ── Benachrichtigung ──────────────────────────────────────────
|
||
|
||
RegisterNetEvent('mercyv-bike:notify', function(msg, type)
|
||
-- Wird clientseitig behandelt
|
||
end)
|
||
|
||
-- ── Admin: Claim zurücksetzen ─────────────────────────────────
|
||
|
||
RegisterCommand('bikereset', function(src, args)
|
||
if not IsAdmin(src) then return end
|
||
local targetId = tonumber(args[1])
|
||
if not targetId then
|
||
print('[mercyv-bike] Verwendung: /bikereset [Spieler-ID]')
|
||
return
|
||
end
|
||
local xp = ESX and ESX.GetPlayerFromId(targetId)
|
||
if not xp then return end
|
||
MySQL.update('DELETE FROM mercyv_bike_claims WHERE identifier = ?', { xp.identifier })
|
||
Config.ServerNotification(src, 'Claim von Spieler ' .. targetId .. ' zurückgesetzt.', 'success')
|
||
Config.ServerNotification(targetId, 'Dein Fahrrad-Claim wurde zurückgesetzt.', 'info')
|
||
end, false)
|