121 lines
4.2 KiB
Lua
121 lines
4.2 KiB
Lua
-- d4rk_tablet — Bridge Server
|
|
-- Token holen (Shared-Secret), Dienst + Positionen an /bridge/* pushen, Dispatch-Export.
|
|
|
|
local onDuty = {} -- [src] = { citizenid, name, callsign, department }
|
|
|
|
--- HTTP POST an das Backend (mit Shared-Secret-Header).
|
|
local function post(path, body, cb)
|
|
PerformHttpRequest(Config.BackendUrl .. path, function(status, res)
|
|
if cb then cb(status, res) end
|
|
end, 'POST', json.encode(body), {
|
|
['Content-Type'] = 'application/json',
|
|
['x-bridge-secret'] = Config.Secret,
|
|
})
|
|
end
|
|
|
|
--- Baut den Auth-Body aus den QBox-Charakterdaten.
|
|
local function authBody(player)
|
|
local pd = player.PlayerData
|
|
local job = pd.job or {}
|
|
local grade = job.grade or {}
|
|
return {
|
|
license = pd.license,
|
|
citizenid = pd.citizenid,
|
|
name = ('%s %s'):format(pd.charinfo.firstname, pd.charinfo.lastname),
|
|
job = job.name,
|
|
gradeLevel = grade.level or 0,
|
|
callsign = pd.metadata and pd.metadata.callsign or nil,
|
|
}
|
|
end
|
|
|
|
-- ── Token holen (Client öffnet Tablet) ──
|
|
lib.callback.register('d4rk_tablet:getAuth', function(source)
|
|
local player = exports.qbx_core:GetPlayer(source)
|
|
if not player then return nil end
|
|
if not Config.JobDepartments[player.PlayerData.job.name] then return nil end -- kein Behörden-Job
|
|
|
|
local p = promise.new()
|
|
post('/auth/fivem', authBody(player), function(status, res)
|
|
if status == 200 and res then
|
|
local ok, data = pcall(json.decode, res)
|
|
if ok and data and data.token then
|
|
p:resolve({ token = data.token, user = data.user })
|
|
return
|
|
end
|
|
end
|
|
p:resolve(nil)
|
|
end)
|
|
return Citizen.Await(p)
|
|
end)
|
|
|
|
-- ── Dienststatus ──
|
|
local function setDuty(src, isOn)
|
|
local player = exports.qbx_core:GetPlayer(src)
|
|
if not player then return end
|
|
local jobName = player.PlayerData.job.name
|
|
local department = Config.JobDepartments[jobName]
|
|
if not department then return end
|
|
|
|
if isOn then
|
|
onDuty[src] = {
|
|
citizenid = player.PlayerData.citizenid,
|
|
name = ('%s %s'):format(player.PlayerData.charinfo.firstname, player.PlayerData.charinfo.lastname),
|
|
callsign = player.PlayerData.metadata and player.PlayerData.metadata.callsign or nil,
|
|
department = department,
|
|
}
|
|
end
|
|
|
|
local o = onDuty[src] or {
|
|
citizenid = player.PlayerData.citizenid,
|
|
name = ('%s %s'):format(player.PlayerData.charinfo.firstname, player.PlayerData.charinfo.lastname),
|
|
department = department,
|
|
}
|
|
post('/bridge/officer/duty', {
|
|
citizenid = o.citizenid,
|
|
name = o.name,
|
|
callsign = o.callsign,
|
|
department = o.department,
|
|
onDuty = isOn,
|
|
})
|
|
if not isOn then onDuty[src] = nil end
|
|
end
|
|
|
|
-- QBox-Dienstwechsel (best effort) + Fallback-Command
|
|
RegisterNetEvent('QBCore:Server:SetDuty', function(src, isOn)
|
|
setDuty(src, isOn == true)
|
|
end)
|
|
lib.addCommand('mdtduty', { help = 'MDT: Dienststatus umschalten' }, function(source)
|
|
setDuty(source, onDuty[source] == nil)
|
|
end)
|
|
|
|
AddEventHandler('playerDropped', function()
|
|
local src = source
|
|
if onDuty[src] then setDuty(src, false) end
|
|
end)
|
|
|
|
-- ── Server-seitiger Positions-Push (vertraut Client nicht) ──
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(Config.PositionInterval)
|
|
for src, info in pairs(onDuty) do
|
|
local ped = GetPlayerPed(src)
|
|
if ped and ped ~= 0 then
|
|
local c = GetEntityCoords(ped)
|
|
post('/bridge/officer/position', {
|
|
citizenid = info.citizenid,
|
|
coords = { x = c.x, y = c.y, z = c.z },
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- ── Positionsziel an einen Officer senden (von der Leitstelle via Web → Socket → hier) ──
|
|
RegisterNetEvent('d4rk_tablet:relayWaypoint', function() end) -- Platzhalter M8 (Socket→Bridge)
|
|
|
|
-- ── Dispatch-Export für andere Resourcen ──
|
|
-- exports.d4rk_tablet:createDispatch({ code='10-90', title='Banküberfall', department='police', priority='high', location='...', coords={x=,y=,z=} })
|
|
exports('createDispatch', function(data)
|
|
post('/bridge/dispatch', data)
|
|
end)
|