Spare 20 % mit WELCOMEAngebote ansehen
Wie man ESX → QBCore richtig migriert

Wie man ESX → QBCore richtig migriert

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

  1. Werkzeuge
    1. GIT und ein separater Zweig für die Migration.
    2. MariaDB oder MySQL 8 mit aktivierten vollständigen Backups.
    3. Ein Staging-Server, der die Produktion spiegelt.
  2. Serverartefakte
    1. FXServer auf denselben Build wie die Produktion aktualisiert.
    2. Abonnieren Basisframework und Standardressourcen.
  3. Bibliotheken, die du verwenden wirst
    1. oxmysql für die Datenbank.
    2. ox_lib für Rückrufe, UI-Helfer und Utility-Wrapper.

Schritt 1. Erstelle einen Plan und einen Rollback-Punkt

  1. Produktionsänderungen einfrieren. Neue Skriptinstallationen und Datenbankschreibvorgänge stoppen, die zum Testen nicht erforderlich sind.
  2. Sichere dein gesamtes Datenbank-Dump als benannte Momentaufnahme.
  3. Verzweige dein Server-Repository und erstelle ein dediziertes Migration von ESX zu QBCore Zweig.
  4. 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

  1. Deploye eine frische QBCore-Basis auf die Staging-Umgebung.
  2. Behalte nur die wesentlichen Funktionen aktiviert. Deaktiviere Jobs, Inventare und benutzerdefinierte Skripte bis nach der Datenbankmigration.
  3. Installiere und starte diese Ressourcen zuerst
    1. qb-kern
    2. qb-Fahrzeuge oder deine bevorzugten Ersatzteile
    3. oxmysql
    4. ox_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

  1. Bevorzugen Abfrage.warten, Skalar.warten, Und vorbereiten.warten für einen sauberen Durchfluss.
  2. 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-TabelleSchlüsselspalteQBCore-TabelleSchlüsselspalteHinweise
BenutzerKennungSpielerBürger-IDKonvertiere Bezeichner und erstelle Bürger-ID für jede Zeile
eigene_FahrzeugeEigentümerSpielerfahrzeugeBürger-IDKonvertiere Plattengehäuse und JSON-Payloads
DatenspeicherdatenEigentümerSpielermetadatenBürger-IDWenn du JSON speicherst, verschmelze vorsichtig
Addon-KontodatenEigentümerSpielerkontenBürger-IDOrdne Kontonamen QBCore-Banking oder Bargeld zu
Addon_InventarartikelEigentümerSpielerinventareBürger-IDWenn 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

  1. Erstelle ein Bürger-ID für jeden Spieler.
  2. Verlinke Legacy-Identifikatoren mit dem neuen Datensatz.
  3. 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.

  1. Artikelzusätze einfrieren.
  2. Exportiere die Artikel-Hauptliste.
  3. Ordne die Namen der Artikel eins zu eins zu.
  4. 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

  1. Komponententests
    1. Teste die Identifikator-Suchfunktionen für eine zufällige Gruppe von Spielern.
    2. Teste Geldtransfers, Jobwechsel und Fahrzeugbesitz.
  2. Gameplay-Tests
    1. Erzeuge Spieler mit alten ESX-Identifikatoren und bestätige die automatische Zuordnung.
    2. Führe einen Polizeidienstablauf, einen Ladenraub und einen Fahrzeugkauf aus.
  3. Leistungstests
    1. Verwenden resmon um CPU und Speicher zu überwachen.
    2. Bestätige, dass die DB-Abfrage-Zählungen nach der oxmysql-Umstellung gesunken sind.
  4. Rollout-Plan
    1. Verschiebe die Staging-DB während eines Wartungsfensters in die Produktion.
    2. Kündige eine 60-minütige Downtime an.
    3. Überwache die Protokolle auf fehlende Identifikatoren und Fremdschlüssel-Fehler.

Fehlerbehebung

  1. Doppelte Bürger
    1. Ursache: Die Migration wird zweimal ausgeführt.
    2. Reparieren. Erzwinge eindeutige Schlüssel auf Bürger-ID und verwenden INSERT IGNORE während der Aussaat.
  2. Fehlende Fahrzeuge
    1. Ursache: Eigentümerschlüssel stimmt nicht überein zwischen owned_vehicles.owner Und legacy_identifier_map.license.
    2. Reparieren. Normalisiere Besitzerwerte und führe den Fahrzeugeinzug für die betroffenen Nummernschilder erneut aus.
  3. Spieler spawnen ohne Inventar
    1. Ursache: Bestandsmigration übersprungen.
    2. Repariere. Baue die Inventarzuordnung neu auf und importiere sie erneut.
  4. Skripte schlagen fehl mit MySQL.Async nicht gefunden
    1. Ursache: Das Skript hängt immer noch von mysql-async ab.
    2. Repariere. Ersetze die Aufrufe durch oxmysql und entferne mysql-async vom Server.

Checkliste für die Umstellung

  1. Sichere die Produktionsdatenbank mit einem Zeitstempel.
  2. Stoppe den Server und sperre Spielerbeitritte.
  3. Stelle den finalen Staging-Dump in der Produktion wieder her.
  4. Installiere QBCore-Build mit qb-kern, oxmysql, ox_lib zuerst in der Reihenfolge der Gewährleistung.
  5. Führe das Identifikator-Sampling-Skript einmal aus.
  6. Aktiviere konvertierte Skripte nur, wenn ihre Abfragen auf oxmysql sind.
  7. Öffne den Server wieder und beobachte die Logs für 30 Minuten.
  8. 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

  1. Stabile Spieleraufzeichnungen eingegeben von Bürger-ID.
  2. Eine saubere Oxmysql-Schicht mit vorbereiteten Anweisungen und Wartezeiten.
  3. ESX-Code mithilfe von ox_lib-Rückrufen und -Dienstprogrammen auf QBCore portiert.
  4. Ein versionierter Plan, den du für zukünftige Server wiederholen kannst.

  1. Framework-Konvertierungs-Hub. https://fivemx.com/framework-conversion/
  2. MySQL Async-zu-Oxmysql-Anleitung. https://fivemx.com/mysql-async-to-oxmysql/
  3. Migration von SQL-Kennungen. https://fivemx.com/sql-identifiers-migration/
  4. Adaptermuster für Skriptports. https://fivemx.com/adapter-patterns/
  5. Schnellstart zur QBCore-Installation. https://fivemx.com/how-to-install-qbcore/
  6. Checkliste zur Skriptkonvertierung. https://fivemx.com/converting-fivem-scripts/
  7. QBOX mit Ox-Stack-Übersicht. https://fivemx.com/qbox-ox-stack/
  8. Resmon und Leistung. https://fivemx.com/how-to-use-resmon-in-fivem-optimize-resources/

Externe Referenzen

  1. QBCore-Framework
  2. Oxmysql-Dokumentation.
  3. ox_lib-Dokumentation.
  4. CFX.re-Kennungsreferenz.