Spare 20 % mit WELCOMEAngebote ansehen

SQL- und Kennungsmigration: 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:


Was ändert sich zwischen ESX und QBCore/QBOX

ThemaESX (allgemein)QBCore / QBOX (allgemein)
Primärer SpielerschlüsselKennung (z.B, Lizenz:xxx oder Vermächtnis Dampf:xxx)Bürger-ID (servergeneriertes Token)
Alt-KennungenBenutzerkennung, manchmal eine separate Kennungen TischSpalten wie Lizenz, Dampf, fivem nebenbei gespeichert Bürger-ID
GeldmodellGetrennte Konten (Bargeld/Bank/Schwarzgeld) über Benutzerkonten (JSON) oder Benutzerkonten ReihenEinzel Geld JSON auf Spieler (z.B, { "Bargeld": 0, "Bank": 5000 }); optionale zusätzliche Geldbörsen
Fahrzeugeowned_vehicles.owner bezieht sich auf ESX KennungSpielerfahrzeuge.Bürger-ID (oder Lizenz auf einigen Gabeln)

QBOX folgt im Allgemeinen der QB-DB-Form. Behandle QBOX als “QB-Schema + qbx-Ergänzungen.” Diff immer dein Live-Schema.


Goldene Regeln (nicht überspringen)

  1. Schreibvorgänge einfrieren während der Migration (stoppe den Spielserver + alle externen Bots, die die DB berühren).
  2. Vollständige Sicherung und ein Dump von Tabellenstrukturen. Speichere beide mit Zeitstempeln.
  3. Arbeiten in einer Transaktion pro Tabelle, falls möglich; halte die Schritte idempotent.
  4. Erstelle eine Kreuzung (alte_KennungBürger-ID) die du wiederverwenden oder darauf zurückgreifen kannst.

Ziel, auf das du abzielst (QB/QBOX-Basislinie)

Ein typisches Spieler Tabelle (Spalten variieren je nach Gabel):

-- Überprüfe dein tatsächliches Schema und passe es an.
DESCRIBE players; -- Erwarte Spalten wie: citizenid, license, name, money, charinfo, job, gang, metadata
  • 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 hinzu Krypto, schmutzig, usw.

Schritt 0 – Snapshot und Staging

# MySQL/MariaDB-Backup: mysqldump -u root -p --routines --triggers yourdb > yourdb_$(date +%F_%H%M).sql # Optional: Struktur-Snapshot: mysqldump -u root -p --no-data yourdb > yourdb_schema_$(date +%F_%H%M).sql

Richte eine Staging-Kopie ein. Führe alles dort zuerst aus

Schritt 1 — Baue den Zebrastreifen Tisch

Wir mappen jeden ESX Kennung zu einem neuen Bürger-ID. Wenn du bereits ein Spieler Tabelle mit citizenids, du invertierst die Zuordnung (siehe Vorhandene QB-Spieler Hinweis unten).

-- 1) Erstelle eine Kreuzung
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) Saatgut aus ESX-Benutzern (passe Tabellen-/Spaltennamen an deine ESX-Variante an)
-- Häufiges ESX hat `users.identifier`, das license:xxx oder steam:xxx enthält
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 du eine separate `identifiers`-Tabelle hast, fusioniere die besten bekannten Werte
-- Beispiel (optional): Bevorzuge die Lizenz, 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 & 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 du bereits Spieler Zeilen, erstelle die Fußgängerüberführung, indem du sie auswählst Lizenz/Dampf Und bestehende Bürger-ID anstatt neue zu generieren. Ihr Crosswalk darf einem vorhandenen QB-Spieler niemals eine neue Citizen-ID zuweisen.


Schritt 2 – Ziel normalisieren/vorbereiten Spieler Reihen

Erstelle fehlende Spieler Zeilen basierend auf ESX Benutzer.

-- Sicherstellen, dass `players` existiert und zuerst seine Spalten überprüfen.
-- Wir fügen nur Hüllen 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,
 '{"cash":0,"bank":0}' AS money,
 JSON_OBJECT(
 'firstName', COALESCE(u.firstname,''),
 'lastName', COALESCE(u.lastname,''),
 'birthdate', COALESCE(u.dateofbirth,''),
 'gender', COALESCE(u.sex,'')
 ) AS charinfo,
 JSON_OBJECT('esx_identifier', u.identifier) AS metadata
FROM users u
JOIN identifier_crosswalk x ON x.old_identifier = u.identifier
LEFT JOIN players p ON p.citizenid = x.citizenid
WHERE p.citizenid IS NULL;

Notiz: Verwende die Zeichenkettenverkettung deiner SQL-Variante (CONCAT in MySQL) und JSON-Funktionen entsprechend. Für MySQL 5.7 ersetzen JSON_OBJECT mit 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,''),'"','\"'),'",',
 '"gender":"', REPLACE(COALESCE(u.sex,''),'"','\"'), '"',
 '}') AS charinfo,
 CONCAT('{',
 '"esx_identifier":"', REPLACE(u.identifier,'"','\"'), '"',
 '}') AS metadata
FROM users u
JOIN identifier_crosswalk x ON x.old_identifier = u.identifier
LEFT JOIN players 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) Extrahieren aus ESX JSON sicher
-- Temporäre Ansicht/Tabelle mit geparsten Zahlen erstellen
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) In QB/QBOX-Geld-JSON zusammenführen
-- Entscheiden, wie black_money behandelt 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): Baue JSON-Strings mit CONCAT.

B) ESX speichert Guthaben in Benutzerkonten Reihen

-- Beispiel: user_accounts(identifier, account, money)
CREATE TEMPORARY TABLE esx_balances AS
SELECT ua.identifier,
 SUM(CASE WHEN ua.account='money' THEN ua.money ELSE 0 END) AS esx_cash,
 SUM(CASE WHEN ua.account='bank' THEN ua.money ELSE 0 END) AS esx_bank,
 SUM(CASE WHEN ua.account='black_money' THEN ua.money ELSE 0 END) AS esx_black
FROM user_accounts ua
GROUP BY ua.identifier;

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)
);

Handhabung Schwarzgeld (wähle eines)

  • Option 1 (empfohlen): Erstelle einen dedizierten Wallet-Schlüssel in QB money JSON, z. B. "schmutzig".
  • Option 2: Konvertiere in Artikel (z. B. markierte Rechnungen) und gutschreibe stattdessen im Inventar (erfordert Artikelmigration; nicht im Rahmen dieser Anleitung).
  • Option 3: Setze es auf null (stark abzuraten, es sei denn, du hast 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)));

Stelle sicher, dass dein Framework/Ressourcen den zusätzlichen Wallet tatsächlich respektieren. Andernfalls bevorzugst du Option 2.


Schritt 4 – Neuschlüsselung fremder Tabellen, die auf ESX verweisen Kennung

Typische zu reparierende Tabellen:

  • owned_vehicles.owner → Karte zu Bürger-ID (QB: Spielerfahrzeuge.Bürger-ID)
  • Alle benutzerdefinierten Tabellen, die Kennung Spalten (Häuser, Abrechnungen, Banden, Geschäfte)

Fahrzeuge (ESX → QB)

-- Wenn du ESX `owned_vehicles` behältst, re-key owner → citizenid für die Abwä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 (Fahrzeug gegen Mods/Requisiten) und die Spaltenliste gegen dein tatsächliches QB/QBOX-Schema.


Schritt 5 – Einschränkungen, Indizes und Integritätsprüfungen

-- Primär-/eindeutige Schlüssel sicherstellen
ALTER TABLE players
 ADD UNIQUE KEY ux_players_citizenid (citizenid);

-- Optional: schnelle Suche nach license/steam beibehalten
ALTER TABLE players
 ADD KEY ix_players_license (license);

-- Verwaiste Querverweise erkennen (keine players-Zeile)
SELECT x.*
FROM identifier_crosswalk x
LEFT JOIN players p ON p.citizenid = x.citizenid
WHERE p.citizenid IS NULL;

-- Spieler mit leeren Geldbörsen erkennen (Plausibilitätsprüfung)
SELECT citizenid, money FROM players
WHERE JSON_EXTRACT(money, '$.cash') IS NULL OR JSON_EXTRACT(money, '$.bank') IS NULL;

-- Duplikate erkennen (gleiche Person mit mehreren Identifikatoren)
SELECT old_identifier, COUNT(*)
FROM identifier_crosswalk
GROUP BY old_identifier
HAVING COUNT(*) > 1;

Schritt 6 – Validierungssuite

  1. Zeilenanzahl: COUNT(Benutzer)COUNT(Spieler) (innerhalb der erwarteten Deltas).
  2. Saldensummen: Summe des ESX-Bargelds/Bankguthabens ≈ Summe der QB-Wallets nach der Migration.
  3. Beispielaudit: Wähle 10 Spieler nach Namen aus; überprüfe Bürger-ID, Waagen, Fahrzeuge.
  4. 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(accounts,'$.money'),0)) AS esx_cash_total,
 SUM(COALESCE(JSON_EXTRACT(accounts,'$.bank'),0)) AS esx_bank_total
FROM users;

-- QB Summen
SELECT
 SUM(COALESCE(JSON_EXTRACT(money,'$.cash'),0)) AS qb_cash_total,
 SUM(COALESCE(JSON_EXTRACT(money,'$.bank'),0)) AS qb_bank_total
FROM players;

Schritt 7 – Laufzeitkompatibilität (Adapter)

Auch nach der Migration können einige ältere Skripte noch auf ESX verweisen Kennung. Behalte Zebrastreifen und verwende einen Helfer, um “ (oder invers) zur Laufzeit aufzulösen.

Lua-Helfer (Server):

--- lookup_citizenid.lua
local function getCitizenIdByIdentifier(identifier)
 local result = MySQL.query.await('SELECT citizenid FROM identifier_crosswalk WHERE old_identifier = ? LIMIT 1', { identifier })
 if result and result[1] then return result[1].citizenid end
 return nil
end

return { getCitizenIdByIdentifier = getCitizenIdByIdentifier }

Verwende dies in Legacy-Event-Handlern, bis alle Skripte QB/QBOX-nativ sind. Siehe den Artikel zu Adapter-Mustern für vollständige Interface-Shims.


Rollback-Strategie

  1. Halten Kennung_Fußgängerübergang und ein Sicherung vor der Migration.
  2. Wenn etwas schiefgeht, lass das Neue fallen Spieler Zeilen, die in diesem Fenster erstellt wurden, und stelle das Backup wieder her.
  3. Führe die Migration nach der Behebung von Datenrandfällen erneut aus.

Einfaches Etikett, um dein Fenster zu markieren:

-- 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 Kennung pro Konto (kein Multi-Char), aber du planst Multi-Char auf QB, dann überlege, 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: Was du an stabilen Identifikatoren hast (Dampf, Lizenz2, fivem). Auffüllen Spielerlizenz mit dem Besten, was es gibt.
  • Altes MySQL ohne JSON: Verwende einfache JSON-Textzeilen und parsi in der App-Code; plane einen Upgrade ein.
  • Schwarzgeldpolitik: Teile deine Entscheidung mit. Wenn du zu Items konvertierst, führe eine separate, transparente Item-Migration durch.

Checkliste für die Umstellung (Produktion)


Häufig gestellte Fragen

F: Kann ich ESX weiterhin verwenden? überall?
A: Ja, aber behandle es als Vermächtnis. Verwende den Crosswalk, um bei Bedarf aufzulösen, und aktualisiere die Skripte auf citizenid so schnell wie möglich.

F: Benötigt QBOX ein anderes SQL?
A: Nicht für Identifikatoren/Geld; QBOX verfolgt QB-Schema genau. Überprüfe Spaltennamen, bevor du ausführst.

F: Was ist mit Lagerbeständen, Jobs, Gangs?
A: Außerhalb des Umfangs dieses Artikels. Behandle sie, nachdem sich die Identifikatoren/Gelder stabilisiert haben. Verwende den Pillar-Leitfaden für eine vollständige Abdeckung.


Nächste Schritte


Anhang – Idempotente Wrapper

Schütze kritische UPDATE/INSERT-Anweisungen mit Wächtern, damit du sie sicher neu ausführen kannst.

-- 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;

Behalte die Kreuzreferenz für immer. Sie ist dein Rosetta Stone für alte Protokolle und Skripte.