Converting FiveM Scripts – ESX, QBCore, QBOX (Frame…
Dies ist ein Code-First-Leitfaden zur einfachen Konvertierung von FiveM-Skripten ohne Schnickschnack, der Ihnen genau zeigt, wie Sie Konvertieren Sie FiveM-Skripte zwischen ESX, QBCore, QBOX (qbx_core) und Framework-agnostischen (eigenständigen) Setups. Sie erhalten dreiseitige API-Zuordnungen, Adaptercode, SQL-Migrationsschritte (mysql-async → oxmysql), QBOX-Spezifikationen, Test-Checklisten und Tipps zur Produktionshärtung.
TL;DR – Migration in 10 Schritten
- Momentaufnahme und Staging: DB/Dateien sichern. Starten Sie einen lokalen Testserver mit dem Zielframework.
- Abhängigkeiten identifizieren: Inventar, Ziel, Menü/UI, Rückrufe, DB-Ebene, Kern Zugang.
- Pin-Versionen & Bestellung: oxmysql → ox_lib → Framework (es_extended / qb-core / qbx_core) → Ihre Ressourcen.
- Lesen Sie den Codepfad: Finden Sie alle Spieler-, Geld-, Job-/Aufgaben-, Inventar-, Rückruf-/Ereignis- und DB-Anrufe.
- Karten-APIs: Verwenden Sie die folgenden Rosetta-Tabellen, um ESX ↔ QBCore ↔ QBOX zuzuordnen.
- Überbrücken Sie es: Fügen Sie einen bridge.lua Adapter, der das Framework automatisch erkennt und Anrufe normalisiert.
- Rückrufe konvertieren: ESX/QB-Rückrufe → QBOX/ox_lib
lib.callback.*bei Bedarf. - Datenbank migrieren: Verschieben Sie Kennungen, Konten, Artikel, Fahrzeuge; wechseln Sie zu oxmysql
*.erwartenStil. - Ereignisse härten: Bestätigen
Quelle, Grenzen, Gruppen/Jobs, Eigentum; vertraue niemals dem Client. - Qualitätssicherung und Veröffentlichung: Führen Sie die Checklisten aus: Tag, Änderungsprotokoll und Rollback-Plan.
Sind Sie ein Entwickler, der nach einer Adapterlösung sucht? Schauen Sie sich hier unser kostenloses Adapterskript an
1) Frameworks 101: ESX vs. QBCore vs. QBOX vs. Standalone
Architekturübersicht
| Kategorie | ESX | QBCore | QBOX |
|---|---|---|---|
| Architektur/Core-Zugriff | exports['es_extended']:getSharedObject() → ESX | exports['qb-core']:GetCoreObject() → QBCore | Kein globales Kernobjekt; verwenden exports.qbx_core:* für Spieler, Gruppen und Benachrichtigungen |
| Spielerobjekt | ESX.GetPlayerFromId(src) → xPlayer | QBCore.Functions.GetPlayer(src) → Spieler | exports.qbx_core:GetPlayer(src) → Spieler mit Spielerdaten |
| Kennungen | Historisch Kennung (Lizenz/Steam) | Bürger-ID als Primärschlüssel | Bürger-ID als Primärschlüssel |
| Geld Handhabung | xPlayer.addMoney() / `addAccountMoney('bank' | 'Schwarzgeld')` | `Player.Functions.AddMoney('Bargeld' |
| Berufe & Pflichten | xPlayer.job.name, .Job.Grade | Player.PlayerData.job.name, .Klassenstufe, Im Dienst | Job-/Gruppenhelfer über exports.qbx_core:* (Gruppenkontrollen, Dienstplaner) |
| Inventar | ESX-Standard / ox_inventory / qb-inventar (empfohlen: ox_inventory) | qb-inventar / ox_inventory | ox_inventory empfohlen |
| Rückrufe | ESX.RegisterServerCallback / ESX.TriggerServerCallback | QBCore.Functions.CreateCallback / TriggerCallback | Bevorzugen ox_lib Rückrufe (lib.callback.register/await) oder QBOX-Exporte |
| Datenbankebene | Alt: mysql-async → Jetzt: oxmysql (MySQL.query.await, MySQL.update.await) | oxmysql | oxmysql |
| Benutzeroberfläche – Benachrichtigungen | ESX-Benachrichtigung | QB-Benachrichtigung | QBOX Helfer benachrichtigen oder lib.notify |
| UI – Menüs | esx_menu_default / ox_lib:registerContext | qb-menü / ox_lib:registerContext | ox_lib:registerContext / lib.inputDialog |
| UI – Zielsystem | esx_target / ox_ziel | qb-ziel / ox_ziel | ox_ziel empfohlen |
Wo sich QBOX unterscheidet
- Ressourcenname und Exporte:
qbx_core(NEINGetCoreObject). - Stützt sich auf ox_lib (Rückrufe, Benachrichtigungen) und oxmysql standardmäßig.
- Integrierte Funktionen für mehrere Charaktere/Warteschlangen/Gruppen; eigensinnige Helfer für Jobs/Aufgaben.

2) Preflight: Umgebung und Werkzeuge
- Server: aktuell FXServer Artefakte, Lua 5.4, CFX
sicherstellenBefehl - Befehl:
stellen Sie Oxmysql sicher→Stellen Sie ox_lib sicher→Rahmen gewährleisten→Stellen Sie sicher, dass Ihre Ressource - Editor: VS Code + Lua LS, Stift; optional ripgrep für schnelle Musterscans
- Versuchsaufbau: sauberer Datenbank-Snapshot; ausführliche Protokollierung; kleiner Datensatz
- Suchmuster Sie verwenden:
ESX.xPlayer.QBCore.Spielerdatenqbx_coreexports.ox_inventorymysql%-asyncMySQL.
3) Rosetta Stone: ESX ↔ QBCore ↔ QBOX (Dreiwege-Mapping)
Benutzerfreundliche Referenz kopieren und einfügen. Verwenden Sie sie, um Anrufe zu ersetzen oder Ihren Adapter zu verkabeln.
Kern & Spieler
| Konzept | ESX | QBCore | QBOX |
|---|---|---|---|
| Holen Sie sich den Kern | exports['es_extended']:getSharedObject() | exports['qb-core']:GetCoreObject() | (n. z. – Exporte verwenden) |
| Spieler holen (Quelle) | ESX.GetPlayerFromId(src) | QBCore.Functions.GetPlayer(src) | exports.qbx_core:GetPlayer(src) |
| Erhalten Sie per Bürger-ID | (benutzerdefinierte Abfrage) | QBCore.Functions.GetPlayerByCitizenId(id) | exports.qbx_core:GetPlayerByCitizenId(id) (falls vorhanden) |
Kennungen
| Konzept | ESX | QBCore | QBOX |
|---|---|---|---|
| Primär | Kennung (Lizenz/Steam) | Bürger-ID | Bürger-ID |
| Zebrastreifen | Tisch id_map(Kennung, Bürger-ID) | Dasselbe | Dasselbe |
Geld
| Aktion | ESX | QBCore | QBOX |
|---|---|---|---|
| Bargeld hinzufügen | xPlayer.addMoney(a) | Player.Functions.AddMoney('Bargeld', a) | Player.PlayerData.money.cash += a (über Adapter + Speichern) |
| Bank hinzufügen | xPlayer.addAccountMoney('bank', a) | Player.Functions.AddMoney('bank', a) | Player.PlayerData.money.bank += a (Adapter + Speichern) |
| Schmutzig hinzufügen | xPlayer.addAccountMoney('schwarzes_Geld', a) | Artikel oder zusätzliches Konto (serverdefiniert) | Karte zu Artikel/Alt-Wallet (zB, Krypto) (Adapterauswahl) |
Jobs, Pflichten, Gruppen
| Konzept | ESX | QBCore | QBOX |
|---|---|---|---|
| Stellenanzeige lesen | xPlayer.job.name, .Grad | Player.PlayerData.job.name, .Klassenstufe | Spieler.Spielerdaten.Job.* + Gruppenhelfer |
| Im Dienst | (variiert) | Player.PlayerData.job.onduty | SetJobDuty/Gruppenhelfer über Exporte |
| Gruppenprüfung | (hinzufügen Auf) | (hinzufügen Auf) | exports.qbx_core:HasGroup(src, group) (Beispiel) |
Inventar
| Aktion | ESX | QBCore | QBOX |
|---|---|---|---|
| Artikel hinzufügen | xPlayer.addInventoryItem(n, c) | Player.Functions.AddItem(n, c, ...) | bevorzugen ox_inventory Export über Adapter |
| ox_inventory | exports.ox_inventory:* | exports.ox_inventory:* | exports.ox_inventory:* |
Rückrufe
| Konzept | ESX | QBCore | QBOX |
|---|---|---|---|
| Server registrieren | ESX.RegisterServerCallback | QBCore.Functions.CreateCallback | lib.callback.register (ox_lib) |
| Client-Trigger | ESX.TriggerServerCallback | QBCore.Functions.TriggerCallback | lib.callback.await |
DB-Schicht
| Konzept | ESX/QB (alt) | oxmysql (Ziel) |
|---|---|---|
| Bringen | MySQL.Async.fetchAll(sql, params, cb) | lokale Zeilen = MySQL.query.await(sql, {p1, ...}) |
| Aktualisieren | MySQL.Async.execute(sql, params, cb) | lokales aff = MySQL.update.await(sql, {p1, ...}) |
UI / Benachrichtigen / Ziel
| Konzept | ESX | QBCore | QBOX |
|---|---|---|---|
| Benachrichtigen | ESX.ShowNotification(msg) | QBCore.Functions.Notify(msg, Typ) | exports.qbx_core:Benachrichtigen(...) oder lib.notify({...}) |
| Speisekarte | (variiert) | qb-menü | ox_lib:registerContext / Eingänge |
| Ziel | (hinzufügen Auf) | qb-ziel | ox_ziel |
Tipp: favorisieren ox_lib, ox_inventory, ox_ziel um über Frameworks hinweg neutral zu bleiben.
4) Tutorial A – ESX → QBCore (praktisch)
Ziel: Konvertieren Sie eine einfache Shop-Ressource von ESX zu QBCore.
Schritt 1 – Abhängigkeiten
- Sicherstellen
oxmysql,ox_lib,qb-kernwerden gestartet. - Wenn das Skript ESX-Inventar verwendet, migrieren Sie zu ox_inventory (empfohlen) oder dem QB-Inventar zuordnen.
Schritt 2 – Framework erkennen und eine Brücke hinzufügen
Erstellen bridge.lua um Spieler/Geld/Inventar zu normalisieren:
-- bridge.lua (ESX/QB/QBOX adapter)
local M = {}
local fw
CreateThread(function()
if GetResourceState('qbx_core') == 'started' then
fw = 'qbx'
elseif GetResourceState('qb-core') == 'started' then
fw = 'qb'
elseif GetResourceState('es_extended') == 'started' then
fw = 'esx'
else
fw = 'none'
end
end)
function M.getFramework()
return fw
end
function M.getPlayer(src)
if fw == 'qbx' then
return exports.qbx_core:GetPlayer(src)
elseif fw == 'qb' then
return exports['qb-core']:GetCoreObject().Functions.GetPlayer(src)
elseif fw == 'esx' then
return exports['es_extended']:getSharedObject().GetPlayerFromId(src)
end
end
local function saveQbox(src)
if fw == 'qbx' and exports.qbx_core and exports.qbx_core.Save then
exports.qbx_core:Save(src)
end
end
function M.addMoney(src, account, amount)
local p = M.getPlayer(src)
if not p or type(amount) ~= 'number' then return false end
if fw == 'qb' then
return p.Functions.AddMoney(account, amount)
elseif fw == 'esx' then
if account == 'cash' then
return p.addMoney(amount)
elseif account == 'bank' then
return p.addAccountMoney('bank', amount)
elseif account == 'black' or account == 'black_money' then
return p.addAccountMoney('black_money', amount)
end
elseif fw == 'qbx' then
local money = p.PlayerData and p.PlayerData.money or {}
account = account == 'black' and 'crypto' or account -- example mapping
money[account] = (money[account] or 0) + amount
saveQbox(src)
return true
end
return false
end
function M.removeMoney(src, account, amount)
return M.addMoney(src, account, -math.abs(amount))
end
function M.addItem(src, name, count, meta)
if GetResourceState('ox_inventory') == 'started' then
return exports.ox_inventory:AddItem(src, name, count, meta)
elseif fw == 'qb' then
return exports['qb-inventory']:AddItem(src, name, count, false, meta)
elseif fw == 'esx' then
local p = M.getPlayer(src)
return p and p.addInventoryItem(name, count)
elseif fw == 'qbx' then
return exports.ox_inventory:AddItem(src, name, count, meta)
end
end
function M.notify(src, msg, ntype)
if fw == 'qb' then
TriggerClientEvent('QBCore:Notify', src, msg, ntype or 'primary')
elseif fw == 'esx' then
TriggerClientEvent('esx:showNotification', src, msg)
elseif fw == 'qbx' then
if exports.qbx_core and exports.qbx_core.Notify then
exports.qbx_core:Notify(src, msg, ntype or 'info')
else
lib.notify(src, { title = 'Notice', description = msg })
end
else
print(('notify(%s): %s'):format(src, msg))
end
end
return M
Schritt 3 – Rückrufe konvertieren
- ESX → QB: ersetzen
ESX.RegisterServerCallbackmitQBCore.Functions.CreateCallback. - Client-Anwendungen
QBCore.Functions.TriggerCallback.
-- server.lua (callback)
local bridge = require 'bridge'
local function getPrice(item)
return 100
end
if GetResourceState('qb-core') == 'started' then
local QBCore = exports['qb-core']:GetCoreObject()
QBCore.Functions.CreateCallback('shop:getPrice', function(source, cb, item)
cb(getPrice(item))
end)
else
-- fallback for QBOX/ESX via ox_lib
lib.callback.register('shop:getPrice', function(source, item)
return getPrice(item)
end)
end
-- client.lua (callback)
if GetResourceState('qb-core') == 'started' then
local QBCore = exports['qb-core']:GetCoreObject()
QBCore.Functions.TriggerCallback('shop:getPrice', function(price)
print('price', price)
end, 'bread')
else
local price = lib.callback.await('shop:getPrice', false, 'bread')
print('price', price)
end
Schritt 4 – Geld- und Inventarpfade
- Ersetzen
xPlayer.addAccountMoneymitPlayer.Functions.AddMoney(für QB) oder Adapter. - Standardisieren Sie alle Bestandsänderungen über den Adapter.
Schritt 5 – Ereignisse (serverseitige Sicherheit)
RegisterNetEvent('shop:buy', Funktion(Artikel, Betrag) lokale Quelle = Quelle wenn Typ(Artikel) ~= 'Zeichenfolge' oder Typ(Betrag) ~= 'Zahl' dann returniere Ende wenn Betrag < 1 oder Betrag > 10 dann returniere Ende lokales p = bridge.getPlayer(Quelle) wenn nicht p dann returniere Ende lokaler Preis = 100 * Betrag wenn nicht bridge.removeMoney(Quelle, 'Bargeld', Preis) dann returniere Ende bridge.addItem(Quelle, Artikel, Betrag) bridge.notify(Quelle, ('Gekauft %dx %s'):Format(Betrag, Artikel), 'Erfolg') Ende)
Schritt 6 – DB-Migration (Kennungen, Konten)
- Erstellen Sie einen Zebrastreifen
id_map(Kennung, Bürger-ID). - Bevölkern
Bürger-IDfür QB von ESXBenutzerTabelle über Auswahlregel (vorhandene Spalte oder generiert).
-- Beispiel: Erstellen Sie einen Crosswalk und füllen Sie ihn nach (passen Sie ihn an Ihr Schema an) CREATE TABLE IF NOT EXISTS id_map (Bezeichner VARCHAR(64) PRIMARY KEY, Bürger-ID VARCHAR(64) NOT NULL UNIQUE); -- Angenommen, Sie haben neue Bürger-IDs generiert und diese zuvor gespeichert INSERT IGNORE INTO id_map(Bezeichner, Bürger-ID) SELECT u.Bezeichner, u.Bürger-ID FROM Benutzer u WHERE u.Bürger-ID IS NOT NULL;
mysql‑async → oxmysql
-- before (mysql-async)
MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @id', {['@id'] = identifier}, function(rows) ... end)
-- after (oxmysql)
local rows = MySQL.query.await('SELECT * FROM users WHERE identifier = ?', { identifier })
Schritt 7 – Qualitätssicherung
- Spawnen Sie Gegenstände, kaufen/verkaufen Sie, stellen Sie sicher, dass sich die Salden korrekt ändern.
- Überprüfen Sie, ob Rückrufe unter Last zurückgegeben werden.
- Überprüfen Sie, ob die Inventarmetadaten erhalten bleiben.
5) Tutorial B – QBCore → ESX (praktisch)
Wichtige Vorbehalte:
- ESX verwendet Konten für
Bank/Schwarzgeld. Port QBs schmutziges Geld Artikel (falls verwendet) zum ESX-Konto oder behalten Sie es als Element. - Das Stellenschema ist unterschiedlich (Klasse vs. Niveau). Planen Sie sorgfältig.
Beispiel: Money-Mapping per Adapter
-- in bridge.lua, when fw == 'esx'
function M.qbToEsxMoney(account)
if account == 'cash' then return 'cash' end
if account == 'bank' then return 'bank' end
if account == 'black' or account == 'black_money' then return 'black_money' end
return account
end
Beispiel: Callback-Konvertierung (Server)
if GetResourceState('es_extended') == 'started' then
local ESX = exports['es_extended']:getSharedObject()
ESX.RegisterServerCallback('garage:getVehicles', function(source, cb)
local src = source
local p = bridge.getPlayer(src)
local rows = MySQL.query.await('SELECT * FROM owned_vehicles WHERE owner = ?', { p.identifier })
cb(rows)
end)
end
Test-Checkliste
- Stellenangebote/Beförderungen bleiben bestehen.
- Bankguthaben/unsaubere Guthaben mutieren wie erwartet.
- Fahrzeugbesitz und Kennzeichenformate validiert.
6) Tutorial C — QBCore ↔ QBOX (Hands-on)
QBCore → QBOX
- Ersetzen
QBCore.Funktionen.*mitexports.qbx_core:*oder ox_lib Rückrufe. - Spieler:
QBCore.Functions.GetPlayer(src)→exports.qbx_core:GetPlayer(src). - Benachrichtigen:
QBCore.Functions.Notify→exports.qbx_core:Benachrichtigenoderlib.notify. - Aufgaben/Gruppen: QBOX-Exporte nutzen (
SetJobDuty,HasGroup, usw.).
QBOX → QBCore
- Ersetzen
exports.qbx_core:*mitQBCore.Funktionen.*Äquivalente oder Ihren Adapter. - Führen Sie QB-Rückrufe und Menü-/Zieläquivalente nach Bedarf wieder ein.
Vorher/Nachher-Ausschnitte
-- Vorher (QB notify) QBCore.Functions.Notify('Hallo', 'Erfolg') -- Nachher (QBOX) exports.qbx_core:Notify(source, 'Hallo', 'Erfolg') -- oder lib.notify(source, { title = 'Hallo', description = 'Willkommen', type = 'Erfolg' })
-- Vorher (QB holt Spieler) lokaler Spieler = QBCore.Functions.GetPlayer(src) -- Nachher (QBOX) lokaler Spieler = exports.qbx_core:GetPlayer(src)
7) Tutorial D – Framework → Standalone mit Adaptern
Schreiben Sie Ihre Skripte einmal und führen Sie sie überall aus:
- Erstellen Sie ein Brücke Bereitstellung einer stabilen API:
getPlayer,getIdentifier,Geld hinzufügen/entfernen,Artikel hinzufügen/entfernen,benachrichtigen,hatGruppe,im Dienst,RückrufeVerpackung. - Framework zur Laufzeit erkennen (
qbx_core→qb-kern→es_extended→keiner). - Verwenden ox_lib für Rückrufe und Benachrichtigungen, wenn kein direkter Framework-Helfer vorhanden ist.
Minimaler Standalone-Fallback
-- when fw == 'none'
function M.getPlayer(src)
-- implement a minimal table or reject actions gracefully
return { id = src }
end
function M.notify(src, msg)
print(('notify(%s): %s'):format(src, msg))
end
8) Leistung und Sicherheit (Produktionshärtung)
Serverseitige Validierung (Client niemals vertrauen)
- Bestätigen Typen Und Grenzen bei jeder Veranstaltung.
- Überprüfen Eigentum, Abklingzeiten, Distanz (falls relevant), Job/Gruppe.
- Stellen Sie sicher, dass der Betrag nicht ins Minus rutscht, begrenzen Sie die Beträge.
- Vergleichen Preis serverseitig; vom Client bereitgestellte Gesamtsummen werden nicht akzeptiert.
Veranstaltungsstruktur
- Verwenden Sie eine Serverereignis pro Aktion; legen Sie den Kunden keine Rohinventar-/Geldfunktionen offen.
- Bevorzugen Rückrufe für Anforderungs-/Antwortflüsse.
DB & Leistung
- Wechseln zu oxmysql Warten Sie auf APIs; Batch-Schreibvorgänge; vermeiden Sie Abfragen pro Tick.
- Indexieren Sie häufig abgefragte Spalten (
Bürger-ID,Kennung,Platte). - Konfigurations-/Preislisten im Speicher zwischenspeichern; sie einmal an Clients exportieren und dann bei Änderungen vergleichen.
- Verwenden
GlobalStatesparsam; vermeiden Sie Hot-Loop-Updates.
9) Häufige Fehler und Debugging-Playbook
- Angenommen
GetCoreObjectexistiert auf QBOX → das tut es nicht; verwendenexports.qbx_core:*. - Rückrufe werden nach der Migration zu QBOX nie wieder angezeigt → ESX/QB-Callbacks werden nicht registriert; wechseln Sie zu
lib.callback.register/await. - Semantik des schmutzigen Geldes unterscheiden → entscheiden Sie sich für Artikel vs. Konto vs. Alt-Wallet und standardisieren Sie in Ihrem Adapter.
- Gemischte Bestandsannahmen → normalisieren auf
ox_inventory. - Probleme mit der Startreihenfolge →
oxmysql→ox_lib→ Rahmen → Ihre Ressourcen. - Drift der Fahrzeugmetadaten → Stellen Sie sicher, dass die JSON-Spalten und Plattenformate mit dem Zielframework übereinstimmen.
10) Best Practices für fxmanifest.lua
fx_version 'cerulean' game 'gta5' lua54 'yes' shared_scripts { '@ox_lib/init.lua', 'bridge.lua', 'config.lua' } client_scripts { 'client/*.lua' } server_scripts { '@oxmysql/lib/MySQL.lua', 'server/*.lua' } escrow_ignore { 'bridge.lua', 'config.lua' } -- Abhängigkeiten (wählen Sie aus, was Sie verwenden): ox_lib, oxmysql, ox_inventory, qb-core ODER qbx_core ODER es_extended
- Erklären Lua 5.4 (
lua54 'ja'). - Halten
escrow_ignoreminimal; versuchen Sie niemals, das Treuhandkonto zu umgehen.
11) Datenmigration: SQL-Snippets (Beispiele)
Passen Sie Tabellen-/Spaltennamen an Ihr Schema an. Führen Sie es immer zuerst auf einer Kopie aus.
Kennungen: ESX → QB/QBOX
- CitizenID zu ESX-Benutzern hinzufügen, falls fehlend ALTER TABLE users ADD COLUMN IF NOT EXISTS citizenid VARCHAR(64); - Mit einem deterministischen Generator oder einer vorhandenen Spalte auffüllen UPDATE users SET citizenid = LOWER(SUBSTRING(MD5(CONCAT(identifier,'-QB')),1,10)) WHERE citizenid IS NULL; - Crosswalk erstellen CREATE TABLE IF NOT EXISTS id_map ( identifier VARCHAR(64) PRIMARY KEY, citizenid VARCHAR(64) NOT NULL UNIQUE ); INSERT IGNORE INTO id_map(identifier, citizenid) SELECT identifier, citizenid FROM users WHERE citizenid IS NOT NULL;
Konten/Geld
-- Beispiel: Konvertieren Sie ESX-Bankguthaben in eine QB-Geldtabelle (wenn Ihr QB-Schema diese separat speichert) INSERT INTO player_money (citizenid, account, amount) SELECT m.citizenid, 'bank', a.money FROM ( SELECT u.citizenid, SUM(CASE WHEN account = 'bank' THEN money ELSE 0 END) AS money FROM user_accounts ua JOIN users u ON u.identifier = ua.identifier GROUP BY u.citizenid ) a JOIN users m ON m.citizenid = a.citizenid ON DUPLICATE KEY UPDATE amount = VALUES(amount);
Fahrzeuge
-- Kennzeichen auf Zielformat normalisieren (Beispiel: 8 Zeichen höher) UPDATE owned_vehicles SET plate = UPPER(LEFT(plate,8)); -- Sicherstellen, dass JSON-Metadatenspalten gültig sind UPDATE owned_vehicles SET mods = JSON_MERGE_PATCH('{}', mods) WHERE JSON_VALID(mods) = 0;
12) Adapter, die Sie wiederverwenden können (Starter)
Legen Sie dies in
bridge.luaund erweitern Sie es nach Ihren Bedürfnissen. Es erkennt ESX/QB/QBOX automatisch und stellt eine stabile API bereit.
local M = {}
local fw = 'none'
CreateThread(function()
if GetResourceState('qbx_core') == 'started' then fw = 'qbx'
elseif GetResourceState('qb-core') == 'started' then fw = 'qb'
elseif GetResourceState('es_extended') == 'started' then fw = 'esx' end
end)
function M.framework() return fw end
function M.player(src)
if fw == 'qbx' then return exports.qbx_core:GetPlayer(src)
elseif fw == 'qb' then return exports['qb-core']:GetCoreObject().Functions.GetPlayer(src)
elseif fw == 'esx' then return exports['es_extended']:getSharedObject().GetPlayerFromId(src) end
end
function M.identifier(src)
local p = M.player(src); if not p then return nil end
if fw == 'qb' or fw == 'qbx' then
return p.PlayerData and p.PlayerData.citizenid
elseif fw == 'esx' then
return p.identifier
end
end
function M.hasGroup(src, group)
if fw == 'qbx' and exports.qbx_core and exports.qbx_core.HasGroup then
return exports.qbx_core:HasGroup(src, group)
end
return false
end
function M.notify(src, msg, ntype)
if fw == 'qb' then
TriggerClientEvent('QBCore:Notify', src, msg, ntype or 'primary')
elseif fw == 'esx' then
TriggerClientEvent('esx:showNotification', src, msg)
elseif fw == 'qbx' then
if exports.qbx_core and exports.qbx_core.Notify then
exports.qbx_core:Notify(src, msg, ntype or 'info')
else
lib.notify(src, { description = msg })
end
end
end
return M
13) Abschließende Checklisten (Kopieren und Versenden)
A) Entdeckung und Preflight
- Snapshot-DB/Dateien; Erstellen eines Staging-Servers
- Artefakte und Ressourcenversionen anheften; Startreihenfolge festlegen
- Inventar-/Ziel-/Menüabhängigkeiten aufgelistet
- Identifikationsstrategie festlegen (Citizenid Crosswalk)
B) Code-Audit
- Grep für ESX/QB/QBOX/ox_*/mysql‑async
- Listen Sie alle Geld-/Inventar-/Job-/Pflichtanrufe auf
- Rückrufe und Server-/Client-Ereignisse auflisten
C) Kartierung und Design
- Adapteroberfläche auswählen (Player, IDs, Geld, Gegenstände, Benachrichtigungen, Rückrufe)
- Legen Sie eine Strategie für schmutziges Geld fest (Konto vs. Artikel vs. alternative Geldbörse)
- Bevorzugen Sie ox_lib/ox_inventory/ox_target
D) Datenmigration
- Bauen Sie einen CitizenID-Zebrastreifen
- Konten/Geld umwandeln
- Normalisieren Sie Fahrzeugmetadaten und Kennzeichen
- Wechseln Sie zu Oxmysql-Await-Aufrufen
E) QS-Tests
- Ereignissicherheit: Typen/Grenzen/Eigentum/Abkühlungszeiten
- Rückrufe: Rückgabewerte unter Last
- Inventarmetadaten bleiben erhalten
- Job-/Pflichten-/Gruppenlogik entspricht Design
F) Freigabe und Rollback
- Tag-Release; Änderungsprotokoll
- Snapshot vor der Migration 7–14 Tage lang aufbewahren
- Überwachen Sie Fehler/Latenz; indizieren Sie Hot Queries
14) FAQ (mit JSON‑LD Schema)
F: Kann ich QBCore-Skripte ohne vollständige Neuschreibung auf QBOX portieren?
A: Mit Adaptern und ox_lib Rückrufe, die meisten Skripte benötigen nur eine Neuzuordnung der Anrufe.
F: Was mache ich mit Schwarzgeld/Schmutzgeld?
A: Standardisieren Sie Ihren Adapter: ESX → Schwarzgeld Konto; QB → Artikel oder zusätzliches Konto; QBOX → Artikel oder alternative Wallet (z. B. Krypto). Behalten Sie projektweit eine Strategie bei.
F: Warum bleiben Rückrufe nach dem Wechsel zu QBOX hängen?
A: ESX/QB-Rückrufe werden auf QBOX nicht ausgelöst. Verwenden Sie lib.callback.register/await.
F: Beste Möglichkeit, MySQL-Async zu migrieren?
A: Ersetzen durch oxmysql Warten Sie auf APIs; entfernen Sie Rückrufpyramiden.
F: Wie konvertiere ich qb‑target in ox_target?
A: Ersetzen Sie addBoxZone/Entity-API-Aufrufe eins zu eins; die Ereignisnutzlasten bleiben ähnlich. Halten Sie die Zielnamen stabil.
15) Visuals (Vorschläge)
- Flussdiagramm (Entdecken → Zuordnen → Anpassen → Datenbank migrieren → Testen → Freigeben)
- Adapterdiagramm: Skript → Bridge → Framework (ESX/QB/QBOX)
- Rosetta-Tabelle: oben enthalten (Export als CSV zum Download)
[Discover] → [Map APIs] → [Write Adapter] → [Migrate DB] → [Harden] → [QA] → [Release]
16) Rechtlich und ethisch
- Beachten Sie die Lizenzen (MIT/GPL/NC). Nicht Umgehung von Treuhandkonten oder Verschleierung.
- Konvertieren Sie nur Skripte, die Sie eigen oder die Erlaubnis zur Anpassung haben.
17) Downloads und Kurzanleitung
Sie können auch diesen Konverter verwenden (allerdings nicht getestet):
https://github.com/sledgehamm3r/ESX-QBCore-Converter
28) Nächste Schritte
- Lassen Sie die bridge.lua in Ihre Ressource und beginnen Sie zuerst mit der Konvertierung Ihrer Skripte mit dem höchsten Wert.
- Standardisieren Sie auf ox_lib + ox_inventory + ox_target + oxmysql um rahmenunabhängig zu bleiben.
- Verwenden Sie die Checklisten während der Überprüfung und Freigabe.
Lesen Sie auch SQL- und Kennungsmigration: Steam/Lizenz → CitizenID und Konten → Geld (ESX → QBCore/QBOX)






