Du willst einen sauberen Wechsel von ESX zu QBCore ohne Datenverlust oder Systemausfälle. Folge diesem Plan. Du wirst mit stabilen Identifikatoren, oxmysql-Abfragen und ox_lib-gestützten Code enden.
Ziel: Verschiebe deinen Server von ESX zu QBCore mit minimaler Ausfallzeit.
Voraussetzungen
- Werkzeuge
- GIT und ein separater Zweig für die Migration.
- MariaDB oder MySQL 8 mit aktivierten vollständigen Backups.
- Ein Staging-Server, der die Produktion spiegelt.
- Serverartefakte
- FXServer auf denselben Build wie die Produktion aktualisiert.
- Abonnieren Basisframework und Standardressourcen.
- Bibliotheken, die du verwenden wirst
oxmysqlfür die Datenbank.ox_libfür Rückrufe, UI-Helfer und Utility-Wrapper.
Schritt 1. Erstelle einen Plan und einen Rollback-Punkt
- Produktionsänderungen einfrieren. Neue Skriptinstallationen und Datenbankschreibvorgänge stoppen, die zum Testen nicht erforderlich sind.
- Sichere dein gesamtes Datenbank-Dump als benannte Momentaufnahme.
- Verzweige dein Server-Repository und erstelle ein dediziertes
Migration von ESX zu QBCoreZweig. - Schreibe ein Runbook. Füge Befehle hinzu, um den Staging-Server zu starten und zu stoppen, die Datenbank wiederherzustellen und Gesundheitsprüfungen durchzuführen.
Schritt 2. Baue eine saubere QBCore Basis
- Deploye eine frische QBCore-Basis auf die Staging-Umgebung.
- Behalte nur die wesentlichen Funktionen aktiviert. Deaktiviere Jobs, Inventare und benutzerdefinierte Skripte bis nach der Datenbankmigration.
- Installiere und starte diese Ressourcen zuerst
qb-kernqb-Fahrzeugeoder deine bevorzugten Ersatzteileoxmysqlox_lib
Schritt 3. Ersetze mysql-async durch oxmysql
Wenn noch verbleibende ESX-Skripte MySQL.Async, konvertiere die Aufrufe zu oxmysql. Verwende einfache Suche und Ersetzung 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; Null- und Längenprüfungen direkt durchführen
-- ESX Skalarbeispiel
MySQL.Async.fetchScalar('SELECT COUNT(1) FROM owned_vehicles', {}, function(count)
-- ...
end)
-- oxmysql skalare lokale Anzahl = MySQL.scalar.await('SELECT COUNT(1) FROM player_vehicles')
-- ESX Einfügen
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
- Bevorzugen
Abfrage.warten,Skalar.warten, Undvorbereiten.wartenfür einen sauberen Durchfluss. - Verwende vorbereitete Anweisungen für Schreibvorgänge.
Schritt 4. ESX-Datenstrukturen QBCore zuordnen
Du wirst Spieleridentitäten und besessene Entitäten verschieben. Verwende diese Referenz, um Tabellen abzubilden.
| ESX-Tabelle | Schlüsselspalte | QBCore-Tabelle | Schlüsselspalte | Hinweise |
|---|---|---|---|---|
Benutzer | Kennung | Spieler | Bürger-ID | Konvertiere Bezeichner und erstelle Bürger-ID für jede Zeile |
eigene_Fahrzeuge | Eigentümer | Spielerfahrzeuge | Bürger-ID | Konvertiere Plattengehäuse und JSON-Payloads |
Datenspeicherdaten | Eigentümer | Spielermetadaten | Bürger-ID | Wenn du JSON speicherst, verschmelze vorsichtig |
Addon-Kontodaten | Eigentümer | Spielerkonten | Bürger-ID | Ordne Kontonamen QBCore-Banking oder Bargeld zu |
Addon_Inventarartikel | Eigentümer | Spielerinventare | Bürger-ID | Wenn du zu ox_inventory, separat migrieren |
Du kannst benutzerdefinierte Tabellen behalten. Passe nur die Fremdschlüssel an, die auf ESX-Identifikatoren verweisen.
Schritt 5. Kennungen stabilisieren
ESX speichert häufig eine CFX-Kennung wie Lizenz: xxxx oder historisch Dampf:xxxxQBCore verwendet Bürger-ID als stabiler Player-Schlüssel und behält die Laufzeitkennungen nur zur Authentifizierung.
Du wirst
- Erstelle ein
Bürger-IDfür jeden Spieler. - Verlinke Legacy-Identifikatoren mit dem neuen Datensatz.
- Behalte eine Nachschlagetabelle für den Support und Audits bei.
SQL-Bootstrap
Führe dies auf einer Kopie deiner ESX-Datenbank aus, um QBCore-Tabellen vorzubereiten.
-- 1) Erstelle die Spielertabelle, falls sie fehlt. Passe sie an dein QBCore-Schema an. 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 zu erstellen, wäre komplex. -- Stattdessen wird die Zuordnung in einer separaten Tabelle gestagert und die citizenid in Lua generiert. 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) Befülle die Zuordnung von ESX-Benutzern 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 der Staging-Plattform 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
-- Build players from ESX users
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('Identifier migration finished')
Eigene Fahrzeuge bewegen
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;
Überprüfe zufällige Stichproben im Spiel. Verifiziere Plattenformate und Garagen.
Schritt 6. Portiere ESX Code zu QBCore mit ox_lib
Ersetze die ESX-Runtime-API durch QBCore-Äquivalente. Verwende ox_lib für Callbacks und Benachrichtigungen.
Spielerobjekt
-- ESX lokaler xPlayer = ESX.GetPlayerFromId(src) xPlayer.addMoney(100)
-- QBCore lokaler Player = QBCore.Functions.GetPlayer(src) Player.Functions.AddMoney('cash', 100)
Jobs
-- ESX Job-Überprüfung if xPlayer.getJob().name == 'police' then -- ... end
-- QBCore Job-Überprüfung local job = Player.PlayerData.job if job and job.name == 'police' then -- ... end
Rückrufe und Benutzeroberfläche
-- 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(Quelle, { Titel = 'Job', Beschreibung = 'Beförderung gewährt', Typ = 'Erfolg' })
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', 'Pay cash', {{name = 'amount', help = 'Amount'}}, 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 Artikel
Wenn du von es_extended Vorräte zu qb-inventar oder ox_inventory, behandle dies als eine separate Teil-Migration.
- Artikelzusätze einfrieren.
- Exportiere die Artikel-Hauptliste.
- Ordne die Namen der Artikel eins zu eins zu.
- Migriere Spielerinventare in Batches. Überprüfe Stapelgrößen und Gewichte.
Beispiel für eine CSV-Elementzuordnung
esx_name,qb_name,Notizen Brot,Brot, Wasser,Wasser,Dietrich,Dietrich,
Schritt 8. Testen und Ausrollen
- Komponententests
- Teste die Identifikator-Suchfunktionen für eine zufällige Gruppe von Spielern.
- Teste Geldtransfers, Jobwechsel und Fahrzeugbesitz.
- Gameplay-Tests
- Erzeuge Spieler mit alten ESX-Identifikatoren und bestätige die automatische Zuordnung.
- Führe einen Polizeidienstablauf, einen Ladenraub und einen Fahrzeugkauf aus.
- Leistungstests
- Verwenden
resmonum CPU und Speicher zu überwachen. - Bestätige, dass die DB-Abfrage-Zählungen nach der oxmysql-Umstellung gesunken sind.
- Verwenden
- Rollout-Plan
- Verschiebe die Staging-DB während eines Wartungsfensters in die Produktion.
- Kündige eine 60-minütige Downtime an.
- Überwache die Protokolle auf fehlende Identifikatoren und Fremdschlüssel-Fehler.
Fehlerbehebung
- Doppelte Bürger
- Ursache: Die Migration wird zweimal ausgeführt.
- Reparieren. Erzwinge eindeutige Schlüssel auf
Bürger-IDund verwendenINSERT IGNOREwährend der Aussaat.
- Fehlende Fahrzeuge
- Ursache: Eigentümerschlüssel stimmt nicht überein zwischen
owned_vehicles.ownerUndlegacy_identifier_map.license. - Reparieren. Normalisiere Besitzerwerte und führe den Fahrzeugeinzug für die betroffenen Nummernschilder erneut aus.
- Ursache: Eigentümerschlüssel stimmt nicht überein zwischen
- Spieler spawnen ohne Inventar
- Ursache: Bestandsmigration übersprungen.
- Repariere. Baue die Inventarzuordnung neu auf und importiere sie erneut.
- Skripte schlagen fehl mit
MySQL.Asyncnicht gefunden- Ursache: Das Skript hängt immer noch von mysql-async ab.
- Repariere. Ersetze die Aufrufe durch oxmysql und entferne mysql-async vom Server.
Checkliste für die Umstellung
- Sichere die Produktionsdatenbank mit einem Zeitstempel.
- Stoppe den Server und sperre Spielerbeitritte.
- Stelle den finalen Staging-Dump in der Produktion wieder her.
- Installiere QBCore-Build mit
qb-kern,oxmysql,ox_libzuerst in der Reihenfolge der Gewährleistung. - Führe das Identifikator-Sampling-Skript einmal aus.
- Aktiviere konvertierte Skripte nur, wenn ihre Abfragen auf oxmysql sind.
- Öffne den Server wieder und beobachte die Logs für 30 Minuten.
- Veröffentliche einen Rückrollplan, wenn kritische Fehler auftreten.
Anhang A. Beispiel fxmanifest für Migrationshelfer
fx_version 'cerulean'
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 Spieleraufzeichnungen eingegeben von
Bürger-ID. - Eine saubere Oxmysql-Schicht mit vorbereiteten Anweisungen und Wartezeiten.
- ESX-Code mithilfe von ox_lib-Rückrufen und -Dienstprogrammen auf QBCore portiert.
- Ein versionierter Plan, den du für zukünftige Server wiederholen kannst.
Nützliche Links innerhalb deiner Seite
- Framework-Konvertierungs-Hub. https://fivemx.com/framework-conversion/
- MySQL Async-zu-Oxmysql-Anleitung. https://fivemx.com/mysql-async-to-oxmysql/
- Migration von SQL-Kennungen. https://fivemx.com/sql-identifiers-migration/
- Adaptermuster für Skriptports. https://fivemx.com/adapter-patterns/
- Schnellstart zur QBCore-Installation. https://fivemx.com/how-to-install-qbcore/
- Checkliste zur Skriptkonvertierung. https://fivemx.com/converting-fivem-scripts/
- QBOX mit Ox-Stack-Übersicht. https://fivemx.com/qbox-ox-stack/
- Resmon und Leistung. https://fivemx.com/how-to-use-resmon-in-fivem-optimize-resources/