Zum Inhalt springen

Dispatch & Schnittstellen

Das System leitet Notrufe über modulare Dispatch-Adapter weiter. Jeder in HM_Notrufsaeulen angelegte Dienst kann einen eigenen Dispatch-Adapter nutzen (z. B. Polizei über Emergency Dispatch, Pannenhilfe über Roadphone).


Die Resource verfügt über eine vorkonfigurierte Integration für Emergency Dispatch von LoveRP Scripts.

Die API von Emergency Dispatch erwartet einzelne Parameter statt eines Payload-Tables:

TriggerEvent('emergencydispatch:emergencycall:new', jobName, message, coords, true)

Da das Event jeweils nur einen Ziel-Job aufnimmt, löst der Adapter das Event pro hinterlegtem Job des Notrufdienstes einmal aus. Besitzt ein Dienst z. B. die Jobs police und sheriff, erhalten beide Fraktionen den Einsatz.

In der config/config.lua unter Config.Dispatch.emergencydispatch.mode:

  • mode = 'client' (Standard): Löst das Event auf dem Client des Anrufers aus. Emergency Dispatch entscheidet selbstständig zwischen Telefonanruf (falls Leitstelle besetzt ist) und Text-Notruf (falls unbesetzt), und ermittelt die Spielerposition automatisch:
    TriggerEvent('emergencydispatch:emergencycall:new', jobName, message, false)
  • mode = 'server': Nutzt die serverseitige Auslösung mit exakter Säulen-Koordinate – erzeugt immer eine reine Text-Einsatzmeldung ohne Telefonie.

Optional kann zusätzlich ein Alarm für eine Feuerwehr- oder Rettungswache ausgelöst werden (emergencydispatch:alarmierung:wache). Der im Dienst hinterlegte Dispatch-Code fungiert hierbei als alarmcode:

Config.Dispatch.emergencydispatch = {
alertStation = false, -- Auf true setzen
station = 'Wache 1', -- Name der Wache
stationEvent = 'emergencydispatch:alarmierung:wache',
...
}

Da die API von Emergency Dispatch kein separates Feld für den Meldernamen anbietet, setzt das Script den Spielernamen automatisch an den Anfang der Einsatzmeldung:

Melder: Frank Wolf | Notrufsäule: A9 Süd km 42 | Ort: Elgin Ave | gewählt: 112 | Meldung: Fahrzeugbrand

Vor dem Absenden fragt der Adapter die Personalstärke bei Emergency Dispatch ab:

exports['emergencydispatch']:mannedvehicles() -- Einsatzkräfte auf Fahrzeugen
exports['emergencydispatch']:mannedcontrol() -- Disponenten in der Leitstelle
  • Ist im jeweiligen Job niemand im Dienst, wird der Notruf trotzdem an das Dispatch-System übergeben (damit der Einsatz nicht verloren geht).
  • Gleichzeitig greift die Redundanz (Config.Dispatch.fallback), und der Anrufer erhält an der Säule den Hinweis: „Meldung weitergeleitet (Bereitschaft übernimmt)“.
  • Über notruf_duty in der Serverkonsole kannst du prüfen, welche Jobnamen Emergency Dispatch aktuell meldet.

Emergency Dispatch besitzt keine eigene Telefonie – für echte Sprachverbindungen kann ein Telefonskript über den Client-Hook Config.Phone.start(data) angebunden werden:

Config.Phone = {
enabled = true,
resource = 'lb-phone',
start = function(data)
exports['lb-phone']:StartCall(data.number, data.anonymous)
end,
}

Das übergebene Objekt data enthält:

  • data.number: Rufnummer des gewählten Dienstes.
  • data.service: Vollständiges Dienst-Objekt.
  • data.serviceId: ID des Dienstes.
  • data.anonymous: Boolean, ob anonym gemeldet wurde.
  • data.pillar: Säulen-Objekt mit Standortdaten.
exports['lb-phone']:StartCall(data.number, data.anonymous)

Möchtest du ein anderes Notruf- oder Dispatch-System (z. B. Roadphone, Quasar Dispatch, Codesign, etc.) anbinden, lässt sich dies in wenigen Schritten modular umsetzen.

  1. Adapter-Datei anlegen:
    Erstelle eine neue Lua-Datei im Ordner server/adapters/ (z. B. server/adapters/roadphone.lua):

    --[[ Adapter: Roadphone ]]
    NRS.Dispatch.Register('roadphone', function(call)
    local cfg = Config.Dispatch.roadphone
    -- Prüfen, ob die Ziel-Resource läuft
    if GetResourceState(cfg.resource) ~= 'started' then
    return false, ('Resource "%s" läuft nicht'):format(cfg.resource)
    end
    local message = ('%s an %s: %s'):format(
    call.service.label, call.street or call.pillar.label, call.message)
    exports[cfg.resource]:CreateCall({
    job = call.service.jobs[1],
    code = call.service.dispatchCode,
    text = message,
    coords = call.coords,
    caller = call.callerName,
    })
    return true
    end)
  2. In der fxmanifest.lua registrieren:
    Füge die Datei in der fxmanifest.lua unter server_scripts ein (nach server/dispatch.lua und vor server/main.lua):

    server_scripts {
    'server/bridge.lua',
    'server/storage.lua',
    'server/permissions.lua',
    'server/dispatch.lua',
    'server/adapters/emergencydispatch.lua',
    'server/adapters/roadphone.lua', -- Neu hinzugefügt
    'server/main.lua',
    }
  3. Im Tablet auswählbar machen:
    Trage den Adapter in config/config.lua unter Config.Dispatch.adapters ein:

    Config.Dispatch.adapters = {
    { value = 'emergencydispatch', label = 'Emergency Dispatch' },
    { value = 'roadphone', label = 'Roadphone' },
    }
  4. Eigenen Config-Block hinzufügen:
    Füge der config/config.lua die nötigen Parameter für deinen Adapter hinzu:

    Config.Dispatch.roadphone = {
    resource = 'roadphone',
    priority = 2,
    }
  5. Dienst zuweisen:
    Öffne im Tablet den Reiter Notrufdienste, wähle den gewünschten Dienst aus und stelle im Dropdown-Menü Dispatch-Adapter auf deinen neuen Eintrag um. Die Änderung ist sofort aktiv.

Jeder Adapter empfängt beim Auslösen ein umfassendes call-Table:

FeldTypBeschreibung
call.sourcenumberServer-ID des Anrufers (0, wenn ohne Spieler ausgelöst).
call.callerNamestringRP-Name des Spielers (oder der Platzhalter bei anonymer Meldung).
call.anonymousbooleantrue, wenn die Meldung als anonym markiert wurde.
call.pillartableSäulen-Daten (id, label, road, km, code, coords, etc.).
call.servicetableDienst-Daten (id, label, number, jobs, dispatchCode, priority, etc.).
call.coordsvector3Exakte 3D-Koordinaten der Notrufsäule.
call.street / zonestringStraßen- und Gebietsname der Säule.
call.messagestringEingegebener Freitext des Anrufers.
call.timenumberZeitstempel (os.time()).

Dein Adapter muss dem System das Ergebnis der Weiterleitung mitteilen:

  • return true: Notruf erfolgreich abgesetzt.
  • return false, 'Fehlermeldung': Fehler aufgetreten – Grund wird im Server-Log vermerkt und das System greift auf die Redundanz zurück.
  • return true, nil, { unstaffed = true }: Einsatz erfolgreich im System erstellt, jedoch ist aktuell kein Personal im Dienst. Das Notrufsäulen-Bedienfeld meldet dem Anrufer, dass die Meldung weitergeleitet wurde und die Bereitschaft übernimmt; gleichzeitig wird die Redundanz ausgeführt.

Möchtest du externe Scripte über eingehende Notrufe informieren, ohne einen Adapter zu schreiben, kannst du auf diese Events lauschen:

-- Wird nach jedem erfolgreich abgesetzten Notruf getriggert:
AddEventHandler('hm_Notrufsaeulen:callDispatched', function(call, adapterName, success)
print(('Notruf an %s über Adapter %s (Erfolg: %s)'):format(call.service.label, adapterName, tostring(success)))
end)
-- Wird beim Adapter 'custom' aufgerufen:
AddEventHandler('hm_Notrufsaeulen:onDispatch', function(call)
-- Enthält alle Daten: call.coords, call.message, call.service, etc.
end)

-- Serverseitig: Tablet für einen bestimmten Spieler öffnen
exports.hm_Notrufsaeulen:openTablet(source)
-- Clientseitig: Notrufmenü einer bestimmten Säule öffnen
exports.hm_Notrufsaeulen:openPillarMenu(pillarId)
BefehlAusführbar durchFunktion
/notruf_configEditor, AdminÖffnet das Config-Tablet im Spiel.
notruf_grant <ID|identifier> [level]Admin, ServerkonsoleWeist admin- oder editor-Rechte zu.
notruf_revoke <ID|identifier>Admin, ServerkonsoleEntzieht vergebene Berechtigungen.
notruf_permsAdmin, ServerkonsoleListet alle Berechtigungs-Einträge auf.
/notruf_idJeder SpielerZeigt die eigenen Identifier zum Kopieren an.
notruf_mapAdmin, ServerkonsoleDruckt aktuelle Kartengrenzen für die config.lua aus.
notruf_dutyAdmin, ServerkonsolePrüft den Personalstatus bei Emergency Dispatch und gleicht Jobs ab.
notruf_reloadAdmin, ServerkonsoleLiest alle JSON-Dateien in data/ neu ein.
notruf_seedAdmin, ServerkonsoleLegt Standarddienste aus config/services.lua erneut an.
/notruf_model [name]Jeder (Client)Prüft, ob das angegebene Model im Client gültig und geladen ist.