SQL & Identifiers Migration: steam/license → citize&#…
Anwendungsfall: Anwendungsfall: Sie wechseln von ESX zu QBCore oder QBOX (qbx_core) und benötigen eine saubere, prüffähige Migration von Spielerkennungen und -salden. Dieser Leitfaden bietet Ihnen produktionsbereites SQL, einen reversiblen Plan und Validierungsschritte.
Verwandte Artikel:
- Adaptermuster: ESX↔QBCore↔QBOX (Exporte, Ereignisse und Player-Modelle) — https://fivemx.com/adapter-patterns/
- Konvertieren von FiveM-Skripten – ESX, QBCore, QBOX (Framework-Handbuch) — https://fivemx.com/converting-fivem-scripts/ (Säule)
Was ändert sich zwischen ESX und QBCore/QBOX
| Thema | ESX (allgemein) | QBCore / QBOX (allgemein) |
|---|---|---|
| Primärer Spielerschlüssel | Kennung (z.B, Lizenz:xxx oder Vermächtnis Dampf:xxx) | Bürger-ID (servergeneriertes Token) |
| Alt-Kennungen | Benutzerkennung, manchmal eine separate Kennungen Tisch | Spalten wie Lizenz, Dampf, fivem nebenbei gespeichert Bürger-ID |
| Geldmodell | Getrennte Konten (Bargeld/Bank/Schwarzgeld) über Benutzerkonten (JSON) oder Benutzerkonten Reihen | Einzel Geld JSON auf Spieler (z.B, { "Bargeld": 0, "Bank": 5000 }); optionale zusätzliche Geldbörsen |
| Fahrzeuge | owned_vehicles.owner bezieht sich auf ESX Kennung | Spielerfahrzeuge.Bürger-ID (oder Lizenz auf einigen Gabeln) |
QBOX folgt im Allgemeinen der DB-Form von QB. Behandeln Sie QBOX als „QB-Schema + qbx-Ergänzungen“. Vergleichen Sie immer Ihr Live-Schema.
Goldene Regeln (nicht überspringen)
- Schreibvorgänge einfrieren während der Migration (stoppen Sie den Spielserver und alle externen Bots, die die Datenbank berühren).
- Vollständige Sicherung und ein Dump von Tabellenstrukturen. Speichern Sie beide mit Zeitstempeln.
- Arbeiten in einer Transaktion wenn möglich pro Tabelle; halten Sie die Schritte idempotent.
- Erstellen Sie einen Zebrastreifen (
alte_Kennung→Bürger-ID), die Sie wiederverwenden oder wiederherstellen können.
Ziel, das Sie anstreben (QB/QBOX-Basislinie)
Ein typisches Spieler Tabelle (Spalten variieren je nach Gabel):
-- Überprüfen Sie Ihr aktuelles Schema und passen Sie es an. BESCHREIBEN Sie die Spieler; -- Erwarten Sie Spalten wie: Bürger-ID, Lizenz, Name, Geld, Charinfo, Job, Gang, Metadaten
- Bürger-ID: Primärschlüssel, der in QB/QBOX verwendet wird.
- Lizenz/Steam: Für forensische Zwecke und zum erneuten Verknüpfen aufbewahren.
- Geld (JSON): zB
{"Bargeld":123,"Bank":456}. Einige Server fügen hinzuKrypto,schmutzig, usw.
Schritt 0 – Snapshot und Staging
# MySQL/MariaDB backup mysqldump -u root -p --routines --triggers yourdb > yourdb_$(date +%F_%H%M).sql # Optional: structure‑only snapshot mysqldump -u root -p --no-data yourdb > yourdb_schema_$(date +%F_%H%M).sql
Starten Sie eine Staging-Kopie. Führen Sie dort zuerst alles aus
Schritt 1 – Erstellen Sie die Zebrastreifen Tisch
Wir mappen jeden ESX Kennung zu einem neuen Bürger-ID. Wenn Sie bereits eine Spieler Tabelle mit Bürger-IDs, kehren Sie die Zuordnung um (siehe Vorhandene QB-Spieler Hinweis unten).
-- 1) Crosswalk erstellen. CREATE TABLE IF NOT EXISTS identifier_crosswalk (old_identifier VARCHAR(60) PRIMARY KEY, citizenid VARCHAR(20) NOT NULL, license VARCHAR(60) NULL, steam VARCHAR(60) NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 2) Seed von ESX-Benutzern (passen Sie die Tabellen-/Spaltennamen an Ihre ESX-Variante an) -- Im allgemeinen ESX enthält „users.identifier“ die Lizenz:xxx oder Steam:xxx. INSERT IGNORE INTO identifier_crosswalk (old_identifier, license, steam, citizenid) SELECT u.identifier AS old_identifier, CASE WHEN u.identifier LIKE 'license:%' THEN u.identifier ELSE NULL END AS license, CASE WHEN u.identifier LIKE 'steam:%' THEN u.identifier ELSE NULL END AS steam, UPPER(SUBSTRING(REPLACE(UUID(),'-',''),1,10)) AS citizenid FROM users u; -- 3) Wenn Sie eine separate Tabelle „Kennungen“ haben, führen Sie die bekanntesten Werte zusammen -- Beispiel (optional): Lizenz bevorzugen, wenn verfügbar UPDATE identifier_crosswalk x JOIN ( SELECT i1.identifier AS old_identifier, MAX(CASE WHEN i1.type='license' THEN i1.value END) AS license, MAX(CASE WHEN i1.type='steam' THEN i1.value END) AS steam FROM identifiers i1 GROUP BY i1.identifier ) i ON i.old_identifier = x.old_identifier SET x.license = COALESCE(i.license, x.license), x.steam = COALESCE(i.steam, x.steam); -- 4) Eindeutigkeit und Indizes ALTER TABLE identifier_crosswalk ADD UNIQUE KEY ux_cid (citizenid), ADD KEY ix_license (license), ADD KEY ix_steam (steam);
Vorhandene QB-Spieler? Wenn Sie bereits
SpielerZeilen, erstellen Sie den Zebrastreifen, indem Sie ihreLizenz/DampfUnd bestehendeBürger-IDanstatt neue zu generieren. Ihr Crosswalk darf einem vorhandenen QB-Spieler niemals eine neue Citizen-ID zuweisen.
Schritt 2 – Ziel normalisieren/vorbereiten Spieler Reihen
Erstellen Sie ggf. fehlende Spieler Zeilen basierend auf ESX Benutzer.
-- Stellen Sie sicher, dass „players“ vorhanden ist, und überprüfen Sie zuerst seine Spalten. -- Wir fügen Shells nur für fehlende Bürger ein. INSERT INTO players (citizenid, license, name, money, charinfo, metadata) SELECT x.citizenid, COALESCE(NULLIF(x.license,''), NULLIF(x.steam,'')) AS license_like, COALESCE(u.firstname, '') || ' ' || COALESCE(u.lastname, '') AS name_like, '{"bargeld":0,"bank":0}' AS Geld, JSON_OBJECT( 'Vorname', COALESCE(u.firstname,''), 'Nachname', COALESCE(u.lastname,''), 'Geburtsdatum', COALESCE(u.dateofbirth,''), 'Geschlecht', COALESCE(u.sex,'') ) AS charinfo, JSON_OBJECT('esx_identifier', u.identifier) AS Metadaten VON Benutzern u JOIN identifier_crosswalk x ON x.old_identifier = u.identifier LEFT JOIN Spieler p ON p.citizenid = x.citizenid WHERE p.citizenid IS NULL;
Notiz: Verwenden Sie die Zeichenfolgenverkettung Ihrer SQL-Variante (
CONCATin MySQL) und JSON-Funktionen entsprechend. Für MySQL 5.7 ersetzenJSON_OBJECTmit manuellem Saitenaufbau, falls erforderlich.
MySQL‑sichere Variante:
INSERT INTO players (citizenid, license, name, money, charinfo, metadata) SELECT x.citizenid, COALESCE(NULLIF(x.license,''), NULLIF(x.steam,'')) AS license_like, TRIM(CONCAT(COALESCE(u.firstname,''), ' ', COALESCE(u.lastname,''))) AS name_like, '{"cash":0,"bank":0}' AS money, CONCAT('{', '"firstName":"', REPLACE(COALESCE(u.firstname,''),'"','\"'), '",', '"lastName":"', REPLACE(COALESCE(u.lastname,''),'"','\"'), '",', '"birthdate":"', REPLACE(COALESCE(u.dateofbirth,''),'"','\"'),'",', „Geschlecht“: „“, ERSETZEN (COALESCE (u.Geschlecht, „), „“, „\“ „), „“ „, „}“) ALS Charinfo, CONCAT („{“, „esx_identifier“: „“, ERSETZEN (u.identifier, „“, „\“ „), „“ „, „}“) ALS Metadaten VON Benutzern u JOIN identifier_crosswalk x ON x.old_identifier = u.identifier LEFT JOIN Spieler p ON p.citizenid = x.citizenid WHERE p.citizenid IS NULL;
Schritt 3 – Migrieren Konten → Geld
Es gibt zwei gängige ESX-Muster:
A) ESX speichert Guthaben im Inneren Benutzerkonten JSON
-- Beispiel: users.accounts = '{"bank":5000, "money":750, "black_money":200}' -- 1) Sicheres Extrahieren aus ESX JSON -- Erstellen Sie eine temporäre Ansicht/Tabelle mit analysierten Zahlen CREATE TEMPORARY TABLE esx_balances AS SELECT u.identifier, COALESCE(JSON_EXTRACT(u.accounts, '$.money'), 0) AS esx_cash, COALESCE(JSON_EXTRACT(u.accounts, '$.bank'), 0) AS esx_bank, COALESCE(JSON_EXTRACT(u.accounts, '$.black_money'), 0) AS esx_black FROM users u; -- 2) Zusammenführen in QB/QBOX-Geld JSON -- Entscheiden Sie, wie mit Schwarzgeld verfahren werden soll (siehe Optionen unten) UPDATE players p JOIN identifier_crosswalk x ON x.citizenid = p.citizenid JOIN esx_balances b ON b.identifier = x.old_identifier SET p.money = JSON_OBJECT( 'cash', CAST(b.esx_cash AS UNSIGNED), 'bank', CAST(b.esx_bank AS UNSIGNED) );
Wenn MySQL ohne native JSON-Operationen (oder alte Version): Erstellen Sie JSON-Strings mit CONCAT.
B) ESX speichert Guthaben in Benutzerkonten Reihen
-- Beispiel: user_accounts(Kennung, Konto, Geld) ERSTELLEN SIE EINE VORÜBERGEHENDE TABELLE esx_balances ALS AUSWÄHLEN ua.Kennung, SUMME(FALL, WENN ua.account='Geld' DANN ua.Geld SONST 0 ENDE) ALS esx_cash, SUMME(FALL, WENN ua.account='Bank' DANN ua.Geld SONST 0 ENDE) ALS esx_bank, SUMME(FALL, WENN ua.account='Schwarzgeld' DANN ua.Geld SONST 0 ENDE) ALS esx_black VON user_accounts ua GRUPPE NACH ua.Kennung; UPDATE Spieler p JOIN identifier_crosswalk x ON x.citizenid = p.citizenid JOIN esx_balances b ON b.identifier = x.old_identifier SET p.money = JSON_OBJECT( 'cash', CAST(b.esx_cash AS UNSIGNED), 'bank', CAST(b.esx_bank AS UNSIGNED) );
Handhabung Schwarzgeld (Wählen Sie eine Option)
- Option 1 (empfohlen): Erstellen Sie einen dedizierten Wallet-Schlüssel in QB Money JSON, zB
"schmutzig". - Option 2: Konvertieren Sie stattdessen in Artikel (z. B. markierte Rechnungen) und schreiben Sie den Bestand gut (erfordert Artikelmigration; liegt hier außerhalb des Geltungsbereichs).
- Option 3: Setzen Sie es auf Null (dringend abgeraten, es sei denn, Sie haben einen Wipe angekündigt).
Implementierung von Option 1:
-- Dirty Wallet in JSON hinzufügen (Server, die zusätzliche Wallets unterstützen) UPDATE players p JOIN identifier_crosswalk x ON x.citizenid = p.citizenid JOIN esx_balances b ON b.identifier = x.old_identifier SET p.money = JSON_MERGE_PATCH(p.money, JSON_OBJECT('dirty', CAST(b.esx_black AS UNSIGNED)));
Stellen Sie sicher, dass Ihr Framework/Ihre Ressourcen das zusätzliche Budget tatsächlich berücksichtigen. Andernfalls bevorzugen Sie Option 2.
Schritt 4 – Neuschlüsselung fremder Tabellen, die auf ESX verweisen Kennung
Typische zu reparierende Tabellen:
owned_vehicles.owner→ Karte zuBürger-ID(QB:Spielerfahrzeuge.Bürger-ID)- Alle benutzerdefinierten Tabellen, die
KennungSpalten (Häuser, Abrechnungen, Banden, Geschäfte)
Fahrzeuge (ESX → QB)
-- Wenn Sie ESX „owned_vehicles“ behalten, ändern Sie den Schlüssel owner → citizenid aus Gründen der Vorwärtskompatibilität. ALTER TABLE owned_vehicles ADD COLUMN citizenid VARCHAR(20) NULL; UPDATE owned_vehicles v JOIN identifier_crosswalk x ON x.old_identifier = v.owner SET v.citizenid = x.citizenid WHERE v.citizenid IS NULL; CREATE INDEX ix_ov_cid ON owned_vehicles (citizenid);
**Fahrzeuge in QB's **“ (minimale Felder; an Ihr Schema anpassen):
INSERT IGNORE INTO player_vehicles (citizenid, plate, vehicle, state, garage) SELECT x.citizenid, v.plate, v.vehicle, 0 AS state, 'A' AS garage FROM owned_vehicles v JOIN identifier_crosswalk x ON x.old_identifier = v.owner;
JSON-Feldnamen validieren (
FahrzeuggegenMods/Requisiten) und Spaltenliste mit Ihrem tatsächlichen QB/QBOX-Schema.
Schritt 5 – Einschränkungen, Indizes und Integritätsprüfungen
-- Ensure primary/unique keys ALTER TABLE players ADD UNIQUE KEY ux_players_citizenid (citizenid); -- Optional: keep a quick lookup by license/steam ALTER TABLE players ADD KEY ix_players_license (license); -- Spot orphaned crosswalks (no players row) SELECT x.* FROM identifier_crosswalk x LEFT JOIN players p ON p.citizenid = x.citizenid WHERE p.citizenid IS NULL; -- Spot players with zeroed wallets (sanity) SELECT citizenid, money FROM players WHERE JSON_EXTRACT(money, '$.cash') IS NULL OR JSON_EXTRACT(money, '$.bank') IS NULL; -- Detect duplicates (same human with multiple identifiers) SELECT old_identifier, COUNT(*) FROM identifier_crosswalk GROUP BY old_identifier HAVING COUNT(*) > 1;
Schritt 6 – Validierungssuite
- Zeilenanzahl:
COUNT(Benutzer)≈COUNT(Spieler)(innerhalb der erwarteten Deltas). - Saldensummen: Summe des ESX-Bargelds/Bankguthabens ≈ Summe der QB-Wallets nach der Migration.
- Beispielaudit: Wählen Sie 10 Spieler nach Namen aus; überprüfen Sie
Bürger-ID, Waagen, Fahrzeuge. - Login-Test: Server in den Staging-Modus bringen; einige bekannte Spieler anmelden; Benutzeroberflächen überprüfen.
Beispiele für die Summenprüfung:
-- ESX-Summen SELECT SUM(COALESCE(JSON_EXTRACT(Konten,'$.Geld'),0)) AS esx_cash_total, SUM(COALESCE(JSON_EXTRACT(Konten,'$.Bank'),0)) AS esx_bank_total FROM Benutzer; -- QB-Summen SELECT SUM(COALESCE(JSON_EXTRACT(Geld,'$.Geld'),0)) AS qb_cash_total, SUM(COALESCE(JSON_EXTRACT(Geld,'$.Bank'),0)) AS qb_bank_total FROM Spieler;
Schritt 7 – Laufzeitkompatibilität (Adapter)
Auch nach der Migration können einige ältere Skripte noch auf ESX verweisen Kennung. Behalten Sie die Zebrastreifen und verwenden Sie einen Helfer, um „ (oder umgekehrt) zur Laufzeit aufzulösen.
Lua-Helfer (Server):
--- lookup_citizenid.lua lokale Funktion getCitizenIdByIdentifier(Kennung) lokales Ergebnis = MySQL.query.await('SELECT citizenid FROM identifier_crosswalk WHERE old_identifier = ? LIMIT 1', {Kennung}) wenn Ergebnis und Ergebnis[1], dann gib Ergebnis[1].citizenid zurück, Ende, nil zurückgeben, Ende, { getCitizenIdByIdentifier = getCitizenIdByIdentifier}
Verwenden Sie dies in älteren Ereignishandlern, bis alle Skripte QB/QBOX-nativ sind. Vollständige Schnittstellen-Shims finden Sie im Artikel zu Adaptermustern.
- Adaptermuster: https://fivemx.com/adapter-patterns/
- Vollständiger Umbauleitfaden (Säule): https://fivemx.com/converting-fivem-scripts/
Rollback-Strategie
- Halten
Kennung_Fußgängerübergangund ein Sicherung vor der Migration. - Wenn etwas schief geht, lassen Sie das neue
Spielerin diesem Fenster erstellte Zeilen und stellen Sie die Sicherung wieder her. - Führen Sie die Migration erneut aus, nachdem Sie die Datenrandfälle behoben haben.
Einfaches Etikett zum Markieren Ihres Fensters:
-- Neue Zeilen markieren UPDATE players SET metadata = JSON_MERGE_PATCH(COALESCE(metadata,'{}'), JSON_OBJECT('migration_tag','esx_to_qb_2025_08_16')) WHERE citizenid IN (SELECT citizenid FROM identifier_crosswalk);
Randfälle und Tipps
- Mehrere Charaktere pro Mensch: Wenn Ihr ESX einen verwendet
Kennungpro Konto (kein Multi-Char), aber Sie planen Multi-Char auf QB, ziehen Sie in Erwägung, später zusätzliche Bürger über In-Game-Flows zu generieren, nicht hier. - Namenskollisionen: Zwei ESX-Benutzer mit demselben Vor-/Nachnamen sind in Ordnung; Bürger-ID ist der Schlüssel.
- Fehlen “ Werte: Bevorzugen Sie die stabile Kennung, die Sie haben (
Dampf,Lizenz2,fivem). AuffüllenSpielerlizenzmit dem Besten, was es gibt. - Altes MySQL ohne JSON: Verwenden Sie JSON-Strings im Klartext und analysieren Sie sie im App-Code. Planen Sie ein Upgrade.
- Schwarzgeldpolitik: Teilen Sie Ihre Entscheidung mit. Führen Sie bei der Konvertierung in Artikel eine separate, transparente Artikelmigration durch.
Checkliste für die Umstellung (Produktion)
Häufig gestellte Fragen
F: Kann ich ESX weiterhin verwenden? “ überall?
A: Ja, aber behandeln Sie es als Vermächtnis. Verwenden Sie den Crosswalk, um das Problem bei Bedarf zu lösen, und aktualisieren Sie die Skripte so schnell wie möglich auf CitizenID.
F: Benötigt QBOX ein anderes SQL?
A: Nicht für Kennungen/Geld; QBOX verfolgt das Schema von QB genau. Überprüfen Sie die Spaltennamen vor der Ausführung.
F: Was ist mit Lagerbeständen, Jobs, Gangs?
A: Nicht Gegenstand dieses Artikels. Behandeln Sie sie, nachdem sich Kennungen/Geld stabilisiert haben. Nutzen Sie den Pillar-Leitfaden für eine vollständige Abdeckung.
Nächste Schritte
- Implementieren Sie Laufzeit-Shims von Adaptermuster: https://fivemx.com/adapter-patterns/
- Schließen Sie die vollständige Migration mit dem Rahmenleitfaden (Säule): https://fivemx.com/converting-fivem-scripts/
- Dokumentieren Sie Ihre lokalen Abweichungen (benutzerdefinierte Wallets, zusätzliche Spalten) in Ihrem Repo.
Anhang – Idempotente Wrapper
Umschließen Sie kritische UPDATE/INSERTs mit Schutzvorrichtungen, damit Sie sie sicher erneut ausführen können.
-- Beispielwächter: Aktualisiere nur Spieler mit unberührtem Geld. UPDATE players p JOIN identifier_crosswalk x ON x.citizenid = p.citizenid JOIN esx_balances b ON b.identifier = x.old_identifier SET p.money = JSON_OBJECT('cash', CAST(b.esx_cash AS UNSIGNED), 'bank', CAST(b.esx_bank AS UNSIGNED)) WHERE JSON_EXTRACT(p.money, '$.cash') = 0 AND JSON_EXTRACT(p.money, '$.bank') = 0;
Behalten Sie den Zebrastreifen für immer. Er ist Ihr Rosettastein für alte Protokolle und Skripte.






