So migrierst du ESX → QBCore richtig
Lerne mit unserer Schritt-für-Schritt-Anleitung, wie du migrierst. Enthält stabile Identifikatoren, oxmysql-Abfragen und ox_lib. Vollständiges Tutorial für 2026.

Du möchtest einen sauberen Wechsel von ESX zu QBCore ohne Datenverlust oder den Ausfall von Kernsystemen. Folge diesem Plan. Du wirst am Ende stabile Identifikatoren, oxmysql-Abfragen und ox_lib-basierten Code haben.
Dieser Guide ist Teil unseres vollständigen FiveM-Frameworks-Guides, in dem wir ESX, QBCore und QBOX ausführlich vergleichen und dir helfen, das richtige zu wählen.
Ziel: Deinen Server mit minimalen Ausfallzeiten von ESX zu QBCore zu migrieren.
Überblick

- Voraussetzungen
- Schritt 1. Einen Plan und einen Rollback-Punkt erstellen
- Schritt 2. Eine saubere QBCore-Basis aufbauen
- Schritt 3. mysql-async durch oxmysql ersetzen
Voraussetzungen
- Tools
- GIT und ein separater Branch für die Migration.
- MariaDB oder MySQL 8 mit aktivierten vollständigen Backups.
- Ein Staging-Server, der die Produktion spiegelt.
- Server-Artifacts
- FXServer auf denselben Build wie Produktion aktualisiert.
- QBCore Basis-Framework und Standard-Ressourcen.
- Bibliotheken, die du verwenden wirst
-
oxmysqlfür die Datenbank. -
ox_libfür Callbacks, UI-Helfer und Utility-Wrapper.
Schritt 1. Einen Plan und einen Rollback-Punkt erstellen
- Produktionsänderungen einfrieren. Stoppt neue Skript-Installationen und DB-Schreibvorgänge, die nicht zum Testen erforderlich sind.
- Deine gesamte Datenbank als benannten Snapshot sichern.
- Dein Server-Repository branchen und einen dedizierten
migrate-esx-to-qbcore-Branch erstellen. - Ein Runbook schreiben. Enthält Befehle zum Starten und Stoppen des Staging-Servers, zur DB-Wiederherstellung und zur Ausführung von Health-Checks.
Schritt 2. Eine saubere QBCore-Basis aufbauen
- Eine frische QBCore-Basis auf Staging deployen.
- Nur das Wesentliche aktiviert lassen. Jobs, Inventare und benutzerdefinierte Skripte deaktivieren, bis nach der DB-Migration.
- Diese Ressourcen zuerst installieren und starten
-
qb-core -
qb-vehiclesoder deine bevorzugten Ersetzungen -
oxmysql -
ox_lib
Schritt 3. mysql-async durch oxmysql ersetzen
Wenn verbleibende ESX-Skripte noch
MySQL.Asyncverwenden, konvertiere die Aufrufe zu oxmysql. Verwende einfaches Suchen und Ersetzen mit Überprüfung.Häufige Konvertierungen
-
-
-- ESX mysql-async
MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @id', {['@id'] = identifier}, function(rows)
-- ...
end)
-- QBCore oxmysql
local rows = MySQL.query.await('SELECT * FROM players WHERE citizenid = ?', { citizenid })
-- rows ist eine Lua-Tabelle; nil und Längenprüfungen direkt behandeln
-- ESX scalar Beispiel
MySQL.Async.fetchScalar('SELECT COUNT(1) FROM owned_vehicles', {}, function(count)
-- ...
end)
-- oxmysql scalar
local count = MySQL.scalar.await('SELECT COUNT(1) FROM player_vehicles')
-- ESX insert
MySQL.Async.execute('INSERT INTO addon_account VALUES (@owner, @name, @money)', {
['@owner'] = identifier, ['@name'] = name, ['@money'] = money
})
-- oxmysql insert
MySQL.prepare.await('INSERT INTO player_accounts (citizenid, name, amount) VALUES (?, ?, ?)', { citizenid, name, amount })
Hinweise
- Bevorzuge
query.await,scalar.awaitundprepare.awaitfür sauberen Ablauf. - Verwende prepared statements für Schreibvorgänge.
Schritt 4. ESX-Datenstrukturen auf QBCore mappen
| Du wirst Spieleridentitäten und Entities migrieren. Verwende diese Referenz zum Mappen von Tabellen. | ESX-Tabelle | Schlüsselspalte | QBCore-Tabelle |
|---|---|---|---|
| Schlüsselspalte | Hinweise | users | identifier |
players | citizenid | Identifikatoren konvertieren und citizenid für jede Zeile erstellen | owned_vehicles |
owner | player_vehicles | citizenid | Kennzeichenformatierung und JSON-Payloads konvertieren |
datastore_data | owner | player_metadata | citizenid |
| Wenn du JSON speicherst, sorgfältig zusammenführen | addon_account_data | owner | player_accounts |
citizenid | Kontonamen auf QBCore-Banking oder Cash mappen | addon_inventory_items | owner |
player_inventories | citizenid | Wenn du zu ox_inventory wechselst, separat migrieren | Du kannst benutzerdefinierte Tabellen behalten. Passe nur Fremdschlüssel an, die ESX-Identifikatoren referenzieren. |
Schritt 5. Identifikatoren stabilisieren
ESX speichert oft einen CFX-Identifikator wie `license:xxxx` oder historisch `steam:xxxx`. QBCore verwendet `citizenid` als stabilen Spielerschlüssel und behält die Laufzeit-Identifikatoren nur zur Authentifizierung.
Du wirst
- Eine
citizenidfür jeden Spieler erstellen. - Legacy-Identifikatoren mit dem neuen Datensatz verknüpfen.
- Eine Lookup-Tabelle für Support und Audits führen.
SQL-Bootstrap
Führe dies auf einer Kopie deiner ESX-Datenbank aus, um QBCore-Tabellen vorzubereiten.
-- 1) players-Tabelle erstellen, falls fehlend. An dein QBCore-Schema anpassen.
CREATE TABLE IF NOT EXISTS players (
citizenid VARCHAR(11) PRIMARY KEY,
license VARCHAR(64) UNIQUE,
identifiers JSON NOT NULL,
name VARCHAR(64),
charinfo JSON NOT NULL, metadata JSON NOT NULL, money JSON NOT NULL, job JSON NOT NULL,
position VARCHAR(128) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2) Eine Hilfsfunktion in SQL mit einem deterministischen Generator wäre komplex.
-- Stattdessen das Mapping in einer separaten Tabelle vorbereiten und citizenid in Lua generieren.
CREATE TABLE IF NOT EXISTS legacy_identifier_map (
license VARCHAR(64) PRIMARY KEY,
steam VARCHAR(64) NULL,
fivem VARCHAR(64) NULL,
discord VARCHAR(64) NULL,
xbl VARCHAR(64) NULL,
liveid VARCHAR(64) NULL,
citizenid VARCHAR(11) UNIQUE
);
-- 3) Das Mapping aus ESX-Users befüllen
INSERT INTO legacy_identifier_map (license)
SELECT DISTINCT REPLACE(identifier, 'identifier:', '')
FROM users
WHERE identifier LIKE 'license:%' OR identifier LIKE 'steam:%';
citizenid generieren und Spieler in Lua einfügen
Einmal auf Staging ausführen. Zuerst sichern.
-- server/migrate_identifiers.lua
local QBCore = exports['qb-core']:GetCoreObject()
local function generateCitizenId()
local charset = {}
for c = 65, 90 do table.insert(charset, string.char(c)) end
for n = 48, 57 do table.insert(charset, string.char(n)) end
math.randomseed(GetGameTimer())
local id = {}
for i = 1, 11 do id[i] = charset[math.random(1, #charset)] end
return table.concat(id)
end
local rows = MySQL.query.await('SELECT license FROM legacy_identifier_map WHERE citizenid IS NULL')
for _, r in ipairs(rows) do
local citizenid = generateCitizenId()
MySQL.prepare.await('UPDATE legacy_identifier_map SET citizenid = ? WHERE license = ?', { citizenid, r.license })
end
-- Spieler aus ESX-Users aufbauen
local users = MySQL.query.await([[SELECT u.identifier, u.firstname, u.lastname, u.dateofbirth, u.sex, u.height
FROM users u]])
for _, u in ipairs(users) do
local license = u.identifier
local map = MySQL.single.await('SELECT citizenid FROM legacy_identifier_map WHERE license = ?', { license })
if map and map.citizenid then
local name = string.format('%s %s', u.firstname or 'John', u.lastname or 'Doe')
local charinfo = json.encode({ firstname = u.firstname, lastname = u.lastname, birthdate = u.dateofbirth, gender = u.sex, height = u.height })
local metadata = json.encode({ hunger = 100, thirst = 100 })
local money = json.encode({ cash = 0, bank = 0, crypto = 0 })
local job = json.encode({ name = 'unemployed', label = 'Unemployed', grade = { name = '0', level = 0 }})
MySQL.prepare.await('INSERT IGNORE INTO players (citizenid, license, identifiers, name, charinfo, metadata, money, job) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', {
map.citizenid, license,
json.encode({ license = license }),
name, charinfo, metadata, money, job
})
end
end
print('Identifikator-Migration abgeschlossen')
Fahrzeuge übertragen
INSERT IGNORE INTO player_vehicles (citizenid, plate, vehicle, garage, state)
SELECT m.citizenid,
UPPER(JSON_UNQUOTE(JSON_EXTRACT(v.vehicle, '$.plate'))),
v.vehicle,
'legion',
1 FROM owned_vehicles v
JOIN legacy_identifier_map m ON m.license = v.owner;
Zufällige Stichproben im Spiel validieren. Kennzeichenformate und Garagen überprüfen.
Schritt 6. ESX-Code mit ox_lib auf QBCore portieren
Ersetze die ESX-Runtime-API durch QBCore-Äquivalente. Verwende ox_lib für Callbacks und Benachrichtigungen.
Spielerobjekt
-- ESX
local xPlayer = ESX.GetPlayerFromId(src)
xPlayer.addMoney(100)
-- QBCore
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.AddMoney('cash', 100)
Jobs
-- ESX Job-Prüfung
if xPlayer.getJob().name == 'police' then
-- ...
end
-- QBCore Job-Prüfung
local job = Player.PlayerData.job
if job and job.name == 'police' then
-- ...
end
Callbacks und UI
-- ESX Server-Callback
ESX.RegisterServerCallback('resource:getData', function(source, cb)
cb({ ok = true })
end)
-- ox_lib Callback
lib.callback.register('resource:getData', function(source)
return { ok = true }
end)
-- Benachrichtigung
lib.notify(source, { title = 'Job', description = 'Beförderung gewährt', type = 'success' })
Befehle
-- ESX
RegisterCommand('pay', function(src, args)
local amount = tonumber(args[1]) or 0
xPlayer.removeMoney(amount)
end)
-- QBCore mit Berechtigungen
QBCore.Commands.Add('pay', 'Bargeld bezahlen', {{name = 'amount', help = 'Betrag'}}, false, function(src, args)
local amount = tonumber(args[1]) or 0
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.RemoveMoney('cash', amount)
end)
Schritt 7. Inventar und Items
Wenn du von es_extended-Inventaren zu qb-inventory oder ox_inventory wechselst, behandle dies als separate Teil-Migration.
- Item-Hinzufügungen einfrieren.
- Die Item-Masterliste exportieren.
- Item-Namen eins zu eins mappen.
- Spieler-Inventare in Batches migrieren. Stack-Größen und Gewichte validieren.
Beispiel Item-Mapping CSV
esx_name,qb_name,notes bread,bread, water,water, lockpick,lockpick,
Schritt 8. Testen und Ausrollen
- Unit-Tests
- Identifikator-Lookups für eine zufällige Auswahl von Spielern testen.
- Geldtransfers, Job-Änderungen und Fahrzeugbesitz testen.
- Gameplay-Tests
- Spieler mit alten ESX-Identifikatoren spawnen und automatisches Mapping bestätigen.
- Einen Polizeidienst-Flow, einen Ladenraub und einen Fahrzeugkauf durchführen.
- Performance-Tests
resmonverwenden, um CPU und Speicher zu beobachten.- Bestätigen, dass die DB-Abfrageanzahl nach der oxmysql-Konvertierung gesunken ist.
- Rollout-Plan
-
Staging-DB während eines Wartungsfensters in die Produktion verschieben.
-
60-minütige Ausfallzeit ankündigen.
-
Logs auf fehlende Identifikatoren und Fremdschlüsselfehler überwachen.
Fehlerbehebung
- Duplizierte Citizens
- Ursache: Migration wurde zweimal ausgeführt.
- Fix: Eindeutige Schlüssel auf
citizeniderzwingen undINSERT IGNOREbeim Seeding verwenden. - Fehlende Fahrzeuge
- Ursache: Owner-Schlüssel-Mismatch zwischen
owned_vehicles.ownerundlegacy_identifier_map.license. - Fix: Owner-Werte normalisieren und den Fahrzeug-Insert für die betroffenen Kennzeichen erneut ausführen.
- Spieler spawnen ohne Inventar
- Ursache: Inventar-Migration übersprungen.
- Fix: Inventar-Mapping neu aufbauen und reimportieren.
- Skripte schlagen mit
MySQL.Asyncnicht gefunden fehl-
Ursache: Skript hängt noch von mysql-async ab.
-
Fix: Aufrufe durch oxmysql ersetzen und mysql-async vom Server entfernen.
Cutover-Checkliste
- Produktionsdatenbank mit einem Zeitstempel sichern.
- Server stoppen und Spieler-Joins sperren.
- Den finalen Staging-Dump in die Produktion zurückspielen.
- QBCore-Build mit
qb-core,oxmysql,ox_libzuerst in der Ensure-Reihenfolge deployen. - Das Identifikator-Seeding-Skript einmal ausführen.
- Konvertierte Skripte nur aktivieren, wenn ihre Abfragen auf oxmysql sind.
- Server wieder öffnen und Logs 30 Minuten lang beobachten.
- Rollback-Plan posten, wenn kritische Fehler auftreten.
Anhang A. Beispiel-fxmanifest für Migrationshelfer
fx_version 'cerulean'
-
- Ursache: Owner-Schlüssel-Mismatch zwischen
- Duplizierte Citizens
-
game 'gta5'
lua54 'yes'
server_scripts {
'@oxmysql/lib/MySQL.lua',
'@ox_lib/init.lua',
'server/migrate_identifiers.lua'
}
Anhang B. Sichere JSON-Helfer
local function safeDecode(jsonStr, fallback)
if type(jsonStr) ~= 'string' or jsonStr == '' then return fallback end
local ok, result = pcall(json.decode, jsonStr)
if not ok then return fallback end
return result
end
Was du erreicht hast
- Stabile Spielerdatensätze, die durch
citizenidgekennzeichnet sind. - Eine saubere oxmysql-Schicht mit prepared statements und awaits.
- ESX-Code auf QBCore portiert mit ox_lib-Callbacks und Utilities.
- Einen versionierten Plan, den du für zukünftige Server wiederholen kannst.
Nützliche Links auf deiner Seite
- Framework-Konvertierungs-Hub. https://fivemx.com/blog/converting-fivem-scripts
- MySQL Async zu oxmysql Guide. https://fivemx.com/blog/mysql-async-to-oxmysql
- SQL-Identifikatoren-Migration. https://fivemx.com/blog/sql-identifiers-migration
- Adapter-Patterns für Skript-Ports. https://fivemx.com/blog/adapter-patterns
- QBCore-Install-Schnellstart. https://fivemx.com/blog/how-to-install-qbcore
- Skript-Konvertierungs-Checkliste. https://fivemx.com/blog/converting-fivem-scripts
- QBOX mit ox-Stack-Übersicht. https://fivemx.com/blog/qbox-ox-stack
- Resmon und Performance. https://fivemx.com/blog/how-to-use-resmon-in-fivem-optimize-resources
Externe Referenzen
Frequently Asked Questions
Was muss ich beachten, wenn ich während der Migration von ESX zu QBCore auf Probleme stoße?
Bei Problemen während der Migration ist es entscheidend, einen klaren Kopf zu bewahren und systematisch vorzugehen. Nutze deine erstellten Backups, um zum letzten stabilen Zustand zurückzukehren. Überprüfe die Konsolen auf Fehler und nutze Debugging-Tools, um die Ursache zu finden. Oftmals sind es inkorrekte Datenzuordnungen, fehlerhafte SQL-Abfragen oder Kompatibilitätsprobleme mit einzelnen Ressourcen. Dokumentiere jeden Schritt und Fehler, um daraus zu lernen und zukünftige Migrationen zu vereinfachen. Es kann auch hilfreich sein, die Community oder erfahrene Entwickler um Rat zu fragen.
Wie vermeide ich Datenverluste bei der Übertragung von ESX-Datenstrukturen zu QBCore?
Um Datenverluste zu minimieren, plane die Datenübertragung sorgfältig. Identifiziere zunächst alle relevanten Datenfelder in ESX und ordne sie den entsprechenden Feldern in QBCore zu. Nutze SQL-Skripte, um die Daten zu transformieren und in die QBCore-Datenbank zu importieren. Vor dem eigentlichen Import solltest du eine Testmigration mit einer Kopie deiner Datenbank durchführen, um Fehler zu erkennen und zu beheben. Überprüfe nach der Migration stichprobenartig, ob alle Daten korrekt übertragen wurden und ob keine Informationen fehlen. Achte besonders auf komplexe Datenstrukturen wie Inventare oder Fahrzeugbesitz.
Was ist der Nutzen von `oxmysql` gegenüber `mysql-async` während der Migration?
`oxmysql` bietet im Vergleich zu `mysql-async` einige Vorteile, besonders hinsichtlich Performance und Sicherheit. `oxmysql` ist in der Regel schneller, da es optimierte Abfragemechanismen nutzt und weniger Overhead verursacht. Dies führt zu geringeren Latenzzeiten und einer besseren Serverperformance. Zudem bietet `oxmysql` verbesserte Sicherheitsfunktionen gegen SQL-Injections und andere Angriffe. Durch die Umstellung auf `oxmysql` stellst du sicher, dass dein Server sowohl performanter als auch sicherer wird, was besonders wichtig ist, wenn du von ESX, das oft ältere Datenbankfunktionen verwendet, zu QBCore migrierst.
Wie kann ich sicherstellen, dass die Identifikatoren (wie z.B. `citizenid`) nach der Migration von ESX zu QBCore stabil bleiben?
Stabile Identifikatoren sind entscheidend, um Probleme mit bestehenden Daten wie Fahrzeugbesitz oder Immobilien zu vermeiden. Generiere `citizenid`'s im Voraus und speichere diese in einem separaten Feld in der Datenbank. Ein SQL-Bootstrap kann verwendet werden, um existierende ESX-Spielerdaten mit neuen, eindeutigen `citizenid`'s zu verknüpfen, ohne bestehende Verknüpfungen zu brechen. Nach der Generierung stelle sicher, dass sowohl die Lua-Skripte als auch die Datenbankabfragen die neuen `citizenid`'s verwenden und nicht mehr auf alte, möglicherweise inkonsistente Identifikatoren verweisen. Teste die Login-Funktionalität und die Zuweisung von Gegenständen und Fahrzeugen, um die korrekte Funktion zu gewährleisten.
Was ist So migrierst du ESX → QBCore richtig?
Du möchtest einen sauberen Wechsel von ESX zu QBCore ohne Datenverlust oder den Ausfall von Kernsystemen. Folge diesem Plan. Du wirst am Ende stabile Identifikatoren, oxmysql-Abfragen und ox lib-basierten Code haben.
