EmergencyOS — Dokumentation
Willkommen in der offiziellen Entwickler- und Betreiber-Dokumentation des EmergencyOS-Ökosystems. Hier findest du zu jedem FiveM-Script die vollständige Anleitung: Installation, Voraussetzungen, Datenbank- und Panel-Sync, jede Config-Option und jeden Export mit Code-Beispiel.
Das Ökosystem
EmergencyOS besteht aus zwei Hälften, die über eine sichere HTTP-Bridge zusammenspielen:
- Web-Panel (
emergencyos.de) — die Fach-Anwendung (Akten, Fahndungen, Tickets, LiveMap, Statistiken). Läuft im Browser und als Tablet-Oberfläche im Spiel.
- FiveM-Ressourcen — die Ingame-Scripts auf deinem GTA-V-Server, die sich mit dem Panel synchronisieren.
Produkte im Überblick
| Produkt |
Zweck |
Exports |
| EmergencyOS |
Hauptsystem: Tablet/MDT für Polizei, Medic, Justiz & Feuerwehr |
19 |
| DispatchOS |
Leitstelle / Dispatch / MDT mit umfangreicher Export-API |
23 |
| LicenseOS |
Führerschein- & Fahrschulsystem |
4 |
| Ancomox Garage |
Multi-Framework-Garagensystem |
2 |
| Vehicle Shop |
Fahrzeug-Shop / Autohaus |
Events |
Wähle links in der Sidebar ein Produkt — jede Seite ist gleich aufgebaut: Überblick → Voraussetzungen → Installation → Datenbank & Sync → Konfiguration → Exports → Events & Commands → Troubleshooting.
Allgemeine Voraussetzungen
- Aktueller FiveM-Server (Artifacts)
- oxmysql als Datenbank-Layer
- Framework: ESX, QBCore / Qbox oder Standalone (je nach Script frei wählbar)
- Ein EmergencyOS-Panel-Konto mit API-Key (für die panel-gebundenen Scripts EmergencyOS & LicenseOS)
So funktioniert die Panel-Anbindung
Jedes panel-gebundene Script verbindet sich beim Start automatisch mit dem Web-Panel — Server-IP und Port müssen nirgendwo eingetragen werden:
- Im Panel unter Einstellungen → Datenbank Sync (bzw. FiveM-Bridge) einen API-Key erzeugen.
- Den Key in die jeweilige
config.lua / api_config.lua eintragen (SVConfig.API.key bzw. Config.ApiKey).
- Ressource neu starten — der Server registriert sich selbst am Panel (IP/Port automatisch). Fertig.
Tipp: Die genauen Schritte, Endpunkte und Fehlerbilder stehen bei jedem Produkt in den Abschnitten „Installation" und „Datenbank & Panel-Sync".
Konventionen in dieser Doku
Code in dieser Schrift = exakte Datei-, Config- oder Exportnamen aus dem Quellcode.
- Tabellen listen Key · Standard · Beschreibung — die Standardwerte sind die echten Defaults aus dem ausgelieferten Code.
Support
Bei Fragen: Discord (discord.gg/2pKru3N) oder per E-Mail an info@ancomox.com.
EmergencyOS — Entwickler-Dokumentation
Überblick
EmergencyOS ist die Ingame-Extension (Tablet/MDT) zum EmergencyOS-Web-Panel (emergencyos.de). Das Tablet ist die Bedienoberfläche im Spiel; die eigentliche Fach-App (Akten, Fahndungen, Tickets, LiveMap, Statistiken) läuft als Web-Panel, mit dem sich der Spielserver über eine HTTP-Bridge synchronisiert.
Zielgruppe / Fraktionen: Polizei/Behörden (LSPD, FIB, LSSD/Sheriff), Justiz (DOJ), Rettungsdienst (LSMD/Medic) und Feuerwehr (LSFD). Die Abteilungs-Sichtbarkeit ist strikt getrennt. Standard-Whitelist im Code: police, ambulance, fib, justiz, admin.
Kernfeatures (aus CHANGELOG/README):
- MDT/Tablet mit Multi-Window, freiem Resizing, Dark-Design, Rich-Text-Editor (Quill, Auto-Save), Aktenvorlagen.
- Personen- & Fahrzeugakten, Fahndungssystem (Wanted/BOLO) — intern & öffentlich; öffentliche Fahndungen laufen als DUI-Carousel auf Welt-Bildschirmen (Config.WantedBillboards).
- Digital Citation / Ticketsystem — inszeniertes Strafzettel-Ausstellen mit Beweisfoto (Evidence Cam → Discord), animiertem Beleg, Bürgerportal /strafzettel zum Einsehen & Bezahlen.
- Bußgeldrechner mit dynamischen Modifikatoren (Geständig −20 %, Wiederholungstäter +50 %).
- Sofort-Halterabfrage (F9 / /runplate) mit HUD und BOLO-Treffer.
- App „Dienst & Statistik" — automatische Dienstzeit-Erfassung nach Job, Live-Dienststand, Leaderboard, Team-Übersicht, Dienstpflicht/Wochen-Soll, AFK-Schutz, optionaler Discord-Wochenbericht.
- Verhaftungs-Anbindung für eigene Jail-Scripts (RecordArrest), Dispatch-Anbindung (Vanguard Dispatch Gateway).
- Panic Button (10-13), interner Monitor (Atlas-Feed auf Wachen-Screens), KI-gestützte Tatberichte, Beweismittel-Verwaltung, FIB „Nexus Netzwerk".
- Framework-agnostisch: ESX, QBCore oder STANDALONE (Custom) über die Data-Provider-API.
Voraussetzungen
Aus fxmanifest.lua (fx_version 'bodacious', game 'gta5', lua54 'yes'):
| Voraussetzung |
Pflicht |
Hinweis |
| oxmysql |
Ja (harte Abhängigkeit) |
Muss vor EmergencyOS starten. |
| EmergencyOS-Panel-Konto |
Ja |
Instanz + API-Key auf emergencyos.de (ab Free-Version kostenlos). |
| Framework |
Optional |
es_extended (ESX) oder qb-core (QBCore) für Auto-Integration; sonst STANDALONE + Data Provider. |
ox_lib |
Optional |
Standard-Benachrichtigungen (ox_lib:notify). |
screenshot-basic |
Optional |
Nötig für Panel-Screenshots + Evidence Cam der Ticket-Zeremonie. |
ox_target |
Optional |
Nur wenn Config.TargetSystem = "ox_target". |
vanguard_dispatch |
Optional |
Dispatch-Gateway + Panic-Harmonie. |
| Billing/Notify (kontextabhängig) |
Optional |
esx_billing, okokBilling, qb-phone, okokNotify, esx_addonaccount (Society-Payout), LB-Phone, okokBanking, LicenseOS. |
Es gibt keine expliziten dependency-Zeilen im Manifest außer @oxmysql.
Installation
- Ordnername: Die Resource muss exakt
EmergencyOS heißen (Groß-/Kleinschreibung beachten — der Code nutzt durchgängig exports['EmergencyOS'] und GetCurrentResourceName()).
- Panel-Konto/Instanz: Auf
emergencyos.de anmelden, Instanz anlegen. Im Panel unter Einstellungen → Datenbank Sync einen API-Key erzeugen (nur der Besitzer-Account sieht diesen Button). Panel-DB-Migrationen/-Dateien laut Keymaster/Panel einspielen.
- API-Key eintragen: In
config/api_config.lua den Key in SVConfig.API.key setzen (siehe nächster Abschnitt).
- Framework wählen: In
config/config.lua Config.Framework auf "ESX", "QBCORE" oder "STANDALONE" setzen; bei STANDALONE zusätzlich Data Provider registrieren (siehe README_CUSTOM_FRAMEWORK).
- server.cfg — Startreihenfolge (oxmysql zuerst, Framework davor):
cfg
ensure oxmysql
ensure es_extended # oder qb-core (bei STANDALONE entfällt das)
ensure EmergencyOS
- SQL-Migrationen einmalig ausführen (Dienstzeit-Spalten, Dienstpflicht, Ticket-Zuordnung, optional Patrol-Index; Duty-Tabelle
emos_duty_stats).
- Server starten. Beim Start meldet sich der Server automatisch am Panel an (Handshake). Erfolg:
[EmergencyOS] v… bereit | Panel verbunden (Instanz <id>) — Sync aktiv.
Datenbank & Panel-Sync
Die Synchronisation läuft über eine bidirektionale HTTP-Bridge (server/api_endpoint.lua, server/sync_bridge.lua). Der Spielserver ruft das Panel unter https://emergencyos.de/api/… auf; das Panel ruft umgekehrt den FiveM-HTTP-Endpoint des Servers auf (SetHttpHandler, Autorisierung per Api-Key).
Einrichtung (neuer Weg, ab v3-Handshake)
- Key erstellen: Panel → Einstellungen → Datenbank Sync → API-Key (nur Besitzer-Account). Neue Keys tragen das Präfix
eos_.
- Key eintragen in
config/api_config.lua → SVConfig.API.key.
- Resource neu starten — fertig. Server-IP/Port müssen nirgendwo eingetragen werden: der Server registriert sich beim Start selbst am Panel und schickt beim Handshake
Resource-Name und Server-Port (GetConvar('netPort', '30120')) mit; das Panel registriert IP/Port/Resource-Name automatisch als Push-Rückkanal.
Handshake-Logik (api_endpoint.lua)
- Self-Service-Key (
eos_…): Anmeldung nur mit dem Key. Header: Api-Key, Resource-Name, Server-Port, optional Server-Ip (nur wenn SVConfig.API.serverIp gesetzt). Ziel: GET https://emergencyos.de/api/getInstance → liefert die numerische Instance-ID (GlobalInstanceId).
- Legacy-Key (ohne
eos_, Ancomox-Bestandskunden): zusätzlich Instance-Username + Instance-Password (aus SVConfig.API.username/password) erforderlich. Fehlen sie, bricht der Handshake mit klarer Konsolenmeldung ab (kein weiterer Versuch bis Neustart).
- Retry/Backoff: Bei nicht erreichbarem Panel automatische Wiederholung
10s → 30s → 60s → 2min → 5min. HTTP 400 (unvollständige Daten) stoppt dauerhaft mit Anleitung; 401/403 (Key falsch/abgelaufen) zeigt einmal einen Hinweis und versucht danach leise alle 10 min erneut.
- Re-Handshake: Nach Erst-Erfolg meldet sich der Server stündlich erneut an (registriert IP/Port bei Server-Umzug automatisch neu).
- Nach Verbindung wird nach 5 s die animierte Fahndungs-Rotation gestartet (
api/wantedCarousel.php?instance=<id>).
Sicherheit des eingehenden Endpoints
- Nur
POST mit gültigem Key (Header Authorization: Bearer <key> oder Api-Key), Vergleich in konstanter Zeit (SecureCompare).
- Rate-Limit
SVConfig.ApiRateLimit (Default 120/10 s) → HTTP 429; Body-Limit SVConfig.ApiMaxBodyBytes (Default 524288 = 512 KB) → HTTP 413; Watchdog-Timeout SVConfig.ApiTimeoutMs (Default 15000 ms) → {"error":"timeout"}.
- Jede Action läuft in eigenem Citizen-Thread; Actions können
execute(data)->string (sync) oder executeAsync(data, done) definieren.
Genutzte Panel-Endpoints (ausgehend)
api/getInstance (Handshake), api/getWanteds.php (BOLO-Pull), api/mdtLookup.php (MDT), api/wantedCarousel.php, api/billPaid (via sendAPIReqest), api/getOpenTickets.php, api/setTicketPaid.php (Bürgerportal), version.json (Update-Check).
Eingehende Actions (Panel → Server)
U. a. syncWanteds (BOLO-Push), getDispatchState, dispatchCreateCall, dispatchAssign, dispatchClose, createBill, liveConsoleLog sowie die Data-Provider-Actions (getVehicle, searchPlayers, getPlayer, searchVehicles, trackPhone, getJobPlayerPositions).
BOLO/Fahndungs-Cache (sync_bridge.lua)
RAM-Cache mit 3 Quellen (Priorität absteigend): 1) Push (Actions['syncWanteds']), 2) Pull (api/getWanteds.php, Intervall SVConfig.BoloPullInterval, Default 300000 ms), 3) DEV-Fallback (direkte DB app_wanteds/app_vehicleRegister, nur wenn SVConfig.BoloDevFallback == true). MDT-Lookups werden SVConfig.MdtCacheTTL ms (Default 90000) gecacht, mit Live-Fallback auf die Data-Provider-Actions.
Konfiguration
config/config.lua (Client & Shared)
| Key |
Standard |
Beschreibung |
Config.Framework |
"ESX" |
Framework: "ESX", "QBCORE" oder "STANDALONE" (Custom, via Data Provider). |
Config.TargetSystem |
"none" |
Target-System für Interaktionen; unterstützt "ox_target", sonst eigene Marker-/Tasten-Logik. |
Config.Locale |
"en" |
Sprache der Tablet-Meldungen: "en" oder "de" (siehe locales.lua). |
Config.ScreenshotWebhook |
"https://discord.com/api/webhooks/" |
Discord-Webhook für Panel-Screenshots + Evidence Cam. Leer/Platzhalter = deaktiviert. |
Config.Prop |
"prop_cs_tablet" |
Handobjekt beim geöffneten Tablet. |
Config.Animation.dict |
"amb@world_human_tourist_map@male@base" |
Anim-Dictionary der Haltung (auch für Ticket-Zeremonie). |
Config.Animation.anim |
"base" |
Anim-Name. |
Config.EnablePanicButton |
true |
Panic Button (10-13) aktivieren. |
Config.PanicButtonCommand |
"panicbutton" |
Command-Name (/panicbutton). |
Config.PanicButtonKey |
"P" |
Standard-Taste (GTA-Settings, frei belegbar). |
Config.ServerPraefix |
"TestServerInstance346" |
Präfix für lokal gespeicherte Zugangsdaten (KVP). Pro Instanz eindeutig wählen! |
Config.AutoFill.AutoFillPW |
true |
Gespeicherte Zugangsdaten ins Login eintragen. |
Config.AutoFill.AutoLogin |
true |
Zusätzlich automatisch einloggen. |
Config.AutoFill.AutoFillLogout |
true |
Daten auch nach Logout wieder eintragen. |
Config.EnableParallax |
true |
Parallax-/Tiefeneffekt der Oberfläche (kosmetisch). |
ShouldPadBeLocked() |
return false |
Funktion: eigene Zusatzbedingung; true = Tablet gesperrt. |
Config.LockPadWhileDead |
true |
Tablet sperren, solange der Spieler tot ist. |
Config.PlayerDeadEvent |
"esx:onPlayerDeath" |
Framework-Event Tod (bei Custom anpassen). |
Config.PlayerReviveEvent |
"esx_ambulancejob:revive" |
Framework-Event Wiederbelebung. |
Config.PlayerSpawnedEvent |
"playerSpawned" |
Framework-Event Spawn. |
Config.EnableJobLock |
true |
true = nur Whitelist-Jobs dürfen das Tablet öffnen. |
Config.WhitelistedJobs |
{"police","ambulance","fib","justiz","admin"} |
Jobs mit Tablet-Zugriff; steuert auch F9-Halterabfrage, Atlas-LiveLog-Empfang, Beamtenerkennung, serverseitige Event-Prüfung. |
Config.UseKeyMapping |
true |
Tablet per Taste/Command öffnen (RegisterKeyMapping). |
Config.DefaultKeybind |
"MULTIPLY" |
Standard-Taste (GTA-Bezeichner, hier Numpad *). |
Config.KeyBindText |
"Open EmergencyOS Tablet" |
Beschriftung in den GTA-Tastatureinstellungen. |
Config.NeedItem |
false |
true = Item im Inventar nötig. |
Config.PadItems |
{"tablet","othertablet"} |
Item-Namen, die als Tablet gelten. |
Config.UsePadOnFoot |
true |
Tablet zu Fuß nutzbar. |
Config.OnFootFullscreen |
false |
Zu Fuß im Vollbild öffnen. |
Config.UsePadInCar |
true |
Tablet im Fahrzeug nutzbar. |
Config.PadInCarFullscreen |
true |
Im Fahrzeug im Vollbild öffnen. |
Config.OpenKey |
"E" |
Interaktionstaste an Positionen/Props (Anzeige-Text). |
Config.PlayerJobChange |
"esx:setJob" |
Framework-Event bei Jobwechsel (aktualisiert Whitelist live). |
Config.EnablePositions |
true |
Feste Terminal-Positionen aktivieren. |
Config.OpenDistance |
2.0 |
Interaktionsdistanz zur Position (m). |
Config.MarkerDrawDistance |
5.0 |
Ab dieser Distanz wird der Marker gezeichnet (m). |
Config.Positions |
1 Beispiel (position01, Mission Row) |
Terminal-Liste. Pro Eintrag: position (vector3), jobs, helpnotify, fullscreen, marker{enabled,type,moveUpDown,rotate,color{r,g,b,t},size{x,y,z}}. |
Config.EnableProps |
true |
Welt-Props als Terminals aktivieren. |
Config.PropOpenDistance |
2.0 |
Interaktionsdistanz zum Prop (m). |
Config.Props |
prop_park_ticket_01, prop_monitor_01a, prop_monitor_01b (alle jobs={"police"}) |
Prop-Modelle als Terminal (jobs/helpnotify/fullscreen). |
Config.WantedBillboardProximity |
true |
Öffentliche Fahndungen nur in der Nähe rendern. |
Config.WantedBillboardDistance |
30.0 |
Render-Distanz der Fahndungs-Billboards (m). |
Config.WantedBillboards |
4 Einträge (prop_tv_flat_01/03, prop_trev_tv_01, vw_prop_vw_cinema_tv_01, je tex="script_rt_tvscreen") |
Welt-Screens für die öffentliche Fahndungs-Rotation (DUI). Pro Eintrag dict, tex. |
Config.InternalMonitorJobs |
{"police","fib"} |
Jobs, deren Umgebung interne Monitore aktiviert. |
Config.InternalBillboards |
1 Eintrag (prop_tv_flat_michael, tex="script_rt_tvscreen") |
Screen-Modelle für den internen Atlas-Feed. |
Config.InternalMonitorProximity |
(nicht in Default-Config) |
Wird in cl_internal_monitor.lua gelesen. USE_PROXIMITY ist nur true, wenn der Wert exakt false ist — sonst wird immer gerendert. |
Config.InternalMonitorDistance |
25.0 (Fallback) |
Render-Distanz interner Monitore (m). |
EMOSClientNotify(title,text) |
leer |
Funktion: eigenes Notify-System einhängen (leer = Standard/ox_lib). |
Config.DebugLiveLog |
false |
Jeden Atlas-LiveLog zusätzlich in F8 ausgeben. |
Config.TicketFx.enabled |
true |
Ingame-Inszenierung beim Ticket-Ausstellen. |
Config.TicketFx.handoverDistance |
8.0 |
Max. Distanz Beamter↔Ziel (m) für Vor-Ort-Modus; weiter = Zustellung „per Post". |
Config.TicketFx.evidenceCam |
true |
Beweisfoto im Blitz-Moment in ScreenshotWebhook posten (braucht screenshot-basic). |
Config.PlateCheck.enabled |
true |
Sofort-Halterabfrage aktivieren (Client + Server). |
Config.PlateCheck.defaultKey |
'F9' |
Standard-Taste (frei belegbar; alternativ /runplate). |
Config.CitizenTickets.enabled |
true |
Bürgerportal (offene Strafzettel) aktivieren; deaktiviert auch die Anlaufstellen. |
Config.CitizenTickets.command |
'strafzettel' |
Chat-Command, der das Portal öffnet. |
Config.CitizenTickets.defaultKey |
'' |
Optionaler zusätzlicher Keybind ('' = nur Command). |
Config.CitizenTickets.payFromBank |
true |
true = erst Bank, dann Bargeld; false = nur Bargeld. |
Config.CitizenTickets.payoutSociety |
'society_police' |
ESX-Society (esx_addonaccount), in die Bußgelder fließen; leer = kein Auto-Payout. |
Config.CitizenTickets.Payout |
(auskommentiert) |
Funktion function(amount, ticket) — überschreibt payoutSociety komplett. |
Config.CitizenTickets.CloseFrameworkBill |
(auskommentiert) |
Funktion function(billId) — zugehörige Framework-Rechnung schließen. |
Config.CitizenTickets.interactionKey |
38 |
GTA-Control-ID der Interaktionstaste (38 = E). |
Config.CitizenTickets.interactionDistance |
2.2 |
Interaktionsdistanz an Anlaufstellen (m). |
Config.CitizenTickets.Locations |
1 Beispiel (Mission Row Empfang) |
Bürgerbüro-Punkte. Pro Eintrag: coords (vector4), ped (Model oder false), scenario, label, blip{enabled,sprite,color,scale,label}. NPCs spawnen ab 45 m, despawnen ab 65 m. |
Config.DutyStats.enabled |
true |
App „Dienst & Statistik" aktivieren. |
Config.DutyStats.onDutyJobs |
{'police','fib','ambulance'} |
Jobs, die als „im Dienst" zählen (getrennt von WhitelistedJobs; Off-Duty-Jobs weglassen). |
Config.DutyStats.jobCheckInterval |
15 |
Sek.: wie oft der Server den Job prüft (min. 5). |
Config.DutyStats.flushInterval |
120 |
Sek. bis zur Persistierung in die Spiel-DB (min. 30). |
Config.DutyStats.trackActivity |
true |
Festnahmen/Panic/Scans/Halterabfragen mitzählen. |
Config.DutyStats.leaderboardLimit |
25 |
Leaderboard-Länge (min. 5). |
Config.DutyStats.notifyOnChange |
true |
Kurze Info + Atlas-Log bei Dienstwechsel. |
Config.DutyStats.hud |
false |
Dezentes Live-HUD (IM DIENST + Schichtzeit). |
Config.DutyStats.hudPosition |
{x=0.015, y=0.62} |
HUD-Position. |
Weitere Config.DutyStats-Optionen (im Code unterstützt, in der Default-Config nicht ausgeschrieben — bei Bedarf ergänzen):
| Key |
Standard |
Beschreibung |
Config.DutyStats.afkPause |
true |
AFK-Schutz aktiv (keine Dienstzeit bei Untätigkeit). |
Config.DutyStats.afkSeconds |
480 (min. 60) |
AFK-Schwelle in Sekunden (8 Min). |
Config.DutyStats.offDutySuffix |
"_offduty" |
Suffix, das Off-Duty-Jobs erkennt. |
Config.DutyStats.jobFaction |
{} |
Mapping Jobname → Fraktion, falls abweichend (z. B. { ambulance='medic', bcso='sheriff' }). |
Config.DutyStats.GetCharName |
(nicht gesetzt) |
Funktion function(src) — eigener RP-Name-Hook. |
Config.DutyStats.livePushInterval |
60 (min. 30) |
Sek. zwischen Live-Dienststand-Pushes. |
Config.DutyStats.statsPushInterval |
300 (min. 60) |
Sek. zwischen Statistik-Pushes. |
Config.DutyStats.statsMaxRows |
1000 (min. 50) |
Zeilen-Obergrenze pro Statistik-Push. |
config/api_config.lua (Server)
| Key |
Standard |
Beschreibung |
SVConfig.UseAPI |
true |
Master-Schalter für die gesamte Panel-Bridge (aus = API/Sync/Dispatch/BOLO inaktiv). |
SVConfig.DebugLog |
false |
true = ausführliche Diagnose (EOSLog); false = nur Statuszeile + Warnungen/Fehler. |
SVConfig.API.key |
"eos_1f9b0482bb23f6459bbec0bfe2452643ec4bc4e0" (Beispiel) |
API-Key aus dem Panel (Einstellungen → Datenbank Sync). eos_… = Self-Service. |
SVConfig.API.serverIp |
"" |
Optionales IP-Override für Multi-IP-Server (leer = Panel erkennt automatisch). |
SVConfig.API.username |
"" |
Nur Legacy (Ancomox-Key ohne eos_): Besitzer-Username. |
SVConfig.API.password |
"" |
Nur Legacy: Besitzer-Passwort. |
SVConfig.BoloPullInterval |
300000 |
BOLO-Pull-Fallback (ms); 0 = aus. „recommended not to change". |
SVConfig.MdtCacheTTL |
90000 |
MDT-Lookup-RAM-Cache (ms). |
SVConfig.LicenseSource |
"auto" |
Führerschein-Quelle: "auto", "licenseos", "framework", "off". Immer live aus der Spielserver-DB. |
SVConfig.UseBilling |
true |
Rechnungserstellung (createTicketBill) aktivieren. |
SVConfig.BillingTrigger |
"EmergencyOS:billPayed" |
Event-Name, das bezahlte Rechnungen ans Panel meldet (ruft billPaid). |
SVConfig.ApiRateLimit |
120 |
(nicht in Default-Config; Fallback in api_endpoint.lua) Requests/10 s. |
SVConfig.ApiMaxBodyBytes |
524288 |
(Fallback) Max. Request-Body (512 KB). |
SVConfig.ApiTimeoutMs |
15000 |
(Fallback) Watchdog-Timeout pro Action (ms). |
SVConfig.BoloDevFallback |
(nicht gesetzt → aus) |
true = direkter DB-Zugriff auf app_wanteds/app_vehicleRegister als BOLO-Fallback (nur Dev/geteilte DB). |
createTicketBill(targetIdentifier, billSender, billFraction, billText, billAmount) |
eingebaut |
Funktion: erzeugt Rechnung; unterstützt esx_billing, okokBilling, qb-phone; für Custom Banking anpassen. Gibt billId zurück. |
EOSLog(msg) |
eingebaut |
Log-Helfer (nur bei DebugLog). |
config/cl_integrations.lua (Client, Framework-Bridge-Stubs)
Diese Datei enthält Stub-Funktionen, die bei Custom-Frameworks überschrieben werden können. Standardwerte:
| Funktion |
Standard-Verhalten |
Beschreibung |
FrameworkGetPlayerJob() |
return playerJobName |
Liefert den aktuellen Client-Jobnamen. |
FrameworkDoesPlayerHaveItem(item) |
return true |
Item-Besitzprüfung (Standard: immer true). |
EMOSHelpNotify() |
leer |
Hook für eigene Help-Notify-Anzeige. |
HelpNotifyClose() |
leer |
Hook zum Schließen der Help-Notify. |
Exports
exports['EmergencyOS']:<Name>(...). Insgesamt 19 registrierte Exports (5 Client, 14 Server).
Client-Exports (client/client.lua)
LockEmergencyOS() — Client. Schließt das Tablet und sperrt das Öffnen (IsPadLocked = true). Keine Rückgabe.
exports['EmergencyOS']:LockEmergencyOS()
UnlockEmergencyOS() — Client. Hebt die Sperre auf. Keine Rückgabe.
exports['EmergencyOS']:UnlockEmergencyOS()
CloseEmergencyOS() — Client. Schließt das Tablet (ohne Sperre).
exports['EmergencyOS']:CloseEmergencyOS()
OpenTablet([fullscreen]) — Client. Öffnet das Tablet; fullscreen (bool) erzwingt Vollbild.
exports['EmergencyOS']:OpenTablet(true)
SetPlayerJob(jobName) — Client. Setzt den lokal bekannten Job (Bridge.PlayerJob) für Whitelist/UI. Bei STANDALONE/Custom bei Charwahl, Spawn und Jobwechsel aufrufen.
exports['EmergencyOS']:SetPlayerJob("police")
Server-Exports
SetPlayerJob(source, jobName) — Server (server.lua). Setzt global den Job eines Online-Spielers (Bridge.PlayerJobs[src]) für Whitelist-Prüfung, LiveMap-Gruppen und Panic-Broadcast.
exports['EmergencyOS']:SetPlayerJob(source, "police")
GetInstanceId() — Server (api_endpoint.lua). Gibt die vom Panel zugewiesene Instance-ID zurück (oder nil, solange nicht verbunden).
local iid = exports['EmergencyOS']:GetInstanceId()
billPaid(billId) — Server (server_actions.lua). Meldet dem Panel, dass eine Rechnung bezahlt wurde (ruft api/billPaid). Aus eigenem Banking aufrufen.
exports['EmergencyOS']:billPaid(billId)
RegisterDataProvider(tbl) — Server (sv_data_provider.lua). Registriert die Custom-Framework-Hooks. Gültige Hooks: getIdentifier, resolveSource, getPlayer, searchPlayers, getVehicle, searchVehicles, findByPhone, getJob, removeMoney. Rückgabe: true/false. Alle Hooks müssen synchron liefern (oxmysql:executeSync).
exports['EmergencyOS']:RegisterDataProvider({
getIdentifier = function(src) return "char:"..MyCore.GetCharId(src) end,
getPlayer = function(id) return { firstname="Max", lastname="Muster", dob="1990-05-14" } end,
})
HasDataProvider() — Server. true, wenn ein Provider registriert ist.
if exports['EmergencyOS']:HasDataProvider() then ... end
GetPlayerIdentifierEOS(src) — Server. Persistenter Panel-Identifier eines Spielers (Provider → ESX license → QB citizenid → FiveM license).
local ident = exports['EmergencyOS']:GetPlayerIdentifierEOS(src)
ResolveIdentifierToSource(identifier) — Server. Umkehrung: Identifier → Server-ID (oder nil).
local src = exports['EmergencyOS']:ResolveIdentifierToSource(ident)
RecordArrest(officerSrc) — Server (sv_duty.lua). Schreibt dem Beamten eine Verhaftung gut (erscheint in Team-Übersicht/Detailansicht/Settings). Rückgabe: true/false (false bei ungültiger/offline Source).
exports['EmergencyOS']:RecordArrest(officerSource) -- aus dem eigenen Jail-Script
AddOfficerActivity(officerSrc, column, amount) — Server (sv_duty.lua). Erhöht einen beliebigen Aktivitäts-Zähler (z. B. 'arrests', 'scans', 'plate_checks', 'panics').
exports['EmergencyOS']:AddOfficerActivity(src, 'arrests', 1)
IsOnDuty(src) — Server (sv_duty.lua). true, wenn der Spieler aktuell als im Dienst geführt wird.
if exports['EmergencyOS']:IsOnDuty(src) then ... end
PlayTicketCeremony(officerSrc, targetSrc, amount, reason, fraction) — Server (sv_ticket_fx.lua). Spielt die Digital-Citation-Zeremonie ab (ohne echte Rechnung). officerSrc darf nil sein → Zustellung „per Post" (mode='mail'), sonst Vor-Ort-Modus (mode='handover'). Rückgabe: true/false.
exports['EmergencyOS']:PlayTicketCeremony(officerSrc, targetSrc, 250, "Falschparken", "LSPD")
IsPlateWanted(plate) — Server (sync_bridge.lua). true, wenn das (normalisierte) Kennzeichen im BOLO-RAM-Cache steht. Nicht-blockierend.
if exports['EmergencyOS']:IsPlateWanted("LS12345") then ... end
GetWanteds() — Server (sync_bridge.lua). Gibt wanteds (Tabelle) und version (Zahl) des aktuellen BOLO-Cache zurück.
local wanteds, version = exports['EmergencyOS']:GetWanteds()
MdtLookup(payload, cb) — Server (sync_bridge.lua). Asynchrone MDT-Abfrage ans Panel (mit Live-Fallback + Cache). payload z. B. { action="personDetail", id="emergencyos__LIVE:..." } bzw. { action=..., query=... }. Ergebnis kommt über cb(result).
exports['EmergencyOS']:MdtLookup({ action = "plate", query = "LS12345" }, function(res) ... end)
Öffentliche API-Events ans Panel (Payloads)
Für die Anbindung eigener Scripts (Verhaftungen/Scan/Dispatch/Tickets). Server-authoritativ, alle serverseitig auf WhitelistedJobs geprüft:
EmergencyOS:PanicButton — TriggerServerEvent. Payload: coords (vector3/Table mit x,y), streetName (string). Broadcastet 10-13 an alle Whitelist-Einheiten (bzw. übergibt an Vanguard, falls aktiv).
TriggerServerEvent("EmergencyOS:PanicButton", vec3(443.46,-982.45,30.68), "Mission Row")
EmergencyOS:ScanClosestPlayer — TriggerServerEvent. Payload: targetServerId (number). Serverseitige Distanzprüfung (≤ 6 m), öffnet die Personenakte des Ziels beim scannenden Beamten (ForceOpenRecord). Zählt als Aktivität scans.
TriggerServerEvent("EmergencyOS:ScanClosestPlayer", targetSource)
EmergencyOS:CheckALPR — TriggerServerEvent. Payload: plate (string, ≤ 12 Zeichen). Prüft BOLO-Status, Cooldown 10 s pro Spieler/Kennzeichen; bei Treffer ALPRResult an den Absender.
TriggerServerEvent("EmergencyOS:CheckALPR", "LS12345")
Verhaftungen ans Panel: über den Export RecordArrest(officerSrc) (siehe oben) — der empfohlene Weg aus dem CHANGELOG/examples/.
Dispatch ans Panel (Vanguard Gateway): Actions getDispatchState, dispatchCreateCall, dispatchAssign, dispatchClose; Live-Push über AddEventHandler('vanguard:dispatch:broadcast', …) → TriggerClientEvent('EmergencyOS:ReceiveLiveLog', -1, { dispatch = payload }).
Events & Commands
Commands (für Betreiber/Spieler relevant)
| Command |
Seite |
Beschreibung |
/openEmergencyOS |
Client |
Öffnet das Tablet (Standard-Keybind MULTIPLY, via Config.UseKeyMapping). |
/strafzettel |
Client |
Bürgerportal – offene Strafzettel einsehen & bezahlen (alle Spieler; Name/Key aus Config.CitizenTickets). |
/runplate |
Client |
Sofort-Halterabfrage (Standard-Keybind F9, aus Config.PlateCheck.defaultKey). |
/panicbutton |
Client |
Panic Button 10-13 (Standard-Taste P; aus Config.PanicButtonCommand/PanicButtonKey). |
/scanid |
Client |
Scannt den nächsten Spieler im Radius 3,0 m (nur Whitelist-Jobs) → öffnet dessen Akte. |
/toggleterminal |
Client |
Schaltet die eingebaute System-Konsole (Terminal) im Tablet um. |
/emos_fix |
Client |
Rettungsanker: setzt festgefahrene Anzeigen/NUI sofort zurück. |
/emos_ticketfx <targetId> [betrag] [grund…] bzw. emos_ticketfx demo … |
Server/Konsole |
Test der Ticket-Zeremonie (keine echte Rechnung). Konsole oder Whitelist-Beamter. |
/emos_dutydebug |
Server/Konsole |
Diagnose: zeigt pro Online-Spieler den erkannten Job, On-Duty-Status, Identifier, emos_duty_stats-Zeilen — zum Ermitteln der exakten Jobnamen für onDutyJobs. |
/testinternal, /stopinternal, /whatprop, /emosgethash |
Client |
Entwickler-/Debug-Hilfen (interner Monitor testen/stoppen, Prop-/Hash-Ermittlung). |
Wichtige Netz-Events (Auszug)
- Bürgerportal: Client→Server
EmergencyOS:Citizen:GetTickets, EmergencyOS:Citizen:PayTicket(ticketId), EmergencyOS:Citizen:PayAll; Server→Client EmergencyOS:Citizen:Tickets, EmergencyOS:Citizen:PaidResult, EmergencyOS:Citizen:PaidAll. Zahlung holt frischen Preis (getOpenTickets.php), bucht ab, markiert atomar (setTicketPaid.php), zahlt an Society aus, mit Refund bei Fehler.
- Dienst & Statistik:
EmergencyOS:Duty:Idle, EmergencyOS:Duty:GetMine, EmergencyOS:Duty:GetBoard(period); Antworten EmergencyOS:Duty:State/Mine/Board.
- Sonstige:
EmergencyOS:openTablet, EmergencyOS:ForceOpenRecord, EmergencyOS:ALPRResult, EmergencyOS:PanicButtonAlert, EmergencyOS:RunPlate, EmergencyOS:TicketFx, EmergencyOS:TicketEvidence, EmergencyOS:SyncPosition (Live-Tracking), EmergencyOS:ReceiveLiveLog (Atlas/Dispatch), EmergencyOS:UpdateWantedBillboard, EmergencyOS:UpdateInternalMonitor.
- Billing:
RegisterNetEvent(SVConfig.BillingTrigger) ("EmergencyOS:billPayed") → meldet billId als bezahlt ans Panel.
Hinweise / Troubleshooting
- Ordnername: Resource muss
EmergencyOS heißen — alle exports['EmergencyOS'] und der Handshake (GetCurrentResourceName()) hängen daran.
- Sync erreicht Panel nicht / Konsolenmeldungen:
- „Kein API-Key hinterlegt" →
SVConfig.API.key in api_config.lua fehlt/ist Platzhalter (eos_HIER_DEN_KEY_EINFUEGEN, selberapikeywieimancomoxdashboard).
- HTTP 400 → Anmeldedaten unvollständig: bei
eos_…-Key darf nur der Key gesetzt sein; bei Legacy-Key müssen username+password gesetzt sein. Danach Resource neu starten.
- HTTP 401/403 → Key falsch/abgelaufen. Nach „Key neu generieren" im Panel den neuen Key eintragen. Weitere Versuche laufen leise alle 10 min.
- „Panel nicht erreichbar" → Netzwerk/Firewall; Retry-Backoff greift automatisch.
- Multi-IP-Server: Schlägt der Verbindungstest im Panel trotz laufendem Server fehl, die erreichbare FiveM-IP in
SVConfig.API.serverIp eintragen (überschreibt die Auto-Erkennung).
- BOLO-Pull-Warnung (
api/getWanteds.php HTTP ≠ 200) → Panel-Dateien nicht/veraltet deployed. Für Dev ohne deployte PHP: SVConfig.BoloDevFallback = true (nur bei geteilter DB).
- Framework-Bridge / Custom Framework: Bei
Config.Framework="STANDALONE" liefern nur registrierte Data-Provider-Hooks Daten; ein Hook, der nil liefert, gilt als „nicht gefunden" (kein Fallback auf ESX/QB). Hooks müssen synchron sein (oxmysql:executeSync). Jobs zusätzlich über SetPlayerJob (Server und Client) setzen. getIdentifier muss Reconnects überleben (Server-IDs ungeeignet); ohne Provider wird die FiveM-License genutzt.
- Dienstzeiten falscher/kein Job erkannt:
/emos_dutydebug zeigt den exakt erkannten Jobnamen → in Config.DutyStats.onDutyJobs bzw. jobFaction eintragen; Off-Duty-Jobs nicht auflisten.
- Ticket-Zeremonie ohne Beweisfoto:
screenshot-basic fehlt oder Config.ScreenshotWebhook ist leer/Platzhalter → Evidence Cam deaktiviert.
- Tickets vor dem Update lassen sich nicht nachträglich Beamten zuordnen (Zählung ab Einspielen).
- Update-Check meldet dauerhaft „neue Version":
version.json auf dem Webserver an die tatsächliche Resource-Version 3.9.6 anpassen.
DispatchOS — Dokumentations-Spec
Leitstellen-, Dispatch- und MDT-System für FiveM. Quelle: [ANCOMOX]/dispatchos · Version 2.22.0.
Überblick
Zweck: DispatchOS ist das zentrale Notruf-, Einsatz- und Leitstellensystem. Es nimmt Alarme (automatisch erkannt oder per API) entgegen, legt Einsätze („Calls") mit Priorität, Blip, Zone und Timeline an, verteilt sie an die zuständigen Fraktionsgruppen und stellt eine Vollbild-Leitstelle mit Taktikkarte bereit. Architektur laut Manifest: „RAM-first, Push statt Polling, serverseitig validiert".
Kernfeatures (aus README.md und Code verifiziert):
- Automatische Einsätze (Detektoren): Schüsse, Drive-by, Fahrzeugraub, Schlägerei, Unfall, Raser, Person am Boden, ALPR-Treffer, Panik, Zivil-Notrufe — mit Merge naher Mehrfachmeldungen (MergeRadius/MergeTime).
- Leitstelle / Dispatch Center (F6): Taktikkarte mit Live-Einheiten, Icon-Marker je Code, Einsatzliste + Filter, Detail-Dock mit Timeline & Notizen, Zuweisung, Fahndungs-Ticker (BOLO), Ø-Reaktionszeit-KPI, 7-Tage-Statistik-Cockpit, Panic-Auto-Fokus.
- MDT (F7): Kennzeichen-/Personenabfrage aus der Spieldatenbank (DirectDB) oder über EmergencyOS-Gateway; Fahndungsabgleich, Live-Online-Status.
- ALPR (F10/F11): Kennzeichenscanner im Einsatzfahrzeug per Raycast mit 3D-Holo-Marker und Fahndungstreffer.
- Schnellfunk (F5): konfigurierbare 10-Codes + Freitext + Position.
- Panic Button, Smart-3D-Ping, Eskalation (P3→P2→P1), Zonen-Routing, Rechte-System, Auto-Zuweisung bei Panik/Backup.
- Notruf-Integration: lb-phone (Nachricht + Anruf + Handy-Alerts) und generische Export-API für andere Telefon-Scripts.
- Optionale Web-Leitstelle (dispatchos.de) und EmergencyOS-Panel-Anbindung über FiveM-Events.
- Zweisprachig (de/en), Discord-Webhook-Feed integriert.
Voraussetzungen
Das fxmanifest.lua deklariert keine dependencies — alle Abhängigkeiten werden zur Laufzeit per GetResourceState(...) weich erkannt. Manifest-Eckdaten:
| Manifest-Feld |
Wert |
fx_version |
cerulean |
game |
gta5 |
lua54 |
yes |
ui_page |
html/index.html |
Abhängigkeiten (alle laufzeit-erkannt, keine harte Pflicht):
| Komponente |
Pflicht |
Zweck |
| FiveM-Server (aktuelle Artifacts) |
ja |
Basis |
| oxmysql |
empfohlen |
Persistenz (dispatchos_calls), Statistik/Heatmap, MDT-DirectDB. Ohne → alles läuft RAM-only, keine DB-Statistik. |
ESX (es_extended) / QBCore (qb-core / qbx_core) |
optional |
Job/Duty/Namen. Auto-Erkennung; sonst STANDALONE. |
| EmergencyOS |
optional |
BOLO-Sync, Atlas-Feed, Panel-MDT-Gateway, Tablet-Panic, Heatmap. |
| lb-phone |
optional |
Telefon-Notrufe (Modul modules/phone). |
| ox_lib |
optional |
Schönere Notifications (ox_lib:notify); sonst Chat-Fallback. |
Installation
- Ordner
dispatchos nach resources/ kopieren.
- In
server.cfg nach oxmysql, Framework und (optional) EmergencyOS starten:
cfg
ensure oxmysql
ensure es_extended # bzw. qb-core / qbx_core
ensure EmergencyOS # optional
ensure dispatchos
- Tabelle
dispatchos_calls wird beim ersten Start automatisch angelegt bzw. migriert (fehlende Spalten werden ergänzt; einmalige Übernahme einer alten vanguard_calls-Tabelle).
config.lua anpassen — v. a. VConfig.JobGroups an die eigenen Fraktions-Jobs.
Panel-Bezug: Zwei optionale Anbindungen (beide ohne Ingame-Paywall): die gehostete Web-Leitstelle (VConfig.WebLink, Modul web_link) und das EmergencyOS-Panel (rein über FiveM-Events, siehe nächster Abschnitt).
Datenbank
Nur bei VConfig.PersistCalls = true und gestartetem oxmysql. Eine Tabelle:
dispatchos_calls — angelegt via CREATE TABLE IF NOT EXISTS, danach Auto-Migration der Spalten (SHOW COLUMNS → fehlende per ALTER TABLE ADD COLUMN).
| Spalte |
Typ |
Bemerkung |
id |
INT AUTO_INCREMENT PK |
|
code |
VARCHAR(16) |
z. B. 10-71 |
label |
VARCHAR(120) |
|
street |
VARCHAR(96) |
|
priority |
TINYINT DEFAULT 3 |
1–3 |
x, y, z |
FLOAT DEFAULT 0 |
z per Migration ergänzt |
responded_at |
TIMESTAMP NULL |
erste Zuweisung → Reaktionszeit-KPI |
arrived_at |
TIMESTAMP NULL |
Auto-„Angekommen" |
zone |
VARCHAR(48) |
Zuständigkeitszone |
reports |
INT DEFAULT 1 |
Anzahl gemergter Meldungen |
units |
INT DEFAULT 0 |
zugewiesene Einheiten (bei Close) |
created_at |
TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
Index idx_created |
closed_at |
TIMESTAMP NULL |
|
Engine InnoDB, Charset utf8mb4. Retention: Zeilen älter als VConfig.DBRetentionDays werden periodisch gelöscht (Batch LIMIT 5000; 0 = nie). Einmal-Rename beim Start: falls vanguard_calls existiert und dispatchos_calls nicht, wird umbenannt (Bestandsdaten-Übernahme).
Die MDT-DirectDB liest zusätzlich fremde Tabellen (nur SELECT, keine eigene Anlage): ESX users/owned_vehicles, QBCore players/player_vehicles sowie optional eine lb-phone-Tabelle (Default phone_phones). Tabellennamen über VConfig.MDT.DirectDB überschreibbar.
Sync / Panel-Anbindung
A) EmergencyOS-Panel (Event-basiert, keine Webserver-Last)
Wird automatisch aktiv, sobald GetResourceState('EmergencyOS') == 'started':
- BOLO/Fahndung: DispatchOS ruft per pcall einen der Exports exports.EmergencyOS:GetWanteds/GetBolos/GetWantedPlates/GetWantedList ab (erster funktionierender wird gemerkt) und exports.EmergencyOS:IsPlateWanted(plate). Push-Refresh über Listener EmergencyOS:WantedsUpdated.
- Panic: Kompatibilitäts-Listener RegisterNetEvent('EmergencyOS:PanicButton', ...) — Tablet-Panik landet direkt als Einsatz (Dedupe über Cooldown).
- Atlas-Feed: internes Event dispatchos:dispatch:broadcast transportiert alle Dispatch-/Funk-/Status-Ereignisse (auch Discord/Phone/WebLink hängen hier dran).
- MDT-Gateway: exports.EmergencyOS:MdtLookup(payload, cb) (Panel-Akten + Live-Fallback), genutzt wenn DirectDB aus ist bzw. für Panel-Anreicherung (PanelEnrich/PanelFileLookup).
- Akte öffnen: NUI-Callback mdtOpenRecord → TriggerEvent('EmergencyOS:ForceOpenRecord', id).
- Externer Fokus: AddEventHandler('dispatchos:client:FocusCall', callId) öffnet die Leitstelle und fliegt per Kamera auf den Einsatz (für Atlas-Terminal-Klick).
- Statistik/Heatmap: gemeinsame Tabelle dispatchos_calls.
Für vollen Umfang liegen dem EmergencyOS-Paket 3 aktualisierte Dateien bei (html/app.js, Panel-Terminal.php, kleine client.lua-Ergänzungen). MDT.PanelFileLookup erst nach verbundener Panel-API (gültiger API-Key) aktivieren.
B) Web-Leitstelle (modules/web_link/server.lua, optional)
Nur aktiv, wenn VConfig.WebLink.Token gesetzt und kein Platzhalter ist (Platzhalter-Liste im Code: '', HIER_DEN_TOKEN_EINFUEGEN, los_HIER_DEN_KEY_EINFUEGEN). Ohne gültigen Token: Modul kehrt sofort zurück → reines Standalone.
- Nur ausgehendes HTTPS (
PerformHttpRequest), kein offener Port. Auth per Authorization: Bearer <Token> und Api-Key: <Token> (Apache/PHP-FPM-Kompatibilität).
- Handshake:
GET {BridgeUrl}/v1/hello → prüft Token; Klartext-Feedback in der Konsole (gültig / abgelaufen / unerreichbar / ungültig).
- Push-Loop: Snapshot + Events an
POST {BridgeUrl}/v1/ingest (Intervall PushIntervalMs, Queue max. 400). HTTP 403 = Abo abgelaufen → pausiert, Standalone läuft weiter.
- Command-Long-Poll:
GET {BridgeUrl}/v1/commands?wait=<PollWaitSec>; führt Web-Kommandos aus (Executors: call.assign/unassign/close/note/create, unit.status/callsign, radio.send, gps.send, bolo.add/remove, sys.presence/resync/idle, config.set).
- Status-Global
DispatchOSWebLink = { configured, linked } fürs Leitstellen-Settings-Panel.
Konfiguration
Alle Optionen aus config.lua (VConfig = {}). Zusätzlich am Ende die hartcodierten Blöcke aus server/main.lua (Discord/Auto-Panik) und Config-Keys, die der Code liest, aber die shipped config.lua nicht enthält (mit „(nicht in config.lua)").
Basis
| Key |
Standard |
Beschreibung |
Framework |
"auto" |
"ESX" | "QBCORE" | "STANDALONE" | "auto". Auto: erkennt es_extended, sonst qbx_core/qb-core, sonst Standalone. |
UseDuty |
true |
Wenn true, werden Off-Duty-Spieler als Nicht-Einheit ignoriert (QBCore & Export-Duty). |
Locale |
"de" |
Sprache der Server-/HUD-/Leitstellen-Texte ("de" / "en"). |
JobGroups |
Tabelle (s. u.) |
Job → Alarmgruppen. Ein Job kann mehrere Gruppen empfangen. |
JobGroups-Default: police = {leo, leo_city, leo_county}, lssd = {leo, leo_county}, fib = {leo, leo_city, leo_county}, justiz = {leo, leo_city, leo_county}, ambulance = {ems}.
MDT
| Key |
Standard |
Beschreibung |
MDT.PanelEnrich |
true |
Live-Online-Status (framework-nativ) in Personenakte via EmergencyOS. |
MDT.PanelFileLookup |
false |
Panel-Akten-Lookup („Akte öffnen") — erst mit verbundener Panel-API aktivieren. |
MDT.Enabled |
true |
MDT-Modul an/aus (Client & Server prüfen dies). |
MDT.Keybind |
"F7" |
MDT öffnen/schließen. |
MDT.AlprQueryKeybind |
"F11" |
Letztes ALPR-Kennzeichen direkt im MDT abfragen. |
MDT.LookupBurst |
8 |
Token-Bucket: so viele Abfragen sofort hintereinander. |
MDT.LookupRefillMs |
1000 |
Nachschub: 1 Abfrage-Token pro X ms. |
MDT.Debug |
false |
Konsolen-Trace der MDT-Kette. |
MDT.AllowedJobs |
{police, lssd, fib, justiz = true} |
Jobs mit MDT-Zugriff. |
MDT.DirectDB.Enabled |
true |
Personen/Fahrzeuge direkt aus Spiel-DB statt Gateway. |
MDT.DirectDB.UsersTable |
nil |
Override Users-Tabelle (ESX default users, QB players). |
MDT.DirectDB.VehiclesTable |
nil |
Override Fahrzeug-Tabelle (ESX owned_vehicles, QB player_vehicles). |
MDT.DirectDB.Phone.UsersColumn |
"phone_number" |
Telefon-Spalte in der Users-Tabelle. |
MDT.DirectDB.Phone.Table |
"phone_phones" |
lb-phone-Tabelle (Fallback). |
MDT.DirectDB.Phone.PhoneColumn |
"phone_number" |
Nummer-Spalte in der Phone-Tabelle. |
MDT.DirectDB.Phone.IdentifierColumn |
"owner_id" |
Besitzer-Spalte (LIKE-Match). |
DispatchCenter
| Key |
Standard |
Beschreibung |
DispatchCenter.Enabled |
true |
Leitstelle an/aus. |
DispatchCenter.Keybind |
"F6" |
Leitstelle öffnen/schließen. |
DispatchCenter.BrandName |
"DISPATCHOS" |
Header-Branding. |
DispatchCenter.BrandSub |
"LEITSTELLE" |
Untertitel. |
DispatchCenter.FocusGuard |
true |
Verhindert Öffnen, wenn anderes NUI den Focus hält. |
DispatchCenter.PanicFocus |
true |
Auto-Kameraflug bei Panik (10-13). |
DispatchCenter.MapIcons |
Tabelle |
FontAwesome-Icon je Code, z. B. ["10-71"]="fa-gun", default="fa-location-dot". |
DispatchCenter.Map.Image |
"map/atlas.jpg" |
Kartenbild. |
DispatchCenter.Map.Width/Height |
4096 / 4096 |
Bildmaße. |
DispatchCenter.Map.MinX/MaxX |
-5661.196911 / 6694.015444 |
Weltkoordinaten-Mapping X. |
DispatchCenter.Map.MinY/MaxY |
-4058.536586 / 8429.268293 |
Weltkoordinaten-Mapping Y. |
DispatchCenter.Map.PosInterval |
2500 |
Positions-Broadcast-Takt (ms; min. 1000 erzwungen). |
Statuscodes, Eskalation, Funk
| Key |
Standard |
Beschreibung |
Statuses |
{"10-8","10-6","10-7","10-11","10-15","10-97"} |
Gültige Unit-Status-Codes. |
Escalation.Enabled |
true |
Auto-Höherstufung wartender Einsätze. |
Escalation.Steps |
{[3]=120, [2]=180} |
Sekunden ohne Einheit je Stufe (P3→P2 nach 120 s, P2→P1 nach 180 s). |
Radio.Enabled |
true |
Schnellfunk-Modul. |
Radio.Keybind |
"F5" |
Funk-Panel. |
Radio.Cooldown |
2000 |
Anti-Spam (ms) pro Sender. |
Radio.Codes |
Liste |
10-Codes; je Eintrag code, label, optional pos=true (Position anhängen), priority=true (roter Alarmton). Codes, die auch in Statuses stehen, setzen den Unit-Status mit. |
Radio.Codes-Default: 10-4 Verstanden, 10-8 Einsatzbereit, 10-6 Beschäftigt, 10-97 Am Einsatzort, 10-20 Standort durchgeben (pos), 10-13 Benötige Unterstützung (priority,pos), Code 2, Code 3.
Zones
| Key |
Standard |
Beschreibung |
Zones.Enabled |
true |
Zonen-Routing an/aus. |
Zones.BaseGroup |
"leo" |
Basisgruppe, die auf Zonengruppen aufgefächert wird. |
Zones.FallbackToAll |
true |
Wenn niemand in der Zonengruppe online: an Basisgruppe zurückfallen (keine unversorgten Einsätze). |
Zones.List |
Liste |
Von oben nach unten geprüft: name, groups, optional poly (Polygon {x,y}) oder center+radius; Eintrag ohne Shape = Rest der Karte. Default: „Los Santos" (Polygon → leo_city), „Blaine County" (→ leo_county). |
Permissions
Jeder Wert: "unit" (jede Einheit) oder { jobs = { <job> = <Mindest-Grade> } }.
| Key |
Standard |
Aktion |
Permissions.CloseCall |
"unit" |
Einsatz schließen. |
Permissions.AssignUnits |
"unit" |
Fremde Einheiten zuweisen / Route senden. |
Permissions.CreateCall |
"unit" |
Manuellen Einsatz erstellen. |
Permissions.ManageBolos |
"unit" |
/addbolo / /delbolo. |
Permissions.AddNotes |
"unit" |
Notizen an Einsätze. |
Zeiten, Merge, Blips, Melder
| Key |
Standard |
Beschreibung |
CallLifetime |
600 |
Sekunden, dann Auto-Close durch „SYSTEM". |
MergeRadius |
60.0 |
Merge-Radius (Meter) für gleichen mergeKey. |
MergeTime |
90 |
Merge-Zeitfenster (Sekunden). |
BlipLifetime |
120 |
Blip-Standzeit am Client (Sekunden). |
NotifyDuration |
15 |
HUD-Karten-Anzeigedauer (Sekunden). |
PersistCalls |
true |
Einsätze in DB schreiben. |
DBRetentionDays |
90 |
Aufbewahrung; 0 = nie löschen. |
Show911Caller |
true |
Melder-Name bei /911//912 zeigen (sonst „Anonym"). |
Keybinds & Panik
| Key |
Standard |
Beschreibung |
RespondKeybind |
"F9" |
Auf letzten/gewählten Einsatz reagieren. |
PanicKeybind |
"" |
Panic-Taste (leer = nur /dispatchpanic / GTA-Belegung). |
AutoRoutePanic |
true |
Leitet bei Panik automatisch alle Kollegen per GPS hin. |
AlprKeybind |
"F10" |
ALPR-Radar an/aus. |
AlprLockKeybind |
"NUMPAD8" |
ALPR-Ziel sperren + 3D-Marker. |
SmartPing (3D-Ping)
| Key |
Standard |
Beschreibung |
SmartPing.Enabled |
true |
Smart-Ping-System. |
SmartPing.Keybind |
"MOUSE_MIDDLE" |
Ping-Taste (Mausrad-Klick). |
SmartPing.Duration |
10 |
Sichtbarkeit (Sekunden). |
SmartPing.MaxDistance |
800.0 |
Max. Sichtweite des Markers. |
ALPR & Detektoren
| Key |
Standard |
Beschreibung |
AlprScanDistance |
35.0 |
Raycast-Reichweite (Meter). |
AlprScanRear |
true |
Auch nach hinten scannen. |
AlprScanIntervalMs |
400 |
Raycast-Takt (ms). |
IgnoredWeapons |
Set |
Waffen, die keinen Schuss-Alarm auslösen (Stungun, Flaregun, Snowball, Ball, Fireextinguisher, Petrolcan). |
IgnoreSilenced |
true |
Schallgedämpfte Schüsse ignorieren. |
DetectorDebug |
false |
Konsolen-Trace der Detektoren. |
Sounds
Je Ereignis { name, set } (Frontend-Sound) oder false (stumm). Keys mit Defaults: panic (Laster_10A/GTAO_Biker_FM_Soundset), p1/p2 (Beep_Red/DLC_HEIST_HACKING_SNAKE_SOUNDS), p3 (Text_Arrive_Tone/Phone_SoundSet_Default), alprHit, radio, radioPrio, ping (WAYPOINT_SET/HUD_FRONTEND_DEFAULT_SOUNDSET).
AutoCalls
Je Auslöser: enabled, code, labelKey, priority (1–3), groups, meist cooldown (s), ignoreUnits (Einsatzkräfte lösen nicht selbst aus), teils minSpeed, plus blip = { sprite, color, scale }.
| Auslöser |
enabled |
code |
priority |
groups |
Besonderheit |
shots |
true |
10-71 |
2 |
leo |
cooldown 20, ignoreUnits |
driveby |
true |
10-71 |
2 |
leo |
cooldown 20, ignoreUnits |
carjack |
true |
10-90 |
3 |
leo |
cooldown 30, ignoreUnits |
fight |
false |
10-10 |
3 |
leo |
cooldown 30, ignoreUnits |
crash |
true |
10-50 |
2 |
leo, ems |
cooldown 30, ignoreUnits |
speeding |
true |
10-50 |
3 |
leo |
cooldown 45, minSpeed=160, ignoreUnits |
playerdown |
true |
10-11 |
2 |
ems, leo |
cooldown 30, ignoreUnits=false |
alpr_hit |
true |
10-99 |
2 |
leo |
— |
panic |
true |
10-13 |
1 |
leo, ems |
Blip sprite 161, scale 1.5 |
civ_police |
true |
911 |
3 |
leo |
/911 |
civ_ems |
true |
912 |
2 |
ems |
/912 |
Locales
VConfig.Locales.de / .en — vollständige Text-Tabellen (HUD, Leitstelle, MDT, Funk, BOLO, Meldungen). Labels der AutoCalls kommen über labelKey (z. B. lbl_shots, lbl_panic).
WebLink
| Key |
Standard |
Beschreibung |
WebLink.Token |
'dos_6869…' |
Web-Panel-Token; leer/Platzhalter = Standalone. |
WebLink.BridgeUrl |
'https://dispatchos.de/bridge' |
Basis-URL der PHP-Bridge. |
WebLink.PushIntervalMs |
900 |
Event-Push-Intervall (ms). |
WebLink.PollWaitSec |
8 |
Long-Poll-Wartezeit (PHP-Bridge max. 8; Code erzwingt min. 4). |
WebLink.HandshakeRetrySec |
30 |
Retry-Intervall Verbindungsaufbau. |
WebLink.IdlePollSec |
45 (nicht in config.lua) |
Poll-Takt bei leerem Server. |
WebLink.Runtime |
– (zur Laufzeit gesetzt) |
Vom Web-Executor config.set befüllt. |
Phone
| Key |
Standard |
Beschreibung |
Phone.Enabled |
true |
Telefon-Notruf-Modul. |
Phone.Companies |
police={groups={leo},label='Notruf',code='911'}, ambulance={groups={ems},label='Notruf',code='112'} |
lb-phone-Firma → Alarmgruppen. |
Phone.MessagePriority |
3 |
Priorität einer Services-Nachricht mit Standort. |
Phone.MissedCallPriority |
2 |
Unbeantworteter Notruf-Anruf. |
Phone.CreateOnAnswered |
false |
Auch bei angenommenem Anruf Einsatz anlegen. |
Phone.AnsweredPriority |
3 |
Priorität bei angenommenem Anruf. |
Phone.PushDispatches |
true |
Neue Einsätze als lb-phone-Emergency-Alert. |
Phone.PushMinPriority |
2 |
Alerts nur ≤ dieser Prio (Panik immer). |
Phone.Cooldown |
15000 |
Spam-Schutz je Anrufer/Nummer (ms). |
Config-Keys, die der Code liest, aber die config.lua NICHT enthält (Defaults im Code)
| Key |
Fallback |
Wirkung |
VConfig.ArrivedStatus |
nil |
Wenn gesetzt: Einheit bei Ankunft automatisch auf diesen Status. |
VConfig.ArriveRadius |
60.0 |
Radius (Meter) für Auto-„Angekommen". |
VConfig.Sla.Enabled |
true (≠ false) |
SLA-Wächter: wartende Einsätze erneut in Erinnerung rufen. |
VConfig.Sla.RemindAfter |
240 |
Sekunden bis zur ersten Erinnerung. |
VConfig.Sla.RemindEvery |
300 |
Wiederhol-Intervall (Sekunden). |
Hartcodierte Blöcke in server/main.lua (nicht in config.lua)
Discord-Feed (local DISCORD): enabled=true, webhook="https://discord.com/api/webhooks/DEINE/URL_HIER" (eintragen), username="Leitstelle", avatar="", postNew=true, postClosed=true, postRadio=false, pingRoleOnPanic="". Postet neue/geschlossene Einsätze (optional Funk/Panik-Ping) über dispatchos:dispatch:broadcast.
Auto-Panik-Zuweisung (local AUTO_PANIC): enabled=true, onlyIfUnassigned=true, notifyUnit=true. Weist bei neuem Panik-Einsatz automatisch die nächste freie On-Duty-Einheit derselben Abteilung zu.
Exports
Insgesamt 23 Exports (README nennt „~21"): 18 Server in server/main.lua, 1 Server in modules/phone/server.lua, 4 Client in client/main.lua. Namen SetPlayerJob und CreateCall existieren doppelt (client- und serverseitig, unterschiedliche Signatur/Wirkung — Kontext beachten).
Aufruf serverseitig: exports.dispatchos:Name(...) bzw. exports['dispatchos']:Name(...). Ressourcenname = Ordnername (dispatchos).
Server-Exports (server/main.lua)
1. CreateCall(data) — Seite: server
- Signatur: exports.dispatchos:CreateCall(data) — data muss data.coords = {x,y,z} enthalten.
- Felder (alle optional außer coords): code (≤16, Default "CALL"), label (≤120, Default "Einsatz"), priority (1–3, Default 3), groups (Default {"leo"}, danach Zonen-Routing), street (≤96), plate (≤12), vehName (≤40), details (≤200), sender (≤40), panic (bool), mergeKey, blip = {sprite,color,scale}.
- Rückgabe: Call-id (number) — oder bei Merge die bestehende id; nil bei ungültigen Daten.
- Beschreibung: Kern-Funktion zum Anlegen eines Einsatzes; führt Merge (gleicher mergeKey im Radius/Zeitfenster), Zonen-Routing, Broadcast an passende Gruppen und DB-Insert aus.
local id = exports.dispatchos:CreateCall({
code='10-90', label='Fahrzeugraub', priority=2,
groups={'leo'}, coords={x=215.3,y=-810.1,z=30.0},
street='Legion Square', mergeKey='mycustom'
})
2. CloseCall(id, closedBy) — server · Signatur (id, closedBy) → bool. Schließt Einsatz, schreibt closed_at/reports/units, informiert Gruppen. closedBy = Anzeigename im Feed.
exports.dispatchos:CloseCall(42, 'ADMIN')
3. AttachUnit(id, src) — server · (callId, playerSrc) → bool. Weist Einheit zu (setzt Waypoint, Status active, responded_at). Nur wenn Einheit die Gruppe teilt.
4. DetachUnit(id, src) — server · (callId, playerSrc) → bool. Entfernt Einheit; ohne verbleibende Einheiten → Status pending.
5. AddDispatchCall(code, label, details, coords, priority, groups, blip, mergeKey, plate, vehName, sender) — server · Positionsargumente-Wrapper um CreateCall → gibt id zurück. priority Default 3, groups Default {"leo"}, sender Default "System".
local id = exports.dispatchos:AddDispatchCall(
'10-31','Alarm',nil,{x=1.0,y=2.0,z=3.0},1,{'leo'},60)
6. Alarm(a) — server · Öffentliche Alarm-API → id oder nil. a.type: 'ladenraub' (10-90, P2), 'bankraub' (10-90, P1), 'gefaengnisausbruch' (10-98, P1) oder 'custom'. Weitere Felder: coords, code, label, details, priority, groups (Default {'leo'}), blip, mergeKey, sender. Auch via Event TriggerEvent('dispatchos:alarm', a).
local id = exports.dispatchos:Alarm({
type='custom', code='10-31', label='Juwelier-Alarm',
coords=coords, priority=1, groups={'leo'}, blip=617 })
7. GetCall(id) — server · → öffentliche Call-Tabelle (id, code, label, priority, coords, street, units, history, …) oder nil.
8. GetActiveCalls() — server · → Array aller aktiven Einsätze (öffentliche Form), nach id absteigend sortiert.
9. GetUnits() — server · → Array aller On-Duty-Einheiten {src, name, callsign, status, job}.
10. AddWantedPlate(p, reason) — server · Setzt lokale Fahndung (BOLO) für Kennzeichen; reason ≤90. Pusht an Leitstellen-Viewer. Keine Rückgabe.
exports.dispatchos:AddWantedPlate('ABC123', 'Fahrerflucht')
11. RemoveWantedPlate(p) — server · Entfernt lokale Fahndung. Keine Rückgabe.
12. SetPlayerJob(src, job) — server · Setzt Job im STANDALONE-Tracker (StandaloneJobs[src]). Für Server ohne ESX/QBCore.
exports.dispatchos:SetPlayerJob(source, 'police')
13. SetPlayerDuty(src, state) — server · Setzt Duty-Flag (DutyStates[src]). Wirkt mit UseDuty=true.
14. LstSetStatus(src, st) — server · Web-Leitstand-Wrapper → bool. Setzt Unit-Status (validiert gegen Statuses).
15. LstSetCallsign(src, cs) — server · → true. Setzt Funkrufname.
16. LstGps(src, x, y) — server · → bool. Sendet Waypoint an Einheit.
17. LstAddNote(id, who, text) — server · → bool. Fügt Notiz (≤140) an Einsatz-Timeline.
18. LstRadio(who, code, text) — server · → bool. Sendet Funkspruch an alle Einheiten.
14–18 („Lst…") sind bewusst schlanke Wrapper für das Web-Link-Modul, funktionieren aber auch direkt.
Server-Export (modules/phone/server.lua)
19. PhoneEmergencyCall(data) — server · Generische Notruf-API für beliebige Telefon-Scripts → callId oder nil.
- Felder: x, y (Pflicht), z, code, label, details (≤140), priority (1–3), group ('leo'|'ems'|'both') oder groups, sender (≤24) oder anonymous=true. Eingebauter Cooldown pro Sender (VConfig.Phone.Cooldown), automatischer mergeKey='phone:<sender>'.
local id = exports.dispatchos:PhoneEmergencyCall({
x=215.3, y=-810.1, details='Bewaffneter Überfall',
sender='555-0123', group='leo', priority=2, code='911', label='Notruf' })
Client-Exports (client/main.lua)
20. CreateCall(data) — client · Third-Party-Integration. Ergänzt fehlende data.coords mit der Spielerposition und feuert TriggerServerEvent('dispatchos:server:CreateExternalCall', data). Keine Rückgabe (asynchron). Serverseitig gilt für echte Spieler: On-Duty-Pflicht, Rate-Limit, panic wird verworfen.
exports.dispatchos:CreateCall({ code='911', label='Notruf', priority=2 })
21. SetPlayerJob(job) — client · Setzt DispatchOSJob lokal (für Standalone/eigene Job-Logik). Keine Rückgabe.
22. SetDuty(state) — client · Setzt DispatchOSOnDuty lokal. Keine Rückgabe. (Beachte: Export heißt client SetDuty, server SetPlayerDuty.)
23. TriggerPanic() — client · Löst den Panic-Button aus (ExecuteCommand('dispatchpanic')). Keine Rückgabe.
exports.dispatchos:TriggerPanic()
Events & Commands
Öffentliche/Integrations-Events (für eigene Scripts relevant)
TriggerEvent('dispatchos:alarm', { type=..., coords=..., details=... }) — serverseitig, Alarm auslösen (Pendant zu Export Alarm).
TriggerEvent('dispatchos:server:CreateExternalCall', data) — serverseitig (source 0 erlaubt) Einsatz anlegen; von Spieler-Clients gesendet gilt On-Duty + Rate-Limit, panic entfernt.
dispatchos:dispatch:broadcast — internes Feed-Event ({action='new'|'closed'|'update'|'unit'|'radio', …}); Anknüpfpunkt für Discord, lb-phone-Push, Web-Link, EmergencyOS-Atlas und Auto-Panik. Nicht von Clients auslösbar.
dispatchos:client:FocusCall(callId) — clientlokal, öffnet Leitstelle mit Kamera-Flug (Atlas-Integration).
- Gehörte Fremd-Events:
EmergencyOS:PanicButton, EmergencyOS:WantedsUpdated, lb-phone:newCompanyMessage, lb-phone:callEnded. Ausgelöst: EmergencyOS:ForceOpenRecord.
Client-seitige Server-Events (Auszug, spielergebunden, mit IsUnit/Cooldown/Permission-Guards)
dispatchos:server:WhoAmI, RespondCall, MarkArrived, RequestDispatchState, GetStatsFull, DcViewer, CloseCallReq, LeaveCallReq, SetStatus, SetCallsign, SendRoute (Perm AssignUnits), RadioSend, AddNote (Perm AddNotes), ManualCall (Perm CreateCall), AssignUnit/UnassignUnit (Perm AssignUnits), AutoCall, PanicButton, AlprScan, ReportPos, RequestBackup, SmartPing, SyncDraw/ClearBoard (Whiteboard). MDT: dispatchos:mdt:LookupPlate, SearchPerson, PersonDetail.
Commands & Standard-Keybinds
| Taste* |
Command |
Funktion |
| F5 |
/dispatchosfunk |
Schnellfunk-Panel |
| F6 |
/dispatchcenter |
Leitstelle (Vollbild) |
| F7 |
/dispatchosmdt |
MDT |
| F9 |
/respondcall [id] |
Auf letzten/gewählten Einsatz reagieren |
| F10 |
/alprradar |
ALPR-Scanner an/aus |
| F11 |
/mdt_query_alpr |
Letztes ALPR-Kennzeichen im MDT |
| NUMPAD8 |
/alprlock |
ALPR-Ziel sperren/entsperren |
| MOUSE_MIDDLE |
/dispatchosping |
Smart-3D-Ping |
| — |
/dispatchpanic |
Panic Button (PanicKeybind) |
| — |
/dispatchhud |
HUD-Karten per Drag & Drop positionieren |
| ↓ / ↑ |
/dispatch_hud_next · /dispatch_hud_prev |
HUD: nächster/voriger Einsatz |
| DELETE |
/dispatch_hud_pin |
HUD anpinnen/lösen |
| (frei) |
/dispatch_hud_gps |
GPS zum gewählten Einsatz |
| END |
/dispatch_hud_backup |
Backup für gewählten Einsatz |
| — |
/status <code> · /callsign <kürzel> |
Unit-Status / Funkrufname |
| — |
/closecall [id] · /leavecall |
Einsatz schließen / verlassen |
| — |
/911 <text> · /912 <text> |
Zivil-Notruf Polizei / Rettungsdienst |
| — |
/addbolo <kz> [grund] · /delbolo <kz> |
Lokale Fahndung (Perm ManageBolos) |
| — |
/bolodebug |
BOLO-Quelle & Cache-Diagnose |
| — |
/dcdiag |
Leitstellen-Selbsttest (warum F6 ggf. nicht öffnet) |
* Alle Tasten in config.lua und pro Spieler unter GTA-Einstellungen → Tastenbelegung → FiveM änderbar.
Hinweise / Troubleshooting
- Einsatzkräfte lösen keine Auto-Alarme aus → Absicht:
ignoreUnits = true (Produktions-Default). Zum Testen im jeweiligen AutoCalls-Eintrag auf false.
fight ist standardmäßig aus (enabled=false) — bei Bedarf aktivieren.
- Kein Blip/keine Karte für einen Job → Job in
JobGroups eintragen und Zonengruppen (leo_city/leo_county) prüfen.
- F6 öffnet nicht → anderes NUI hält den Focus (
FocusGuard). /dcdiag zeigt Job/Unit/Focus-Status; notfalls DispatchCenter.FocusGuard = false.
- „Panel nicht erreichbar" im MDT →
MDT.PanelFileLookup erst nach eingerichteter EmergencyOS-Panel-API aktivieren; ohne DirectDB und ohne EmergencyOS liefert das MDT nur BOLO-Fahndung.
- Diagnose-Schalter →
VConfig.DetectorDebug = true (Detektoren) bzw. VConfig.MDT.Debug = true (MDT-Kette; Default bereits „an", da Debug ~= false).
- Ohne oxmysql → Konsolenhinweis „Calls werden nicht persistiert (Heatmap bleibt leer)"; Statistik fällt auf In-Memory (seit Serverstart) zurück, Kern bleibt voll funktionsfähig.
- Standalone-Job setzen → ohne ESX/QBCore Job/Duty über die Exports
SetPlayerJob/SetPlayerDuty (Server) bzw. SetPlayerJob/SetDuty (Client) pflegen, sonst zählt niemand als Einheit.
- Discord-Feed ist im Code aktiviert (
enabled=true), postet aber nur mit gültiger webhook-URL (sonst still verworfen).
- Sounds
reminder/arrived werden clientseitig abgespielt, sind aber in VConfig.Sounds nicht definiert → bleiben stumm, bis ergänzt.
LicenseOS — Entwickler-Dokumentation
Ressourcen-Ordner: licenseos · fxmanifest-Version 1.0.0 · Frameworks: ESX · QBCore · Qbox · ox_core · Standalone
Überblick
LicenseOS ist ein Führerschein- und Fahrschulsystem für FiveM. Die eigentliche Fach-Logik (Lizenzkatalog, Prüfungsbögen, Preise, Fahrstunden-Vorgaben) läuft in einem externen Web-Panel (https://licenseos.app). Die hier dokumentierte FiveM-Ressource ist die Server-Bridge + NUI-Anbindung zu diesem Panel — sie enthält selbst keine Prüfungsinhalte, sondern verbindet Spielserver und Panel.
Die Ressource leistet vier Dinge:
- Panel ingame öffnen — das Web-Panel wird als NUI-iframe geladen (Muster: EmergencyOS-Tablet). Fahrlehrer arbeiten ingame mit demselben Panel wie im Browser, per Web-Login. Die Berechtigung wird serverseitig geprüft.
- Server-Bridge — Handshake ans Panel, plus ein eingehender Push-Endpunkt, über den das Panel Lizenz-Änderungen live an den Spielserver meldet.
- Exports — Lizenz-Checks (
HasLicense, GetLicenses, …) für eigene Scripts, mit RAM-Cache und optionalem lokalem DB-Cache.
- Framework-Sync — Lizenz-Änderungen aus dem Panel werden live ins Framework gespiegelt (ESX
user_licenses, QB/Qbox metadata.licences, ox_core Charakter-Metadaten).
Lizenzarten (aus Config.LicenseMapping)
| Panel-Klasse |
Bedeutung |
Framework-Lizenz |
A1, A |
Motorrad |
drive_bike |
B, BE |
Auto (PKW / mit Anhänger) |
drive |
C, CE, D |
LKW / Bus |
drive_truck |
BOOT |
Boot |
boat |
FLUG |
Flugzeug / Pilot |
pilot |
WS |
Waffenschein |
weapon |
Die Klassen-Namen (A1, B, FLUG …) stammen aus dem Panel; die Mapping-Tabelle ist frei erweiterbar (siehe Konfiguration).
Prüfungen & Vorgänge
- Theorieprüfung — Fahrlehrer starten per
/theorie <klasse> eine Gruppenprüfung; alle Spieler im Umkreis (Config.TheoryRadius) bekommen den Prüfungsbogen automatisch als Panel-Overlay eingeblendet.
- Fahrstunde — Fahrlehrer tragen per
/fahrstunde <id> <minuten> [notiz] Fahrstunden ein; das Panel zählt Richtung eines Soll-Werts (totalMinutes/required).
- Praktische Prüfung / Lizenzvergabe / Gebühren — werden im Web-Panel abgewickelt.
- Bürger-Abfrage — Spieler rufen per
/lizenzen ihre Lizenzen, Punkte, Gültigkeit und offenen Gebühren im Chat ab.
Lizenz-Status
aktiv · gesperrt · entzogen · abgelaufen (werden in Benachrichtigungen und im licenseos:licenseUpdate-Event verwendet).
Voraussetzungen
fxmanifest.lua:
| Element |
Wert |
fx_version |
cerulean |
game |
gta5 |
lua54 |
yes |
ui_page |
html/wrapper.html |
dependency |
/assetpacks |
- Harte Abhängigkeit: nur
/assetpacks.
- Framework: ESX (
es_extended), QBCore (qb-core), Qbox (qbx_core) oder ox_core (ox_core) — automatisch erkannt, keine Anpassung nötig. Auch Standalone (ohne Framework) läuft; dann Identifier license:….
- Datenbank / oxmysql: optional, aber empfohlen. Wird per
GetResourceState('oxmysql') erkannt. Ohne oxmysql schaltet sich der lokale Lizenz-Cache automatisch ab (dann nur RAM-Cache + Live-API); die Offline-Spielersuche entfällt ebenfalls.
- Web-Panel-Account mit erzeugtem API-Key (Panel → Einstellungen → FiveM-Bridge).
- Netzwerk: Der Spielserver-HTTP-Port (Convar
netPort, Standard 30120) muss vom Webserver aus per HTTP erreichbar sein, damit Panel-Pushes ankommen. Exports & Befehle (Richtung Spiel → Panel) funktionieren auch ohne eingehende Erreichbarkeit.
- Optionale Notify-Ressourcen:
okokNotify oder ox_lib (sonst Chat-Fallback).
Installation
- Ordner als
licenseos in resources/ ablegen.
- Im Web-Panel einen API-Key erzeugen (Einstellungen → FiveM-Bridge, als Inhaber/Admin) und in
config.lua eintragen:
lua
Config.PanelURL = 'https://licenseos.app' -- OHNE abschließenden /
Config.ApiKey = 'los_...'
- In die
server.cfg eintragen:
cfg
ensure licenseos
- Server starten. Beim Start meldet sich der Server am Panel an (
/bridge/getInstance.php) und registriert automatisch den Push-Rückkanal (IP/Port/Resource) — es muss keine Server-IP im Panel eingetragen werden.
Reihenfolge: oxmysql und das genutzte Framework sollten vor licenseos gestartet sein. Die Ressource wartet aber nach: bei Config.Framework = 'auto' bis zu 10 s auf ein Framework, bei erzwungenem Framework bis zu 30 s (und weicht dann nie still auf ein anderes aus, sondern auf Standalone).
Berechtigungen setzen (server.cfg):
# Panel ingame öffnen (zusätzlich zu Config.AllowedJobs):
add_ace group.admin licenseos.open allow
# Fahrlehrer-Befehle /fahrstunde und /theorie:
add_ace group.fahrlehrer licenseos.instructor allow
add_principal identifier.license:DEINE_LICENSE group.fahrlehrer
Panel-Bezug: Das Panel wird ingame über /licenseos bzw. Taste F7 geöffnet und als iframe von Config.PanelURL geladen. Login-Daten werden nur lokal auf dem PC des Spielers gecacht (SetResourceKvp), nie auf dem Server.
Datenbank
Die Ressource legt eine eigene Tabelle an, sofern Config.LocalLicenseCache = true und oxmysql läuft. Die Tabelle wird automatisch beim ersten erfolgreichen Handshake erstellt (kein manuelles SQL-Import nötig):
CREATE TABLE IF NOT EXISTS licenseos_licenses (
license_no VARCHAR(32) PRIMARY KEY,
identifier VARCHAR(80) NOT NULL,
owner_name VARCHAR(60) NOT NULL DEFAULT '',
class VARCHAR(6) NOT NULL,
status VARCHAR(12) NOT NULL,
points INT NOT NULL DEFAULT 0,
expires_at DATE DEFAULT NULL,
INDEX idx_lic_ident (identifier, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
- Master bleibt das Panel. Diese Tabelle ist nur ein lokaler Spiegel: Voll-Sync beim Start (
DELETE + Neu-Einfügen), Delta-Sync alle 15 Min (?since=…), plus Live-Pushes. Datenquelle: PanelURL/bridge/export.php.
- Vorteil:
HasLicense/GetLicenses laufen ohne Internet-Roundtrip; der Server bleibt bei Panel-Wartung spielbar.
- Zusätzlich genutzte (Framework-)Tabellen — nur lesend für die Spielersuche bzw. schreibend für den Sync, werden nicht von dieser Ressource angelegt:
- ESX:
users (Suche), user_licenses (Lizenz-Spiegel)
- QBCore/Qbox:
players (Suche), metadata.licences (Spiegel, über Player-API)
- ox_core:
characters (Suche), Charakter-Metadaten licenseos:<fwLicense> (Spiegel)
Sync / Panel-Anbindung
Die Bridge kommuniziert in beide Richtungen über HTTP-Endpunkte unterhalb von Config.PanelURL. Authentifizierung durchgehend per Api-Key-Header (bzw. Authorization beim eingehenden Endpunkt), zeitkonstant verglichen.
Ausgehend (Spiel → Panel)
| Endpunkt |
Methode |
Zweck |
Antwort (relevant) |
/bridge/getInstance.php |
GET |
Handshake; sendet Api-Key, Resource-Name, Server-Port. Registriert Push-Rückkanal. Re-Handshake stündlich. |
Community-ID (Zahl) |
/bridge/export.php |
GET |
Lizenz-Voll-/Delta-Sync (?since=…) für den lokalen Cache. |
{ ok, licenses[], serverTime, count } |
/bridge/licenses.php?identifier=… |
GET |
Lizenzen eines Spielers (für Live-Export & /lizenzen). |
{ ok, licenses[], openFees } |
/bridge/lessons.php |
POST |
Fahrstunde eintragen (/fahrstunde). |
{ ok, required, totalMinutes, error? } |
/bridge/theory.php |
POST |
Theorieprüfung starten (/theorie). |
{ ok, url, code, error? } |
Eingehend (Panel → Spiel, SetHttpHandler auf Port netPort)
Abgesichert durch: zeitkonstanten Key-Vergleich (SecureCompare), Rate-Limit (Config.ApiRateLimit/10 s), Body-Limit (Config.ApiMaxBodyBytes), 15-s-Watchdog-Timeout (504).
| Aktion (Pfad) |
Zweck |
ping |
Erreichbarkeitstest → { ok, resource } |
refresh |
RAM-Cache leeren |
searchPlayers |
Live-Spielersuche (Online-Spieler + Framework-DB), Rückgabe { identifier, name }[], max. 25 Treffer |
licenseUpdate |
Lizenz-Änderung übernehmen: RAM-Cache invalidieren, lokalen Cache aktualisieren, Framework spiegeln, Spieler benachrichtigen, Event feuern |
Datenschutz by Design: Es gibt keinen automatischen Bürger-Sync beim Joinen. Spieler werden nie pauschal ans Panel übertragen; die Suche läuft live, gespeichert wird ein Spieler erst bei einem echten Vorgang (Lizenz, Prüfung, Fahrstunde).
Lizenz vergeben/entziehen erfolgt im Panel (bzw. wird per licenseUpdate an den Server gepusht) — dafür gibt es bewusst keinen Export. Die Ressourcen-Exports sind lesend (siehe unten).
Konfiguration
Vollständige config.lua. Alle Werte sind die echten Defaults.
| Key |
Standard |
Beschreibung |
Config.PanelURL |
'https://licenseos.app' |
Basis-URL des Web-Panels, ohne abschließenden /. Grundlage aller Bridge-Endpunkte und des iframe. |
Config.ApiKey |
'los_HIER_DEN_KEY_EINFUEGEN' |
API-Key aus dem Panel (Einstellungen → FiveM-Bridge). Solange der Platzhalter oder leer bleibt, ist die Bridge inaktiv. |
Config.Command |
'licenseos' |
Chat-Befehl zum Öffnen des Panels ingame. |
Config.Keybind |
'F7' |
Standard-Tastenbelegung (RegisterKeyMapping). '' = kein Keybind. |
Config.AllowedJobs |
{ ['fahrschule']=0, ['police']=1 } |
Job/Gruppe → Mindest-Grade, ab dem das Panel geöffnet werden darf. Bei ox_core sind das Gruppennamen. |
Config.AcePermission |
'licenseos.open' |
Ace-Permission, die das Öffnen immer erlaubt (zusätzlich zu AllowedJobs). |
Config.Framework |
'auto' |
Framework-Wahl. 'auto' erkennt selbst; erzwingbar: 'esx' / 'qbcore' / 'qbox' / 'ox' / 'standalone'. |
Config.SyncFramework |
true |
Spiegelt Lizenz-Änderungen aus dem Panel live ins Framework (siehe LicenseMapping). |
Config.LicenseMapping |
(Tabelle, s. u.) |
Panel-Klasse → Framework-Lizenzname (ESX type / QB metadata-Key). |
Config.NotifyPlayer |
true |
Informiert den betroffenen Spieler ingame bei Lizenz-Änderung (okokNotify / ox_lib / Chat). |
Config.LessonCommand |
'fahrstunde' |
Fahrlehrer-Befehl zum Eintragen einer Fahrstunde. '' = deaktiviert. |
Config.TheoryCommand |
'theorie' |
Fahrlehrer-Befehl zum Start einer Gruppen-Theorieprüfung. '' = deaktiviert. |
Config.TheoryRadius |
25.0 |
Umkreis in Metern, in dem Spieler zur Theorieprüfung eingeladen werden. |
Config.LocalLicenseCache |
true |
Spiegelt alle Lizenzen in die lokale Tabelle licenseos_licenses (benötigt oxmysql). Ohne oxmysql automatisch aus. |
Config.CitizenCommand |
'lizenzen' |
Bürger-Befehl: eigene Lizenzen/Punkte/Gebühren im Chat. '' = deaktiviert (5 s Cooldown). |
Config.CacheTTL |
90000 |
Gültigkeit des HasLicense-RAM-Caches in ms (90 s). |
Config.ApiRateLimit |
120 |
Max. eingehende Push-Requests pro Adresse je 10 s. |
Config.ApiMaxBodyBytes |
262144 |
Max. Body-Größe eingehender Pushes in Bytes (256 KB). |
Config.AllowedJobs — Job → Mindest-Grade
| Key |
Standard |
Beschreibung |
['fahrschule'] |
0 |
Job „fahrschule" darf ab Grade 0 öffnen. |
['police'] |
1 |
Job „police" darf ab Grade 1 öffnen. |
Erweitern: einfach weitere ['jobname'] = mindestGrade ergänzen. Bei ox_core werden die Keys als Gruppennamen gegen player.getGroup(name) geprüft.
Config.LicenseMapping — Panel-Klasse → Framework-Lizenz
| Key |
Standard |
Beschreibung |
['A1'] |
'drive_bike' |
Leichtkraftrad → Motorrad-Lizenz |
['A'] |
'drive_bike' |
Motorrad |
['B'] |
'drive' |
PKW |
['BE'] |
'drive' |
PKW mit Anhänger |
['C'] |
'drive_truck' |
LKW |
['CE'] |
'drive_truck' |
LKW mit Anhänger |
['D'] |
'drive_truck' |
Bus |
['BOOT'] |
'boat' |
Boot |
['FLUG'] |
'pilot' |
Flugzeug |
['WS'] |
'weapon' |
Waffenschein |
Mehrere Panel-Klassen dürfen auf dieselbe Framework-Lizenz zeigen (z. B. B und BE → drive). Beim Entzug entfernt die Bridge die Framework-Lizenz nur, wenn keine andere aktive Panel-Klasse dasselbe Mapping benötigt.
Was wo konfiguriert wird — wichtig
- Lizenzarten (Katalog), Prüfungsinhalte, Preise/Gebühren werden im Web-Panel gepflegt, nicht in
config.lua. In der Ressource legt man nur fest, wie diese ins Spiel wirken:
- Lizenzen: über
Config.LicenseMapping (welche Panel-Klasse welche Framework-Lizenz setzt) und Config.SyncFramework.
- Prüfungen: über
Config.TheoryCommand und Config.TheoryRadius (Start & Einladungsradius der Theorieprüfung); Bögen/Bestehensgrenzen/praktische Prüfung liegen im Panel.
- Preise/Gebühren: rein im Panel; die Ressource zeigt nur die vom Panel gelieferten
openFees (/lizenzen) an.
Exports
Alle vier Exports sind serverseitig (in server/main.lua). Aufruf aus Server-Scripts über exports.licenseos:…. Als identifier ist der Framework-Identifier zu übergeben: ESX identifier, QBCore/Qbox citizenid, ox_core stateId, Standalone license:….
- Seite: Server
- Signatur:
exports.licenseos:GetCommunityId()
- Parameter: keine
- Rückgabe:
number | nil — die Community-ID nach erfolgreichem Handshake, sonst nil (noch nicht verbunden).
- Beschreibung: Liefert die vom Panel vergebene Community-ID. Nützlich für Support-Anfragen und zur Prüfung, ob die Bridge verbunden ist.
local id = exports.licenseos:GetCommunityId()
print(id and ('Verbunden, Community ' .. id) or 'Bridge noch nicht verbunden')
2. GetLicenses
- Seite: Server
- Signatur:
exports.licenseos:GetLicenses(identifier, cb)
- Parameter:
identifier (string), cb (function) — wird mit dem Ergebnis aufgerufen.
- Rückgabe (via Callback): Array aktiver Lizenzen; jeder Eintrag:
{ class, license_no, status, points, expires_at }.
- Beschreibung: Holt die aktiven Lizenzen eines Spielers. Reihenfolge der Quellen: lokale Tabelle (falls Cache aktiv & bereit) → RAM-Cache (<
CacheTTL) → Live-API (/bridge/licenses.php). Kein Callback-Typ = No-Op.
exports.licenseos:GetLicenses(identifier, function(licenses)
for _, l in ipairs(licenses) do
print(l.class, l.license_no, l.points, l.expires_at)
end
end)
3. HasLicense
- Seite: Server
- Signatur:
exports.licenseos:HasLicense(identifier, class, cb)
- Parameter:
identifier (string), class (string, z. B. 'B'), cb (function(has)).
- Rückgabe (via Callback):
boolean — ob eine aktive Lizenz der Klasse existiert.
- Beschreibung: Async-Check (empfohlen für normale Wege). Nutzt intern
GetLicenses und damit denselben Cache.
-- Beispiel: Fahrzeug nur bei gültiger PKW-Lizenz freigeben
exports.licenseos:HasLicense(identifier, 'B', function(has)
if not has then
-- z. B. Motor sperren, Strafe, Hinweis ...
TriggerClientEvent('ox_lib:notify', src, { description = 'Kein Führerschein Klasse B!' })
end
end)
4. HasLicenseCached
- Seite: Server
- Signatur:
local has = exports.licenseos:HasLicenseCached(identifier, class)
- Parameter:
identifier (string), class (string).
- Rückgabe:
boolean — synchron, nur aus dem RAM-Cache. false, wenn für den Spieler noch kein HasLicense/GetLicenses lief (Cache leer).
- Beschreibung: Kein HTTP, für Hot-Paths (z. B. jeden Frame / beim Einsteigen). Voraussetzung: der Spieler wurde vorher einmal geladen.
-- Hot-Path: vorher einmal HasLicense/GetLicenses aufrufen, um den Cache zu füllen
if not exports.licenseos:HasLicenseCached(identifier, 'C') then
-- LKW-Lizenz fehlt (nach Cache-Stand)
end
Lizenz geben/entziehen: dafür existiert kein Export. Das geschieht im Panel und wird per licenseUpdate-Push an den Server übernommen — koppelbar über das Event licenseos:licenseUpdate (siehe unten).
Events & Commands
Events
| Event |
Seite / Typ |
Signatur |
Beschreibung |
licenseos:licenseUpdate |
Server / AddEventHandler |
(identifier, class, status, data) |
Feuert bei jeder vom Panel gepushten Lizenz-Änderung. status: 'aktiv' / 'gesperrt' / 'entzogen' / 'abgelaufen'. Zum Koppeln eigener Systeme. |
licenseos:requestOpen |
Server / RegisterNetEvent |
() |
Vom Client ausgelöst; prüft serverseitig HasAccess und öffnet bei Erfolg das Panel. Intern — nicht manuell missbrauchen. |
licenseos:openPanel |
Client / RegisterNetEvent |
(url) |
Öffnet das NUI-iframe mit der übergebenen URL (auch für Theorie-Einladungen). |
licenseos:denied |
Client / RegisterNetEvent |
(reason) |
Wird bei fehlender Berechtigung gefeuert (Print im Client). |
-- Eigene Systeme an Lizenz-Änderungen koppeln:
AddEventHandler('licenseos:licenseUpdate', function(identifier, class, status, data)
if status == 'entzogen' and class == 'B' then
-- z. B. Fahrzeugschlüssel sperren
end
end)
NUI-Callbacks (Client, intern): close, saveLogin, getLogin, clearLogin — verwalten den lokalen Login-Cache (KVP) und das Schließen.
Commands
| Befehl (Default) |
Config-Key |
Wer |
Nutzung / Wirkung |
/licenseos |
Config.Command |
Jobs aus AllowedJobs oder Ace licenseos.open |
Öffnet das Panel ingame (Berechtigung serverseitig). Auch per Keybind F7. |
/fahrstunde |
Config.LessonCommand |
Ace licenseos.instructor |
/fahrstunde <spieler-id> <minuten> [notiz] — trägt eine Fahrstunde ans Panel ein; Ziel-Spieler muss online sein. |
/theorie |
Config.TheoryCommand |
Ace licenseos.instructor |
/theorie <klasse> — startet eine Gruppen-Theorieprüfung; alle Spieler im Umkreis (TheoryRadius) bekommen den Bogen als Overlay; Fahrlehrer erhält Prüfungs-Code. |
/lizenzen |
Config.CitizenCommand |
alle Spieler |
Zeigt eigene Lizenzen, Punkte, Gültigkeit und offene Gebühren im Chat (5 s Cooldown). |
Hinweise / Troubleshooting
- „Kein API-Key in config.lua hinterlegt — Bridge inaktiv." →
Config.ApiKey ist leer oder noch der Platzhalter los_HIER_DEN_KEY_EINFUEGEN. Echten Key aus Panel → Einstellungen → FiveM-Bridge eintragen.
- HTTP 401 beim Handshake → falscher/abgelaufener API-Key. Panel-Key prüfen bzw. neu erzeugen.
- Panel-Pushes kommen nicht an (Lizenzänderungen erscheinen ingame nicht sofort) → der Spielserver-Port (
netPort, Standard 30120) ist vom Webserver aus nicht erreichbar. Firewall/Portfreigabe prüfen. Exports/Befehle (Spiel → Panel) laufen davon unabhängig.
- Lokaler Cache bleibt aus („oxmysql nicht gestartet") → oxmysql wird nicht vor
licenseos gestartet. ensure oxmysql vor ensure licenseos setzen. Ohne oxmysql läuft die Ressource mit RAM-Cache + Live-API weiter, aber ohne Offline-Suche.
- Falsches Framework erkannt →
Config.Framework explizit setzen. Erzwungenes Framework wartet bis zu 30 s auf die Ressource und weicht nie still auf ein anderes aus (fällt sonst auf Standalone). Bei 'auto' wird qbx_core vor qb-core geprüft (Qbox liefert eine qb-core-Bridge mit).
HasLicenseCached liefert false, obwohl Lizenz vorhanden → RAM-Cache für den Spieler noch leer. Vorher einmal HasLicense/GetLicenses aufrufen (oder lokalen Cache aktiv lassen).
- Panel öffnet nicht / „keine Berechtigung" → weder passender Job-Grade in
AllowedJobs noch Ace licenseos.open. Berechtigung wird ausschließlich serverseitig geprüft.
- Fahrlehrer-Befehle reagieren nicht → Ace
licenseos.instructor fehlt (add_ace + add_principal in server.cfg).
- Datenschutz: Kein automatischer Bürger-Sync beim Join — gewollt. Spieler werden erst bei einem echten Vorgang im Panel gespeichert.
Ancomox Garage — Entwickler-Dokumentation
Überblick
ancomox_garage (Handelsname „Zenith Glass Edition Garage") ist ein premium Multi-Framework-Garagensystem für FiveM. Es verwaltet das Ein- und Ausparken von Fahrzeugen über ein modernes Glass-NUI mit 3D-Fahrzeugvorschau und deckt weit mehr als eine klassische Garage ab.
Kernfunktionen
- Öffentliche & Job-Garagen (statisch aus Config oder dynamisch per In-Game-Creator in der DB)
- Fahrzeug einparken/ausparken, umbenennen, Schlüssel teilen (2 Stufen: Drive / Drive & Park)
- Impound (job-basiert, Gebühr, Sperrzeit) + Verwahrstelle/Impound-Lot (Auslösung) + Schrottplatz für „verlorene" Fahrzeuge
- Private, kaufbare Garagen in 3 Vanilla-GTA-Interiors (2/6/10 Stellplätze, kein MLO nötig, eigene Routing-Bucket-Instanz)
- Vehicle Transfer (Besitzübertragung zwischen Spielern, optional gegen Bezahlung)
- Valet (eingelagertes Fahrzeug liefern lassen), Admin-Fahrzeug-Panel, Housing-Bridge
- Fahrzeug-Lock (Keymapping), GPS-Tracking, Fuel-Bridges, optionale ox_target/qb-target-Integration
- Discord-Webhook-Logs für alle relevanten Aktionen
Autor: Ancomox | Emergency OS
Voraussetzungen
| Anforderung |
Wert / Beschreibung |
| fx_version |
cerulean |
| game |
gta5 |
| Lua |
lua54 'yes' (Lua 5.4 erforderlich) |
| Pflicht-Dependency |
oxmysql (dependency 'oxmysql' in fxmanifest) |
| Framework |
ESX (es_extended), QBCore (qb-core), QBox (qbx_core) oder Custom/Standalone — Auswahl über Config.Framework (Default "auto" erkennt die drei Standard-Frameworks selbst) |
| Datenbank |
MySQL/MariaDB via oxmysql (Tabellen werden automatisch angelegt/migriert, siehe unten) |
| OneSync |
Nur nötig, wenn Config.ServerSpawn.Enabled = true (server-autoritatives Spawnen, Anti-Dupe) |
| Optional |
ox_lib (empfohlen für Menüs/TextUI), ox_target/qb-target (Targeting), Fuel-Script (ox_fuel/legacyfuel/ps-fuel/rcore_fuel), Vehiclekeys-Script |
Installation
- Ordner
ancomox_garage nach resources/ (bzw. [ANCOMOX]/) kopieren.
- In der
server.cfg ensure oxmysql VOR ensure ancomox_garage setzen (Reihenfolge ist wichtig).
- In
config/config.lua Config.Framework auf "auto" lassen (erkennt ESX/QBCore/QBox) oder fest auf "esx", "qb" bzw. "custom" setzen.
- Server starten — es muss KEINE SQL-Datei importiert werden, das Script legt seine Tabellen beim ersten Start selbst an und ergänzt fehlende Fahrzeug-Spalten automatisch.
Empfohlene server.cfg-Reihenfolge:
ensure oxmysql
# ggf. Framework, ox_lib, target, fuel ...
ensure ancomox_garage
Datenbank
Es ist kein manueller SQL-Import nötig. Bei MySQL.ready werden folgende Tabellen per CREATE TABLE IF NOT EXISTS angelegt und bestehende Fahrzeugtabellen migriert (EnsureColumnExists):
| Tabelle |
Zweck |
Wichtige Spalten |
ancomox_garages |
Creator-/DB-Garagen (nur bei UseDatabaseGarages = true) |
id, name, type (public/job), job_name (JSON-Array), loc_x/y/z/w, spawn_x/y/z/w, npc_hash, blip_sprite, blip_color, vehicle_types (CSV), vehicle_classes (CSV), society (0/1), min_grade |
ancomox_garage_keys |
Geteilte Fahrzeugschlüssel |
plate, receiver_identifier, access_level (UNIQUE plate,receiver) |
ancomox_settings |
Globale Runtime-Settings aus dem Creator (Key/Value) |
skey, sval (DB-Wert hat Vorrang vor Config) |
ancomox_private_garages |
Besitz privater Garagen |
id, garage_key (UNIQUE), owner, price, created, interior_override |
ancomox_private_garage_vehicles |
Zuordnung Kennzeichen → private Garage |
plate (PK), private_garage_id |
ancomox_private_garage_members |
Mitbenutzer privater Garagen |
private_garage_id, member, member_name, access_level (1–3) |
ancomox_private_garage_presence |
Merkt sich, wer in welcher Garage ausgeloggt ist (Logout-in-Garage-Fix v2.11) |
identifier (PK), private_garage_id, garage_key, ent_x/y/z/w |
ancomox_private_locations |
In-Game erstellte private Standorte (Creator-Tab) |
id, label, interior, price, ent_x/y/z/w, spawn_x/y/z/w |
Automatisch ergänzte Spalten in deiner Fahrzeugtabelle (Config.CustomFramework.VehiclesTable, Default owned_vehicles): fuel (INT 100), name (VARCHAR 100), pound_time (INT 0), impound_reason (VARCHAR 120), impound_by (VARCHAR 64), last_x/last_y/last_z (FLOAT), garage_id (VARCHAR 64) sowie bei DBStyle = "esx" zusätzlich pound (TINYINT).
Konfiguration
config/config.lua — Core-Einstellungen
| Key |
Standard |
Beschreibung |
Config.Framework |
"auto" |
Framework: "esx", "qb", "custom" oder "auto" (erkennt es_extended, qb-core, qbx_core selbst) |
Config.Debug |
false |
Debug-Prints in der Server-Konsole |
Config.UseDatabaseGarages |
true |
true = Garagen aus DB + In-Game-Creator; false = ausschließlich Config.Garages (statisch) |
Config.AdminGroups |
{'admin','superadmin','god'} |
Gruppen mit Zugriff auf den Garage-Creator und das Admin-Panel |
Config.AllowSettingsInCreator |
true |
true = Settings-Tab im /creategarage sichtbar (Werte in ancomox_settings); false = nur config.lua als Quelle |
Config.RetrievalMode |
"anywhere" |
Abholmodus: "anywhere" (jede passende Garage), "origin" (nur Ursprungsgarage), "transfer" (wie origin + „Bring here" gegen Gebühr) |
Config.RetrievalTransferFee |
500 |
Gebühr für „Bring here" im Modus "transfer" |
Config.OnRestart |
"return" |
Restart-Recovery: "return" (ausgeparkte Autos kommen zurück, dupe-sicher), "recover_free" (kostenlos im Menü zurückholbar), "keep" (nur Schrottplatz gegen Gebühr) |
Config.SaveLastLocation |
true |
Letzte bekannte Fahrzeugposition speichern (für „Track"-Wegpunkt) |
Config.SaveLastLocationInterval |
20000 |
ms-Intervall für das Positions-Update des gefahrenen Fahrzeugs |
Config.MenuSystem |
"ox_lib" |
Kontextmenü-System für Impound & Schrottplatz: "ox_lib", "esx" oder "qb" |
Config.Locale |
'en' |
Sprache ('en'/'de' oder eigene, siehe locales.lua) |
Config.Locales |
(abgeleitet) |
Wird automatisch aus Locales[Config.Locale] gesetzt (nicht manuell ändern) |
Config.CustomFramework (nur bei Framework "custom" / wenn auto nichts findet)
| Key |
Standard |
Beschreibung |
DBStyle |
"esx" |
"esx" = getrennte Spalten stored + pound (0/1); "qb" = eine state-Spalte (0=draußen, 1=Garage, 2=Impound) |
VehiclesTable |
"owned_vehicles" |
Name deiner Fahrzeugtabelle |
Columns.Owner |
"owner" |
Spalte des Besitzer-Identifiers |
Columns.Props |
"vehicle" |
Spalte mit dem JSON der Fahrzeug-Properties |
Columns.Plate |
"plate" |
Kennzeichen-Spalte |
Columns.Fuel |
"fuel" |
Tank-Spalte |
Columns.Name |
"name" |
Custom-Name-Spalte |
Columns.Stored |
"stored" |
Bei DBStyle "qb": Name der State-Spalte (z. B. "state") |
Columns.Pound |
"pound" |
Nur DBStyle "esx" |
Columns.PoundTime |
"pound_time" |
Zeit-Spalte der Beschlagnahmung |
Jobs |
{police, ambulance, mechanic} |
Jobliste für den Garage-Creator ({value=, label=}), falls das Framework keine liefert |
Custom-Anbindung: Pflichtfunktion Custom_GetPlayer in server/open.lua implementieren (muss identifier, name, job, group, getMoney, removeMoney, addMoney, jobGrade, source zurückgeben). addMoney ist Pflicht für Transfer-/Garagenverkäufe. Client-seitig bei Jobwechsel TriggerEvent('ancomox_garage:setJob', jobName) feuern.
Config.PrivateGarages (Premium — kaufbare private Garagen)
| Key |
Standard |
Beschreibung |
Enabled |
true |
Feature ein/aus |
MaxOwnedPerPlayer |
2 |
Max. private Garagen pro Spieler |
SellPercent |
50 |
Rückerstattung in % beim Verkauf (/sellgarage) |
RoutingBucketBase |
5000 |
Instanz-ID = Base + DB-ID der Garage (bei Bucket-Kollision erhöhen) |
Blips.ForSale |
{sprite=374,color=2,scale=0.7,name="Garage For Sale"} |
Blip für kaufbare Garagen |
Blips.Owned |
{sprite=357,color=5,scale=0.7,name="My Garage"} |
Blip für eigene Garagen |
Interiors |
{small, medium, large} |
Die 3 Vanilla-Interiors (siehe unten) |
Locations |
{legion_small, grove_medium, sandy_large} |
Kaufbare Standorte (siehe unten) |
Interiors-Eintrag (je small/medium/large): label, capacity (2/6/10), playerEnter (vector4), exitPoint (vector3), menuPoint (vector3), slots (Liste von vector4-Stellplätzen). Z-Höhe der drei Vanilla-Garagen ist bewusst -99.60 (knapp über dem Interior-Boden).
Locations-Eintrag (Table-Key = eindeutige DB-ID):
| Feld |
Beispiel |
Beschreibung |
label |
"Legion Square Garage" |
Anzeigename |
interior |
"small" |
Verweist auf Interiors (small/medium/large) |
price |
65000 |
Kaufpreis |
entrance |
vector4(193.50,-831.60,30.73,340.0) |
Kauf-/Betreten-Punkt (im Auto: [E] parkt ein und betritt) |
vehicleSpawn |
vector4(200.80,-838.60,30.62,340.0) |
Außen-Punkt: hier spawnen Autos & alternativer [E]-Einlagerpunkt |
Default-Standorte: legion_small ($65.000), grove_medium ($145.000), sandy_large ($295.000).
Config.PrivateGarageUpgrades
| Key |
Standard |
Beschreibung |
Enabled |
true |
Upgrade privater Garagen per /upgradegarage am Eingang |
Order |
{ 'small', 'medium', 'large' } |
Upgrade-Reihenfolge |
Prices |
{ medium = 120000, large = 260000 } |
Preis der Ziel-Stufe (Fahrzeuge bleiben erhalten) |
Config.VehicleTransfer (Premium — Besitzübertragung)
| Key |
Standard |
Beschreibung |
Enabled |
true |
Feature ein/aus |
AllowPrice |
true |
Verkauf mit Preis erlauben (Geld → Verkäufer) |
MaxPrice |
10000000 |
Maximaler Verkaufspreis |
RequireNearby |
true |
Empfänger muss in der Nähe sein |
MaxDistance |
10.0 |
Max. Distanz (Meter), wenn RequireNearby |
RequestTimeout |
60 |
Sekunden bis eine Anfrage verfällt |
Config.Valet (Premium — Fahrzeug liefern lassen)
| Key |
Standard |
Beschreibung |
Enabled |
true |
Feature ein/aus |
Command |
"valet" |
Befehl (/valet [kennzeichen]) |
Fee |
750 |
Gebühr pro Lieferung |
Cooldown |
300 |
Sekunden Cooldown pro Spieler |
NodeSearch |
60.0 |
Max. Suchradius für einen Straßen-Node |
Config.Keys (Premium — Fahrzeugschlüssel-Bridge)
| Key |
Standard |
Beschreibung |
System |
"none" |
"none", "qb", "qbx", "wasabi_carlock", "mk_vehiclekeys", "jaksam" oder "custom" (eigene Integration in client/open.lua → Custom_GiveVehicleKeys) |
GiveOnSpawn |
true |
Schlüssel beim Ausparken automatisch geben |
Config.AdminPanel
| Key |
Standard |
Beschreibung |
Enabled |
true |
In-Game-Fahrzeugverwaltung für Admins |
Command |
"adminvehicles" |
Panel-Befehl |
SpawnCarCmd |
"admincar" |
Temporäres (nicht gespeichertes) Fahrzeug spawnen |
SearchLimit |
60 |
Max. Treffer pro Suche |
Config.HousingBridge
| Key |
Standard |
Beschreibung |
Enabled |
true |
Erlaubt Housing-Scripts, per Export/Event eine Garage an eine Immobilie zu binden (garage_id = "house:<hausId>") |
Config.PreviewCustomization
| Key |
Standard |
Beschreibung |
Livery |
true |
Livery-Umschaltung in der 3D-Vorschau |
Extras |
true |
Extra-Toggles (1–14) in der Vorschau |
Config.Target & Config.TextUI
| Key |
Standard |
Beschreibung |
Target.Enabled |
false |
true = ox_target/qb-target-Zonen statt Marker/[E]-Loops |
Target.System |
"ox_target" |
"ox_target" oder "qb-target" |
Target.Distance |
2.5 |
Interaktionsreichweite (Meter) |
Target.Debug |
false |
Zonen sichtbar rendern (nur zum Einrichten) |
TextUI.Enabled |
false |
true = TextUI statt DrawText3D (wenn Target aus) |
TextUI.System |
"ox_lib" |
"ox_lib" oder "qb-core" |
Config.ServerSpawn (Anti-Dupe, opt-in)
| Key |
Standard |
Beschreibung |
Enabled |
false |
true = Server spawnt ausgeparkte Fahrzeuge autoritativ (CreateVehicleServerSetter). Benötigt OneSync. Vor Produktivbetrieb testen! |
MaxPerPlayer |
25 |
Obergrenze gleichzeitig server-gespawnter Fahrzeuge pro Spieler (Anti-Grief) |
| Key |
Standard |
Beschreibung |
IdleSleep |
1000 |
ms-Schlafzeit der Interaktions-Loops im Leerlauf (höher = weniger CPU, trägere Reaktion) |
Fahrzeug-Lock & System-Settings
| Key |
Standard |
Beschreibung |
Config.EnableVehicleLocking |
true |
Fahrzeug-Lock-System aktiv |
Config.VehicleLockDefaultKey |
'u' |
Standardtaste (per RegisterKeyMapping vom Spieler änderbar) |
Config.MaxLockDistance |
10.0 |
Max. Distanz zum Ab-/Aufschließen |
Config.LockFeedback |
true |
Lichter blinken beim Ab-/Aufschließen |
Config.LockAnimAndSound |
true |
Keyfob-Animation und -Sound |
Config.UseVehicleHorn |
false |
Hupe statt Frontend-Sound |
UI, Kamera, Distanzen, Fuel
| Key |
Standard |
Beschreibung |
Config.ServerName |
"Ancomox Roleplay" |
Anzeigename im Garagen-UI |
Config.LogoPath |
"images/logo.png" |
Pfad zum Server-Logo (in html/images/) |
Config.UseShowroom |
false |
Casino-Podium-Hintergrund im Menü (private Garagen nutzen ihn immer) |
Config.CameraPanEnabled |
true |
Kamera langsam ums Fahrzeug rotieren |
Config.CameraPanSpeed |
0.1 |
Rotationsgeschwindigkeit |
Config.MaxDistance |
5.0 |
Distanz zum Öffnen des NPC-Menüs |
Config.MaxDistancevehicleparkmarker |
8.0 |
Distanz zum Spawnpunkt zum Einparken |
Config.NPCSpawnDistance |
30.0 |
Distanz, ab der der NPC spawnt |
Config.AllowOthersToParkVehicles |
false |
Erlaubt Einparken fremder Fahrzeuge |
Config.EnableGPS |
true |
GPS-Tracking für ausgeparkte Fahrzeuge |
Config.Fuel.system |
"ox_fuel" |
Fuel-Bridge: "ox_fuel", "legacyfuel", "ps-fuel", "rcore_fuel" |
Config.NotifyClient / Config.NotifyServer (Funktionen)
Standard-Benachrichtigungen. NotifyClient(msg, type) und NotifyServer(source, msg, type) verzweigen intern nach ESX (ShowNotification / esx:showNotification) bzw. QB (Functions.Notify / QBCore:Notify). Für Custom-Frameworks hier anpassen.
Impound, Schrottplatz & Verwahrstelle
Config.Impound (Beschlagnahmungs-/Impound-Interaktionspunkt):
| Key |
Standard |
Beschreibung |
location |
vector4(425.81,-1014.28,28.99,89.54) |
Impound-Punkt |
fee |
500 |
Auslösegebühr |
jobCanAlwaysRelease |
"police" |
Job, der jederzeit freigeben darf |
spawnNPC |
true |
NPC spawnen |
npcHash |
"s_m_y_cop_01" |
NPC-Modell |
Config.ScrapYard (Schrottplatz für „verlorene" Fahrzeuge):
| Key |
Standard |
Beschreibung |
location |
vector4(101.34,-1071.60,29.23,338.30) |
Schrottplatz-Punkt |
fee |
1000 |
Gebühr zum Wiederherstellen |
spawnNPC |
true |
NPC spawnen |
npcHash |
"s_m_y_garbage" |
NPC-Modell |
Config.ImpoundLot (physische Verwahrstelle/Depot zum Auslösen):
| Key |
Standard |
Beschreibung |
Enabled |
true |
Verwahrstelle aktiv |
location |
vector4(409.79,-1622.92,29.29,231.0) |
Büro (Davis Impound) |
spawn |
vector4(407.44,-1645.31,29.31,142.0) |
Fahrzeug-Ausgabepunkt |
releaseFee |
800 |
Auslösegebühr nach Ablauf der Sperrzeit |
blip |
{sprite=68,color=1,scale=0.75,name="Impound Lot"} |
Blip der Verwahrstelle |
Config.Garages — statische Garagen (nur bei UseDatabaseGarages = false)
Enthält rund zwei Dutzend vordefinierte Standard-Garagen (öffentliche + Job-Garagen für police, ambulance, mechanic, merryweather, admin sowie Boots-/Air-Garagen). Struktur eines Garagen-Eintrags:
| Feld |
Typ |
Pflicht |
Beschreibung |
location |
vector4 |
ja |
NPC-/Interaktionspunkt (Garage öffnen) |
spawnPos |
vector4 |
ja |
Punkt, an dem ausgeparkte Fahrzeuge erscheinen / eingeparkt wird |
job |
nil | string | table |
nein |
Job-Bindung: nil = für alle, 'police' oder {'admin'} |
spawnNPC |
boolean |
nein |
NPC an der Garage spawnen |
npcHash |
string |
nein |
NPC-Modell (Default 'a_m_y_business_02') |
blip |
table |
nein |
{ sprite, color, scale, name } |
markerSettings |
table |
nein |
{ id, size = {x,y,z}, color = {r,g,b,a} } (optional; DB-Garagen nutzen Default-Marker id 36, Farbe hellblau) |
vehicleTypes |
table |
nein |
Erlaubte Typen, z. B. {"car","air","boat","bike"} |
vehicleClasses |
table |
nein |
Erlaubte GTA-Fahrzeugklassen, z. B. {0,1,2,...,20} |
Neue Garage anlegen — Variante A (statisch, in Config.Garages)
Neuen Eintrag in die Config.Garages-Liste ergänzen. Beispiel öffentliche PKW-Garage:
{
location = vector4(-1985.00, 3065.13, 32.81, 102.20), -- Garage öffnen
spawnPos = vector4(-1993.00, 3067.41, 32.81, 57.95), -- Fahrzeug-Ausgabe
job = nil, -- nil = für alle
spawnNPC = true,
npcHash = 'a_m_y_business_02',
blip = { sprite = 357, color = 3, scale = 0.75, name = "Garage" },
markerSettings = { id = 25, size = {x = 5.0, y = 5.0, z = 1.0}, color = {r = 0, g = 255, b = 0, a = 100} },
vehicleTypes = {"car"},
vehicleClasses = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 19, 20}
}
Beispiel Job-Garage (Polizei):
{
location = vector4(451.94, -973.12, 25.70, 176.38),
spawnPos = vector4(450.43, -977.88, 25.70, 102.37),
job = 'police',
spawnNPC = true,
npcHash = 'a_m_y_business_02',
blip = { sprite = 60, color = 29, scale = 0.8, name = "Police Garage" },
vehicleClasses = {18, 20, 0, 1, 2}
}
Neue Garage anlegen — Variante B (dynamisch, empfohlen bei UseDatabaseGarages = true)
Im Spiel als Admin /creategarage → Platzierung per Raycast, Job-Bindung, Fahrzeugtyp-/Klassenfilter, Blip-Einstellungen, optional Company fleet (society) + Mindest-Job-Rang. Speicherung in ancomox_garages, sofortiger Sync an alle Clients. Private Standorte im selben Creator unter Tab „Private Garages" (Name, Interior small/medium/large, Preis, Entrance + Vehicle Exit Point) → Speicherung in ancomox_private_locations.
config/locales.lua — Sprachdatei
Locales enthält vollständige en- und de-Blöcke. Sprache über Config.Locale wählen; eigene Sprache = Block kopieren, Kürzel ändern, übersetzen. Strings mit %s/%d sind Format-Platzhalter (nicht entfernen). Übersicht der Schlüsselgruppen:
| Gruppe (Präfix) |
Zweck |
Beispiel-Keys |
| Core |
Prompts & Grundmeldungen |
store_vehicle, garage_prompt, impound_prompt, scrapyard_prompt, vehicle_stored, not_your_vehicle, vehicle_locked, gps_set |
target_* |
Saubere Labels für ox_target/qb-target (ohne [E]) |
target_garage, target_store, target_impound, target_scrapyard |
retr_* |
Abhol-Modi origin/transfer |
retr_other_garage, retr_brought_here, retr_move_prompt |
settings_saved, mods_saved |
Creator-Settings / Vorschau-Anpassung |
— |
valet_* |
Valet-Service |
valet_disabled, valet_cooldown, valet_delivered, valet_arrived |
adm_* |
Admin-Fahrzeug-Panel |
adm_returned, adm_given, adm_deleted, adm_plate_changed, adm_car_spawned |
pg_* |
Private Garagen (Kauf/Verkauf/Betreten/Slots/Session/Mitbenutzer/Upgrade) |
pg_buy_prompt, pg_purchased, pg_sold, pg_limit_reached, pg_garage_full, pg_member_added, pg_upgraded |
soc_* |
Firmen-Fuhrpark (society) |
soc_added, soc_removed, soc_wrong_job, soc_grade_low |
imp_* |
Verwahrstelle/Impound-Lot |
imp_lot_prompt, imp_release_paid, imp_list_header, imp_list_entry, imp_list_empty |
tr_* |
Vehicle Transfer |
tr_sent, tr_received_free, tr_received_price, tr_done_sender, tr_done_receiver, tr_no_money, tr_failed |
dbg_no_response |
Diagnose (Server antwortet nicht) |
— |
(Beide Sprachblöcke enthalten jeweils dieselbe Schlüsselmenge; Standardwerte sind die englischen bzw. deutschen Klartexte oben in config.lua-Nähe zitiert.)
config_logs.lua — Discord-Webhook-Konfiguration
Config_Logs = {
Webhooks = {
Store = "", -- Fahrzeug eingeparkt
ParkOut = "", -- Fahrzeug ausgeparkt / verschoben / Valet
Impound = "", -- Beschlagnahmung / Auslösung
Scrap = "", -- Schrottplatz / kostenlose Wiederherstellung / Löschung
Admin = "", -- Admin- & Creator-Aktionen
Transfer = "", -- Fahrzeug-Übertragungen zwischen Spielern
Property = "" -- Kauf/Verkauf privater Garagen, Mitglieder, Upgrades
},
LogSettings = {
Store = true, ParkOut = true, Impound = true, Scrap = true,
Admin = true, Transfer = true, Property = true
}
}
| Key |
Standard |
Beschreibung |
Webhooks.Store |
"" |
Discord-Webhook-URL für Einpark-Logs |
Webhooks.ParkOut |
"" |
Ausparken / „Bring here" / Valet |
Webhooks.Impound |
"" |
Beschlagnahmung & Auslösung |
Webhooks.Scrap |
"" |
Schrottplatz, kostenlose Wiederherstellung, Löschung |
Webhooks.Admin |
"" |
Admin-/Creator-/Settings-Aktionen |
Webhooks.Transfer |
"" |
Besitzübertragungen |
Webhooks.Property |
"" |
Private-Garagen-Kauf/-Verkauf, Mitglieder, Upgrades |
LogSettings.<Typ> |
true (alle 7) |
Schaltet den jeweiligen Log-Typ global an/aus |
Ein Log wird nur gesendet, wenn LogSettings[typ] = true und die passende Webhook-URL gesetzt ist. Ein leerer String oder eine Demo-Webhook (interner Guard api/webhooks/119887…) unterdrückt den Versand.
Exports
Beide Exports liegen client-seitig in client/client.lua und dienen der Housing-Bridge. Sie sind nur aktiv, wenn Config.HousingBridge.Enabled = true. Beide geben keinen Wert zurück (kein Rückgabewert / nil).
1. OpenHouseGarage(houseId, data)
- Seite: Client
- Definition:
exports('OpenHouseGarage', function(houseId, data) ... end)
- Signatur:
houseId (string|number) — eindeutige Haus-ID (wird zu string konvertiert; intern retrievalKey = "house:<hausId>")
data (table, optional):
label (string) — Anzeigename der Garage (Default "House Garage")
spawn (vector4/{x,y,z,w}) — wo ausgeparkte Fahrzeuge erscheinen (Default: aktuelle Ped-Position)
vehicleTypes (table, optional) — Typ-Filter
vehicleClasses (table, optional) — Klassen-Filter
- Rückgabe: keine
- Beschreibung: Öffnet das volle Garagen-Menü für eine an ein Haus gebundene Garage. Fahrzeuge, die hier eingelagert werden, erscheinen nur in dieser Haus-Garage. Auch als Event
ancomox_garage:openHouseGarage verfügbar.
- Beispiel:
exports.ancomox_garage:OpenHouseGarage(houseId, {
label = "Mein Haus",
spawn = vector4(x, y, z, w), -- wo Autos rauskommen
vehicleTypes = {"car"},
vehicleClasses = nil
})
-- alternativ per Event:
TriggerEvent('ancomox_garage:openHouseGarage', houseId, data)
2. StoreVehicleAtHouse(houseId, vehicle)
- Seite: Client
- Definition:
exports('StoreVehicleAtHouse', function(houseId, vehicle) ... end)
- Signatur:
houseId (string|number) — Haus-ID (Zuordnung über garage_id = "house:<hausId>")
vehicle (entity handle, optional) — zu lagerndes Fahrzeug. Wird nichts/0 übergeben, nutzt der Export das Fahrzeug, in dem der Spieler sitzt, sonst das nächste Fahrzeug im Umkreis < 5.0 m.
- Rückgabe: keine
- Beschreibung: Lagert das angegebene (oder automatisch ermittelte) Fahrzeug in die Haus-Garage ein (ruft intern
AttemptToStoreVehicle). Ist kein Fahrzeug in Reichweite, erscheint die Notify no_vehicle_nearby.
- Beispiel:
-- aktuelles/nächstes Fahrzeug einlagern:
exports.ancomox_garage:StoreVehicleAtHouse(houseId)
-- bestimmtes Fahrzeug einlagern:
exports.ancomox_garage:StoreVehicleAtHouse(houseId, vehicleEntity)
Events & Commands
Befehle (RegisterCommand)
| Befehl |
Wer |
Zweck |
/creategarage |
Admin (Config.AdminGroups) |
Garage-Creator (öffentliche + private Standorte, Society, Settings-Tab) |
/impound <stunden> [grund...] |
Impound-Job |
Fahrzeug beschlagnahmen (1–48 h); speichert Grund + Beamten |
/impoundlist |
Impound-Job |
Übersicht beschlagnahmter Fahrzeuge |
/sellgarage |
Besitzer, am Eingang |
Private Garage verkaufen (SellPercent % Rückerstattung; muss leer sein) |
/upgradegarage |
Besitzer, am Eingang |
Private Garage auf nächstes Interieur aufwerten |
/acceptvehicle |
Transfer-Empfänger |
Fahrzeug-Übertragung annehmen |
/declinevehicle |
Transfer-Empfänger |
Übertragung ablehnen |
/valet [kennzeichen] |
Alle |
Eingelagertes Fahrzeug liefern lassen (Name via Config.Valet.Command) |
/adminvehicles |
Admin |
Admin-Fahrzeug-Panel (Name via Config.AdminPanel.Command) |
/admincar <model> |
Admin |
Temporäres Fahrzeug spawnen (Name via Config.AdminPanel.SpawnCarCmd) |
/addjobvehicle <job> |
Admin |
Fahrzeug zum Firmen-Fuhrpack hinzufügen (im/neben Fahrzeug) |
/removejobvehicle |
Admin |
Firmenfahrzeug entfernen |
/togglegaragelock |
(intern/Admin) |
Garagen-Lock umschalten |
/garagedebug |
(Diagnose) |
Diagnose-Ausgabe, wenn der Garagen-Server nicht antwortet |
Taste U |
Alle |
Fahrzeug ab-/aufschließen (via Config.VehicleLockDefaultKey, Keymapping frei belegbar) |
Wichtige Events (Auswahl)
| Event |
Seite |
Zweck |
ancomox_garage:openHouseGarage (houseId, data) |
Client |
Haus-Garage öffnen (Pendant zum Export) |
ancomox_garage:setJob (jobName) |
Client |
Job-Update für Custom-Frameworks setzen |
ancomox_garage:leavePrivate |
Server |
Private Garage verlassen |
ancomox_garage:forceExitPrivate (entrance, msg) |
Client |
Spieler aus der Garage nach draußen bringen |
ancomox_garage:updateVehCoords |
Server |
Letzte Fahrzeugposition speichern |
ancomox_garage:saveCustomName |
Server |
Fahrzeug-Custom-Name speichern |
garage:syncGarages / garage:syncPrivateGarages |
Client |
Garagenlisten an Clients syncen |
garage:settingsUpdated / garage:notify / gps:trackVehicle |
Client |
Runtime-Settings-Sync, Benachrichtigung, GPS-Track |
garage:toggleLockServer / garage:toggleLockResult |
Server/Client |
Fahrzeug-Lock-Roundtrip |
garage:deleteImpoundedVehicle |
Client |
Beschlagnahmtes Fahrzeug löschen |
esx:setJob / QBCore:Client:OnJobUpdate / esx:playerLoaded / QBCore:Client:OnPlayerLoaded |
Client |
Framework-Hooks (Jobwechsel/Login) |
Server-Callbacks laufen über eine eigene Bridge (ancomox_garage:cbRequest / cbResponse, Custom_RegisterServerCallback), z. B. garage:getVehicles, garage:fetchDBGarages, garage:saveCreatorGarage, garage:getPrivateGarageStates.
Discord-Logs
Zentrale Funktion: SendWebhookLog(type, title, message, color) (server/server.lua, Zeile ~8583). Sie prüft Config_Logs.LogSettings[type], holt Config_Logs.Webhooks[type], überspringt leere/Demo-Webhooks und postet dann per PerformHttpRequest ein Discord-Embed:
- Benutzername:
"Garage Logs"
- Embed:
title, description (Markdown mit Spielername, Identifier, Kennzeichen [PLATE], Beträgen), footer = "Garage System | <YYYY-MM-DD HH:MM:SS>", color = dezimaler Farbwert (z. B. grün 5763719 Store/Kauf, blau 3447003 ParkOut/Info, rot 15548997 Impound/Löschung, gelb 16776960 Update/Verkauf)
Welche Aktionen loggen in welchen Typ:
| Typ |
Geloggte Aktionen (Embed-Titel) |
| Store |
„Vehicle Stored" (Fahrzeug eingeparkt; inkl. Hinweis auf private Garage) |
| ParkOut |
„Vehicle Parked Out" (ausgeparkt), „Vehicle Moved" (Bring here, transfer-Modus), „Valet Delivery" |
| Impound |
„Vehicle Impounded" (mit Stunden + Grund), „Impound Released" (Auslösung) |
| Scrap |
„Vehicle Recovered" (kostenlose Wiederherstellung), „Vehicle Retrieved from Scrapyard" (gegen Gebühr), „Vehicle Permanently Deleted" |
| Admin |
„Settings Updated", „Garage Created/Updated/Deleted", „Society Vehicle", „Vehicle Returned/Given/Deleted", „Plate Changed", „Private Location Saved/Deleted" |
| Transfer |
„Vehicle Transferred" (Besitzübergabe zwischen Spielern) |
| Property |
„Private Garage Purchased/Sold", „Garage Member Added", „Garage Upgraded" |
Webhook-Setup:
1. In Discord: Servereinstellungen → Integrationen → Webhooks → Neuer Webhook, Kanal wählen, URL kopieren.
2. URL in config_logs.lua beim gewünschten Typ eintragen, z. B. Store = "https://discord.com/api/webhooks/…". Für mehrere Kategorien im selben Kanal dieselbe URL mehrfach eintragen.
3. Einzelne Kategorien über LogSettings.<Typ> = false deaktivieren (spart HTTP-Requests).
Hinweise / Troubleshooting
- Reihenfolge in server.cfg:
oxmysql muss vor ancomox_garage starten, sonst schlagen die MySQL.ready-Migrationen fehl.
- Kein SQL-Import nötig: Alle Tabellen und fehlende Fahrzeug-Spalten werden beim ersten Start automatisch angelegt/ergänzt.
- „Garage server did not respond" (
dbg_no_response): /garagedebug ausführen und die Ressource neu starten; meist ein DB-/Callback-Timing-Problem.
- Falscher
identifier: Bei Custom-Frameworks muss der von Custom_GetPlayer gelieferte identifier exakt zur Owner-Spalte (Config.CustomFramework.Columns.Owner) passen, sonst tauchen keine Fahrzeuge auf. addMoney ist Pflicht für Transfer-/Garagenverkauf, jobGrade für Fleet-Garagen.
DBStyle korrekt wählen: "esx" (getrennte stored/pound-Spalten) vs. "qb" (eine state-Spalte). Falsche Wahl → Fahrzeuge gelten als draußen/beschlagnahmt.
- Private Garagen — Routing Buckets:
RoutingBucketBase (Default 5000) darf nicht mit anderen Instanz-Systemen kollidieren; bei Bedarf erhöhen. Slot-Positionen bei „Auto klebt in der Wand" einfach als vector4 in Config.PrivateGarages.Interiors verschieben — Z wird per SetVehicleOnGroundProperly abgesichert (Z = -99.60 nicht unter -100.0 setzen, sonst fällt man durchs Interior).
- Logout in privater Garage: Wird über
ancomox_private_garage_presence abgefangen (v2.11-Fix) — Tabelle nicht manuell leeren, während Spieler online sind.
- ServerSpawn (Anti-Dupe):
Config.ServerSpawn.Enabled = true benötigt OneSync (Infinity) und ändert die Spawn-Architektur — vorher auf einem Testserver prüfen. Passt zu Config.OnRestart = "return".
- AdvancedParking:
fixDeleteVehicle.lua ist ein Kompatibilitäts-Shim; die Datei nicht in AdvancedParking selbst starten (sie bricht dort bewusst ab).
- Discord-Logs kommen nicht an: Prüfen, ob (a)
LogSettings[typ] = true, (b) die Webhook-URL gesetzt und gültig ist und (c) keine Demo-Webhook (…api/webhooks/119887…) verwendet wird — diese wird bewusst blockiert.
- Fahrzeugbilder: Eigene PNGs (Modellname, klein) nach
html/images/vehicles/ legen; fehlt ein Bild, lädt das UI das offizielle FiveM-Docs-Bild und danach default.png.
ancomox_vehicleshop — Entwickler-Dokumentation
Ancomox Premium Vehicle Shop — Fahrzeug-Shop/Autohaus-Script für FiveM mit NUI-Oberfläche, permanenten Vorschaufahrzeugen, Probefahrt, kinematischer Kamera, Job-beschränkten Shops und Discord-Logging.
Überblick
ancomox_vehicleshop stellt beliebig viele Autohäuser (Standard: 14 Shops) an frei konfigurierbaren Orten bereit. Der Spieler interagiert über einen Verkäufer-Ped (via ox_target/qb-target) oder über einen klassischen 3D-Marker und öffnet eine NUI, in der Fahrzeuge nach Kategorien durchgeblättert, in einer Live-3D-Vorschau betrachtet (Farbe/Türen/Licht/Motor umschaltbar), probegefahren und gekauft werden können.
Kernmerkmale (aus dem Code bestätigt):
- Multi-Framework: esx, qbcore oder custom (Config.Framework).
- Wiederverwendbare Fahrzeug-Pools (Config.GlobalVehicles), pro Shop über useGlobal referenziert, oder Inline-Fahrzeuglisten pro Kategorie (vehicles).
- Job-beschränkte Shops (restrictedJob), z. B. Police/Ambulance/Mechanic.
- Permanente Zufalls-Vorschaufahrzeuge vor dem Shop (Config.UseRandomPreview, pro Shop via DisablePermanentPreview abschaltbar).
- Ghost-Car-Schutz (Preview-Fahrzeuge tragen das Kennzeichen SHOPPREV* und werden aggressiv aufgeräumt).
- Discord-Logs für Kauf und Probefahrt (config_logs.lua).
Wichtig: Die Ressource besitzt keine exports() und keine RegisterCommand. Der Shop wird ausschließlich über Ped-Target oder Marker geöffnet. Integriert wird ausschließlich über Config, Events und die entkapselten Dateien client/open.lua / server/open.lua.
Voraussetzungen
| Abhängigkeit |
Pflicht |
Zweck / Quelle |
| es_extended |
Ja (siehe Hinweis) |
client/shop_fix.lua ruft exports['es_extended']:getSharedObject() unbedingt auf. → Auch im qbcore/custom-Betrieb aktuell erforderlich, sofern nicht editiert. |
| oxmysql |
Ja |
Fahrzeug-Speicherung. |
| qb-core |
Alternativ |
Nur wenn Config.Framework = "qbcore". |
| ox_target oder qb-target |
Optional |
Nur wenn Config.InteractionType = "ox_target" bzw. "qb-target". Andernfalls klassische Marker. |
| Keys-Ressource |
Optional |
Für Fahrzeugschlüssel nach Kauf: ESX → Event vehicles_keys:selfGiveVehicleKeys, QB → vehiclekeys:client:SetOwner, custom → custom_keys:give. |
| AdvancedParking |
Optional |
fixDeleteVehicle.lua bietet Kompatibilität; darf laut Datei nicht in AdvancedParking selbst gestartet werden. |
Framework-Runtime (aus dem Code):
- ESX: exports["es_extended"]:getSharedObject()
- QBCore: exports["qb-core"]:GetCoreObject()
- custom: keine Core-Anbindung; die relevante Logik (Geld, Speichern, Job, Notify) muss in client/open.lua / server/open.lua selbst implementiert werden.
Installation
- Ordner
ancomox_vehicleshop in das resources-Verzeichnis kopieren.
- In der
server.cfg ergänzen:
cfg
ensure ancomox_vehicleshop
Reihenfolge: nach Framework (es_extended/qb-core), nach oxmysql und (falls genutzt) nach ox_target/qb-target sowie der Keys-Ressource.
config/config.lua: Framework, Interaktionsart, Shops und Fahrzeuge einrichten.
config/locales.lua: Sprache über Config.Locale ('en' oder 'de') wählen.
config_logs.lua: Discord-Webhooks eintragen (optional).
- Fahrzeugbilder unter
html/images/vehicles/<model>.png ablegen (Kleinschreibung; fehlt das Bild, wird images/vehicles/default.png genutzt).
Custom-Framework: Config.Framework = "custom" setzen und die entkapselte Logik in client/open.lua / server/open.lua ausfüllen. Achtung: In server/open.lua gibt ProcessPayment im custom-Modus immer true zurück (kein Geldabzug) und SaveVehicleToDatabase speichert nichts (cb(true)) — beides selbst implementieren.
Datenbank
Die Ressource bringt keine eigene .sql-Datei mit. Es werden die Standard-Tabellen des jeweiligen Frameworks verwendet (Speicherung nur wenn Config.Use_ESX_Owned_Vehicles = true).
ESX → Tabelle owned_vehicles:
| Spalte |
Wert im Code |
owner |
Spieler-Identifier (xPlayer.identifier) |
plate |
generiertes 8-stelliges Kennzeichen (A–Z, 0–9) |
vehicle |
json.encode(props) inkl. model (Hash) und plate |
type |
'car' (fest) |
QBCore → Tabelle player_vehicles:
| Spalte |
Wert im Code |
license |
GetPlayerLicense(src) |
citizenid |
Player.PlayerData.citizenid |
vehicle |
Modellname (String) |
hash |
GetHashKey(model) |
mods |
json.encode(props) |
plate |
generiertes Kennzeichen |
state |
0 (fest) |
Beide Tabellen müssen aus dem jeweiligen Framework bereits existieren. Der Fahrzeug-type ist bei ESX fest 'car' — für Boote/Helis/Flugzeuge ggf. anpassen, sonst erscheint das Fahrzeug evtl. nicht im korrekten Garagentyp.
Konfiguration
Globale Optionen — config/config.lua
| Key |
Standard |
Beschreibung |
Config.Framework |
"esx" |
Framework: "esx", "qbcore" oder "custom". Steuert Geld-, Job-, Notify- und Speicherlogik. |
Config.InteractionType |
"ox_target" |
Interaktion am Ped: "ox_target", "qb-target" oder "marker" (3D-Marker + Taste E). |
Config.TargetPedModel |
"a_m_y_business_02" |
Ped-Modell des Verkäufers, das an jedem Shop gespawnt wird. |
Config.Use_ESX_Owned_Vehicles |
true |
true = Kauf wird in DB gespeichert + Kennzeichen generiert. false = keine Speicherung, Kennzeichen "NOPLATE". |
Config.UseRandomPreview |
true |
true = permanentes Zufalls-Vorschaufahrzeug vor jedem Shop (sofern Shop DisablePermanentPreview nicht setzt). |
Config.TestDriveCoords |
vector3(-891.3095, -3205.846, 13.94) |
Globaler Spawnort für alle Probefahrten. |
Config.TestDriveHeading |
58.0 |
Blickrichtung beim Probefahrt-Spawn. |
Config.TestDriveTime |
40 |
Probefahrt-Dauer in Sekunden. |
Config.ShopMarker |
36 |
Marker-Typ (nur bei InteractionType = "marker"). |
Config.ShopMarkerColor |
{ r = 0, g = 255, b = 0, a = 100 } |
Marker-Farbe (RGBA). |
Config.ShopMarkerScale |
1.0 |
Marker-Größe. |
Config.ShopDrawDistance |
30.0 |
Distanz (m), ab der der Marker gezeichnet wird. |
Config.ShopInteractDistance |
2.5 |
Distanz (m) für „Drücke E"-Hinweis und Interaktion (Marker-Modus). |
Config.BlipSprite |
225 |
Standard-Blip-Icon aller Shops. |
Config.BlipColor |
5 |
Standard-Blip-Farbe. |
Config.BlipScale |
0.8 |
Standard-Blip-Größe. |
Config.Shops |
Tabelle (14 Einträge) |
Liste aller Autohäuser (siehe Shop-Struktur). |
Config.GlobalVehicles |
Tabelle (16 Kategorien) |
Wiederverwendbare Fahrzeug-Pools, referenziert über useGlobal. |
Config.DefaultScale |
1.0 |
Im gelieferten Code nicht referenziert — vermutlich reserviert/Legacy. |
Config.MinScale |
0.5 |
Im gelieferten Code nicht referenziert. |
Config.MaxScale |
3.0 |
Im gelieferten Code nicht referenziert. |
Hinweis: Im ox_target/qb-target-Modus ist die Interaktionsdistanz in der Target-Option fest 2.5 hinterlegt (nicht aus Config.ShopInteractDistance).
Struktur eines Shop-Eintrags (Config.Shops[i])
| Feld |
Pflicht |
Beispiel |
Beschreibung |
Name |
Ja |
"Premium Deluxe Motorsport" |
Anzeigename im UI und am Blip. |
ShopCoords |
Ja |
vector3(-46.82244, -1095.814, 27.67) |
Position von Ped, Blip und Marker. |
ShopHeading |
Ja |
135.0 |
Blickrichtung des Verkäufer-Peds. |
PreviewCoords |
Ja |
vector3(-47.5914, -1091.934, 27.3023) |
Standort des Vorschaufahrzeugs (permanent + im Menü). |
PreviewHeading |
Ja |
137.0 |
Ausrichtung des Vorschaufahrzeugs. |
ReturnCoords |
Ja |
vector3(-46.6705, -1095.858, 27.274) |
Rückkehrposition nach Ende der Probefahrt. |
PurchaseSpawnCoords |
Ja |
vector3(-21.51374, -1084.052, 27.04) |
Spawnort des gekauften Fahrzeugs. |
PurchaseSpawnHeading |
Ja |
68.0 |
Ausrichtung des gekauften Fahrzeugs (auch Heading bei Probefahrt-Rückkehr). |
Categories |
Ja |
Tabelle |
Kategorien/Fahrzeuglisten des Shops. |
restrictedJob |
Nein |
"police" |
Nur Spieler mit diesem Job sehen Blip/Ped/Marker und dürfen kaufen. |
DisablePermanentPreview |
Nein |
true |
Deaktiviert das dauerhafte Vorschaufahrzeug vor diesem Shop. |
BlipSprite / BlipColor / BlipScale |
Nein |
43 / 3 / 0.8 |
Pro-Shop-Override der globalen Blip-Werte. |
MarkerType / MarkerScale / MarkerColor |
Nein |
— |
Pro-Shop-Override der Marker-Werte (im Code unterstützt, in der Default-Config nicht gesetzt). |
Struktur einer Kategorie (Categories[j])
| Feld |
Pflicht |
Beschreibung |
name |
Ja |
Anzeigename des Kategorie-Tabs im UI (z. B. "Super Cars"). |
useGlobal |
Entweder/oder |
Schlüssel in Config.GlobalVehicles (z. B. "Super"). |
vehicles |
Entweder/oder |
Inline-Fahrzeugliste statt useGlobal (siehe Police/Ambulance/Mechanic Shops). |
Ist useGlobal gesetzt, wird Config.GlobalVehicles[useGlobal] geladen; sonst vehicles. Ist keins vorhanden, ist die Kategorie leer.
Struktur eines Fahrzeug-Eintrags
| Feld |
Pflicht |
Beispiel |
Beschreibung |
name |
Ja |
"Adder" |
Anzeigename im UI. |
model |
Ja |
"adder" |
Spawnname des Fahrzeugs (muss serverseitig vorhanden sein; bestimmt auch das Bild images/vehicles/adder.png). |
price |
Ja |
1540000 |
Kaufpreis (Rohwert; 1540000 = $1.540.000). |
maxSpeed |
Optional |
200 |
Im gelieferten Code nicht ausgewertet. Die UI-Stats (Speed/Beschleunigung/Bremse/Handling) werden live aus den Handling-Daten des gespawnten Fahrzeugs berechnet. Feld dekorativ/reserviert. |
Config.GlobalVehicles — verfügbare Pools
16 Pools mit fertigen Vanilla-Fahrzeuglisten, per useGlobal referenzierbar:
Super, Sports, SportsClassic, MuscleCars, Bikes, Trucks, Trailers, Helis, Planes, Boats, SUVs, Offroad, Sedans, Coupes, Vans, Bike
Zwei Stolperfallen:
- Bikes = Motorräder, Bike = Fahrräder (BMX, Cruiser …). Nicht verwechseln.
- Vans ist definiert, wird aber von keinem Default-Shop referenziert — bei Bedarf einer Kategorie über useGlobal = "Vans" zuweisen.
Beispiel: neuen Shop mit globaler und eigener Kategorie anlegen
Config.Shops[#Config.Shops + 1] = {
Name = "Import Tuner",
ShopCoords = vector3(120.0, -1050.0, 29.2),
ShopHeading = 90.0,
PreviewCoords = vector3(124.0, -1047.0, 29.2),
PreviewHeading = 90.0,
ReturnCoords = vector3(120.0, -1050.0, 29.2),
PurchaseSpawnCoords = vector3(128.0, -1045.0, 29.2),
PurchaseSpawnHeading = 180.0,
-- restrictedJob = "cardealer", -- optional: Job-Sperre
-- DisablePermanentPreview = true, -- optional
-- BlipSprite = 326, BlipColor = 3, -- optional: Blip-Override
Categories = {
{ name = "Sportwagen", useGlobal = "Sports" }, -- aus GlobalVehicles
{
name = "Sondermodelle", -- eigene Inline-Liste
vehicles = {
{ name = "Mein Auto", model = "customcar1", price = 250000, maxSpeed = 200 },
{ name = "VIP GT", model = "customcar2", price = 900000, maxSpeed = 200 },
}
}
}
}
- Fahrzeug zu bestehendem Pool hinzufügen: neuen
{ name, model, price }-Eintrag in Config.GlobalVehicles.<Pool> einfügen — erscheint automatisch in jedem Shop, der diesen Pool via useGlobal nutzt.
- Preis ändern:
price des jeweiligen Eintrags anpassen.
- Bild:
html/images/vehicles/<model>.png (Kleinschreibung) ablegen, sonst default.png.
config/locales.lua
Config.Locale wählt die aktive Sprache (Standard 'en'; 'de' vorhanden). Jede Sprache ist ein Eintrag in Config.Locales:
| Key |
Beschreibung / Platzhalter |
press_to_open |
Marker-Hinweistext; %s = Shopname. ~INPUT_PICKUP~ = Taste. |
target_label |
Target-/Interaktions-Label; %s = Shopname. |
already_in_test_drive |
Meldung: bereits in Probefahrt. |
error_loading_vehicle |
Fehler beim Modell-Laden. |
test_drive_started |
Probefahrt gestartet; %d = Sekunden. |
test_drive_ended |
Probefahrt beendet. |
vehicle_bought |
Kaufbestätigung; %s Modell, %d Preis, %s Kennzeichen. |
not_enough_money |
Nicht genug Geld. |
db_error |
Datenbankfehler beim Speichern. |
Standardwerte (de): u. a. press_to_open = "Drücke ~INPUT_PICKUP~ um %s zu betreten", vehicle_bought = "Gekauft: ~b~%s~s~ für ~g~$%d~s~ (Kennz.: ~y~%s~s~)".
Events & Commands
Commands: Keine (RegisterCommand existiert nicht). Der Shop öffnet ausschließlich über Ped-Target oder Marker.
Netz-Events der Ressource:
| Richtung |
Event |
Parameter |
Zweck |
| Client → Server |
ancomox_vehicleshop:buyVehicle |
shopIndex, model, price, vehicleProps |
Kaufanfrage. Server prüft Geld (ProcessPayment), speichert Fahrzeug, sendet purchaseSuccess. |
| Client → Server |
ancomox_vehicleshop:logTestDrive |
model |
Meldet Probefahrt-Start für Discord-Log. |
| Server → Client |
ancomox_vehicleshop:purchaseSuccess |
shopIndex, model, price, plate, vehicleProps |
Spawnt gekauftes Fahrzeug am PurchaseSpawnCoords, setzt Kennzeichen, gibt Schlüssel. |
Betreiber-Warnung (Anti-Cheat): price wird vom Client an buyVehicle übergeben und serverseitig ungeprüft an ProcessPayment/Discord-Log durchgereicht. Es findet keine serverseitige Preis-/Modell-Validierung gegen die Config statt — ein manipulierter Client könnte Preis 0 senden. Bei Bedarf serverseitige Validierung in server/open.lua ergänzen.
Extern erwartete Events (Kompatibilität / selbst bereitzustellen):
| Event |
Modus |
Zweck |
vehicles_keys:selfGiveVehicleKeys (Server) |
esx |
Schlüsselvergabe nach Kauf. |
vehiclekeys:client:SetOwner (lokal) |
qbcore |
Schlüsselvergabe nach Kauf. |
custom_keys:give (Server) |
custom |
Schlüsselvergabe nach Kauf. |
esx:showNotification / QBCore:Notify / custom:notify |
jeweils |
Server-Benachrichtigungen an Client. |
esx:playerLoaded, esx:setJob, QBCore:Client:OnPlayerLoaded, QBCore:Client:OnJobUpdate, custom:playerLoaded, custom:setJob |
jeweils |
Lösen FullShopRefresh() aus (Blips/Previews/Job-Rechte neu aufbauen). |
Interne NUI-Callbacks (nur zur Info, keine Integrationspunkte): closeUI, toggleAutoRotate, changeColor, toggleDoor, toggleLights, toggleEngine, selectVehicle, buyVehicle, startTestDrive, hoverVehicleList.
Discord-Logs
Konfiguration in config_logs.lua (globale Tabelle Config_Logs):
| Key |
Standard |
Beschreibung |
Config_Logs.Webhooks.VehicleSale |
"" |
Webhook-URL für Fahrzeugkäufe. Leer = kein Versand. |
Config_Logs.Webhooks.TestDrive |
"" |
Webhook-URL für Probefahrten. Leer = kein Versand. |
Config_Logs.LogSettings.VehicleSale |
true |
Kauf-Logging aktivieren/deaktivieren. |
Config_Logs.LogSettings.TestDrive |
true |
Probefahrt-Logging aktivieren/deaktivieren. |
Versand über PerformHttpRequest als Embed (Username „Vehicle Shop", Footer „Ancomox Premium Vehicle Shop"). Kauf-Embed enthält Spielername, Identifier, Modell, Preis, Kennzeichen; Probefahrt-Embed Spielername, Identifier, Modell. Bei Use_ESX_Owned_Vehicles = false wird der Kauf als „(No DB Save)" geloggt.
Hinweise / Troubleshooting
- es_extended trotz qbcore/custom nötig:
client/shop_fix.lua ruft exports['es_extended']:getSharedObject() unbedingt auf. Ohne es_extended bricht der Client-Start. Für reinen QB-/Custom-Betrieb muss diese Stelle editiert werden.
- Kein Geldabzug im custom-Modus:
ProcessPayment gibt bei Config.Framework = "custom" immer true zurück und SaveVehicleToDatabase speichert nichts — eigene Logik in server/open.lua einbauen.
- Ghost-Cars / Preview-Reste: Vorschaufahrzeuge tragen das Kennzeichen
SHOPPREV*. client/shop_fix.lua und ClearGhostCars/SafeDeleteEntity räumen sie beim Start, Öffnen und Ressourcen-Stopp auf. Manuell platzierte Fahrzeuge mit diesem Kennzeichenmuster werden ebenfalls gelöscht.
fixDeleteVehicle.lua: überschreibt DeleteEntity/DeleteVehicle für AdvancedParking-Kompatibilität. Die Datei darf nicht innerhalb von AdvancedParking gestartet werden (bricht sonst mit Hinweismeldung ab).
- Fahrzeug erscheint nicht in Garage: Bei ESX wird
type = 'car' fest gespeichert — für Boote/Helis/Flugzeuge ggf. Speicher-Typ in server/open.lua anpassen.
- Kein Schlüssel nach Kauf: Passende Keys-Ressource muss laufen und das jeweilige Event (
vehicles_keys:selfGiveVehicleKeys bei ESX, vehiclekeys bei QB) bereitstellen.
maxSpeed/DefaultScale/MinScale/MaxScale: in der Config vorhanden, aber im ausgelieferten Code nicht ausgewertet (ggf. Legacy).
Vans-Pool ungenutzt, Bikes ≠ Bike — siehe Abschnitt GlobalVehicles.
- Preis-Manipulation: siehe Betreiber-Warnung unter „Events" — serverseitig kein Preisabgleich mit der Config.