- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
196 lines
7.2 KiB
Lua
196 lines
7.2 KiB
Lua
-- =====================================================================
|
|
-- aether-police — Server-Schnittstelle
|
|
--
|
|
-- Ein einziges Netz-Event bedient alle Module. Jede Anfrage durchläuft
|
|
-- Dienstprüfung und Rechteprüfung, bevor sie ein Modul erreicht.
|
|
-- =====================================================================
|
|
|
|
local Core = exports['aether-core']
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Tablet öffnen
|
|
-- ---------------------------------------------------------------------
|
|
RegisterNetEvent('aether:police:oeffnen', function()
|
|
local quelle = source
|
|
|
|
local beamter = Dienst.Hole(quelle)
|
|
if not beamter then
|
|
-- Noch nicht im Dienst: Personalakte prüfen und Anmeldung anbieten
|
|
local charakterId = Core:HoleCharakterId(quelle)
|
|
if not charakterId then return end
|
|
|
|
local geprueft, fehler = Dienst.LadeBeamten(quelle, charakterId)
|
|
if not geprueft then
|
|
TriggerClientEvent('aether:police:fehler', quelle, fehler)
|
|
return
|
|
end
|
|
|
|
TriggerClientEvent('aether:police:anmeldung', quelle, {
|
|
name = geprueft.name,
|
|
dienstnummer = geprueft.dienstnummer,
|
|
rolleName = geprueft.rolleName,
|
|
abteilung = geprueft.abteilung,
|
|
behoerde = Polizei.Config.Behoerde,
|
|
})
|
|
return
|
|
end
|
|
|
|
TriggerClientEvent('aether:police:zeige', quelle, {
|
|
behoerde = Polizei.Config.Behoerde,
|
|
kuerzel = Polizei.Config.Kuerzel,
|
|
module = Kern.SichtbareModule(beamter),
|
|
beamter = {
|
|
name = beamter.name, dienstnummer = beamter.dienstnummer,
|
|
rolle = beamter.rolle, rolleName = beamter.rolleName,
|
|
abteilung = beamter.abteilung, status = beamter.status,
|
|
leitstelle = Dienst.Leitstelle() == quelle,
|
|
},
|
|
status = Polizei.Config.Dienststatus,
|
|
abteilungen = Polizei.Config.Abteilungen,
|
|
})
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Dienst
|
|
-- ---------------------------------------------------------------------
|
|
RegisterNetEvent('aether:police:dienstAntreten', function()
|
|
local quelle = source
|
|
local beamter, fehler = Dienst.Antreten(quelle)
|
|
|
|
if not beamter then
|
|
TriggerClientEvent('aether:police:fehler', quelle, fehler)
|
|
return
|
|
end
|
|
|
|
TriggerClientEvent('aether:police:zeige', quelle, {
|
|
behoerde = Polizei.Config.Behoerde,
|
|
kuerzel = Polizei.Config.Kuerzel,
|
|
module = Kern.SichtbareModule(beamter),
|
|
beamter = {
|
|
name = beamter.name, dienstnummer = beamter.dienstnummer,
|
|
rolle = beamter.rolle, rolleName = beamter.rolleName,
|
|
abteilung = beamter.abteilung, status = beamter.status,
|
|
leitstelle = Dienst.Leitstelle() == quelle,
|
|
},
|
|
status = Polizei.Config.Dienststatus,
|
|
abteilungen = Polizei.Config.Abteilungen,
|
|
})
|
|
end)
|
|
|
|
RegisterNetEvent('aether:police:dienstBeenden', function()
|
|
local quelle = source
|
|
Dienst.Beenden(quelle)
|
|
TriggerClientEvent('aether:police:schliessen', quelle)
|
|
end)
|
|
|
|
RegisterNetEvent('aether:police:status', function(status)
|
|
local quelle = source
|
|
if type(status) ~= 'string' then return end
|
|
Dienst.SetzeStatus(quelle, status)
|
|
end)
|
|
|
|
RegisterNetEvent('aether:police:leitstelle', function(zielQuelle)
|
|
local quelle = source
|
|
local erfolg, fehler = Dienst.SetzeLeitstelle(quelle,
|
|
type(zielQuelle) == 'number' and zielQuelle or nil)
|
|
if not erfolg and fehler then
|
|
TriggerClientEvent('aether:police:fehler', quelle, fehler)
|
|
end
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Zentraler Modul-Aufruf
|
|
--
|
|
-- Die Oberfläche schickt { modul, aktion, daten, ruf }. Der `ruf` ist
|
|
-- eine laufende Nummer, damit die Antwort zugeordnet werden kann.
|
|
-- ---------------------------------------------------------------------
|
|
RegisterNetEvent('aether:police:aufruf', function(modulId, aktion, daten, ruf)
|
|
local quelle = source
|
|
|
|
if type(modulId) ~= 'string' or type(aktion) ~= 'string' then return end
|
|
|
|
local beamter = Dienst.Hole(quelle)
|
|
if not beamter then
|
|
TriggerClientEvent('aether:police:antwort', quelle, ruf, nil,
|
|
'Du bist nicht im Dienst.')
|
|
return
|
|
end
|
|
|
|
local ergebnis, fehler = Kern.FuehreAus(quelle, beamter, modulId, aktion, daten)
|
|
TriggerClientEvent('aether:police:antwort', quelle, ruf, ergebnis, fehler)
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Eingehende Notrufe aus anderen Modulen
|
|
--
|
|
-- Sie erzeugen einen Einsatz mit Status „offen". Zugewiesen wird
|
|
-- ausschliesslich von der Leitstelle — nie automatisch.
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Notruf aus dem Handy (110)
|
|
AddEventHandler('aether:phone:notruf', function(quelle, fraktionId, nummer)
|
|
if fraktionId ~= 'lspd' and fraktionId ~= 'polizei' then return end
|
|
|
|
local position = GetEntityCoords(GetPlayerPed(quelle))
|
|
EinsatzEingang({
|
|
titel = 'Notruf über Telefon',
|
|
kategorie = 'notruf',
|
|
prioritaet = 2,
|
|
ort = 'Position des Anrufers',
|
|
beschreibung = ('Eingehender Anruf von %s.'):format(nummer or 'unbekannt'),
|
|
position = { x = position.x, y = position.y, z = position.z },
|
|
quelle = 'notruf',
|
|
})
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- EXPORTS — Schnittstelle für externe Systeme
|
|
-- Damit lassen sich Notrufe und Hinweise von aussen einspeisen, ohne
|
|
-- dass am Kern etwas geändert werden muss.
|
|
-- =====================================================================
|
|
|
|
--- Meldet einen Notruf an die Polizei.
|
|
--- Er erscheint als offener Einsatz — zugewiesen wird er nie automatisch.
|
|
exports('MeldeNotruf', function(daten)
|
|
if type(daten) ~= 'table' or not daten.titel then return nil end
|
|
return EinsatzEingang(daten)
|
|
end)
|
|
|
|
--- Erstellt einen Hinweis für alle Beamten im Dienst
|
|
exports('MeldeHinweis', function(daten)
|
|
if type(daten) ~= 'table' or not daten.titel then return false end
|
|
Hinweise.Erstelle(nil, daten)
|
|
return true
|
|
end)
|
|
|
|
--- Ist ein Spieler als Polizeibeamter im Dienst?
|
|
exports('IstImDienst', function(quelle)
|
|
return Dienst.Hole(quelle) ~= nil
|
|
end)
|
|
|
|
--- Liefert die Dienstdaten eines Beamten (ohne Rechtetabelle)
|
|
exports('HoleBeamten', function(quelle)
|
|
local beamter = Dienst.Hole(quelle)
|
|
if not beamter then return nil end
|
|
return {
|
|
charakterId = beamter.charakterId,
|
|
name = beamter.name,
|
|
dienstnummer = beamter.dienstnummer,
|
|
rolle = beamter.rolle,
|
|
rolleName = beamter.rolleName,
|
|
abteilung = beamter.abteilung,
|
|
status = beamter.status,
|
|
}
|
|
end)
|
|
|
|
--- Erlaubt fremden Ressourcen, ein eigenes Modul zu registrieren
|
|
exports('RegistriereModul', function(beschreibung)
|
|
return Kern.RegistriereModul(beschreibung)
|
|
end)
|
|
|
|
--- Erlaubt fremden Ressourcen, eine Aktion beizusteuern
|
|
exports('RegistriereAktion', function(modulId, aktion, recht, funktion)
|
|
Kern.RegistriereAktion(modulId, aktion, recht, funktion)
|
|
return true
|
|
end)
|