Risparmia oggi con 20% Usa il codice WELCOME al pagamento. WELCOME

SQL & Identifiers Migration: steam/license → citize&#…

Caso d'uso: Caso d'uso: stai passando da ESX a QBCore o QBOX (qbx_core) e hai bisogno di una migrazione pulita e verificabile degli identificatori e dei saldi dei giocatori. Questa guida fornisce SQL pronto per la produzione, un piano reversibile e passaggi di convalida.

Letture correlate:


Cosa cambia tra ESX e QBCore/QBOX

ArgomentoESX (comune)QBCore / QBOX (comune)
Chiave del giocatore primarioidentificatore (per esempio, licenza:xxx o eredità vapore:xxx)cittadinanza (token generato dal server)
Identificatori altutenti.identificatore, a volte un separato identificatori tavolocolonne come licenza, vapore, fivem immagazzinato accanto cittadinanza
Modello monetarioConti separati (contanti/banca/denaro nero) tramite utenti.account (JSON) O account_utente righeSeparare soldi JSON attivo giocatori (per esempio, { "contanti": 0, "banca": 5000 }); portafogli extra opzionali
Veicoliveicoli_di_proprietà.proprietario si riferisce a ESX identificatoreplayer_vehicles.citizenid (O licenza su alcune forchette)

QBOX generalmente segue la struttura del database di QB. Considera QBOX come "schema QB + aggiunte qbx". Confronta sempre lo schema live con quello attuale.


Regole d'oro (da non saltare)

  1. Freeze scrive durante la migrazione (arrestare il server di gioco + eventuali bot esterni che toccano il DB).
  2. Backup completo e un dump delle strutture delle tabelleMemorizzare entrambi con timestamp.
  3. Lavorare in una transazione per tabella, se possibile; mantenere i passaggi idempotenti.
  4. Creare un attraversamento pedonale (vecchio_identificatorecittadinanza) puoi riutilizzarlo o ripristinarlo.

Obiettivo a cui miri (baseline QB/QBOX)

Un tipico giocatori tabella (le colonne variano a seconda della fork):

-- Esamina il tuo schema attuale e adattalo. DESCRIVI i giocatori; -- Aspettati colonne come: citizenid, license, name, money, charinfo, job, gang, metadata
  • cittadinanza: chiave primaria utilizzata in QB/QBOX.
  • licenza/vapore: conservare per analisi forensi e ricollegamento.
  • denaro (JSON): per esempio {"contanti":123,"banca":456}Alcuni server aggiungono criptovaluta, sporco, ecc.

Fase 0 — Snapshot e 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

Avvia una copia di staging. Esegui prima tutto lì

Fase 1: costruire il attraversamento pedonale tavolo

Mapperemo ogni ESX identificatore a un nuovo cittadinanzaSe hai già un giocatori tabella con citizenid, invertirai la mappatura (vedi Giocatori QB esistenti nota sotto).

-- 1) Crea crosswalk CREA TABELLA SE NON ESISTE 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 dagli utenti ESX (adatta i nomi delle tabelle/colonne al tuo ESX) -- Common ESX ha `users.identifier` che contiene license:xxx o 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) Se hai una tabella `identificatori` separata, unisci i valori più noti -- Esempio (facoltativo): preferisci la licenza quando disponibile 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) Unicità e indici ALTER TABLE identifier_crosswalk ADD UNIQUE KEY ux_cid (citizenid), ADD KEY ix_license (license), ADD KEY ix_steam (steam);

Giocatori QB esistenti? Se hai già giocatori righe, crea l'attraversamento pedonale selezionando le loro licenza/vapore E esistente cittadinanza invece di generarne di nuovi. Il tuo crosswalk non deve mai assegnare un nuovo citizenid a un giocatore QB esistente.


Fase 2 — Normalizzare/preparare il bersaglio giocatori righe

Crea qualsiasi mancante giocatori righe basate su ESX utenti.

-- Assicurati che `players` esista e ispeziona prima le sue colonne. -- Inseriremo le shell solo per i cittadini mancanti. 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;

Nota: Utilizza la stringa concat (del tuo tipo SQL)CONCAT in MySQL) e le funzioni JSON di conseguenza. Per MySQL 5.7, sostituire JSON_OBJECT con creazione manuale delle stringhe, se necessario.

Variante sicura per MySQL:

INSERISCI IN giocatori (citizenid, licenza, nome, denaro, charinfo, metadati) SELEZIONA x.citizenid, COALESCE(NULLIF(x.license,''), NULLIF(x.steam,'')) COME license_like, TRIM(CONCAT(COALESCE(u.firstname,''), ' ', COALESCE(u.lastname,''))) COME name_like, '{"cash":0,"bank":0}' COME money, CONCAT('{', '"firstName":"', SOSTITUISCI(COALESCE(u.firstname,''),'"','\"'), '",', '"lastName":"', SOSTITUISCI(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;

Fase 3: migrazione Conti → Denaro

Esistono due modelli ESX comuni:

A) ESX memorizza i saldi all'interno utenti.account JSON

-- Esempio: users.accounts = '{"bank":5000, "money":750, "black_money":200}' -- 1) Estrarre da ESX JSON in modo sicuro -- Creare una vista/tabella temporanea con numeri analizzati 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) Unisci in denaro QB/QBOX JSON -- Decidi come gestire black_money (vedi Opzioni sotto) AGGIORNA giocatori 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) );

Se MySQL senza operazioni JSON native (o vecchia versione): crea stringhe JSON usando CONCAT.

B) ESX memorizza i saldi in account_utente righe

-- Esempio: user_accounts(identificatore, conto, denaro) 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; AGGIORNA giocatori p UNISCI identifier_crosswalk x ON x.citizenid = p.citizenid UNISCI esx_balances b ON b.identifier = x.old_identifier IMPOSTA p.money = JSON_OBJECT( 'contanti', CAST(b.esx_cash AS UNSIGNED), 'banca', CAST(b.esx_bank AS UNSIGNED) );

Gestione denaro nero (scegline uno)

  • Opzione 1 (consigliata): Crea una chiave di portafoglio dedicata in QB money JSON, ad esempio "sporco".
  • Opzione 2: Convertire in articoli (ad esempio, fatture contrassegnate) e accreditare invece l'inventario (richiede la migrazione degli articoli; qui non rientra nell'ambito).
  • Opzione 3: Azzeralo (fortemente sconsigliato a meno che tu non abbia annunciato un wipe).

Implementazione dell'opzione 1:

-- Aggiungi portafoglio sporco in JSON (server che supportano portafogli extra) AGGIORNA giocatori 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)));

Assicurati che il tuo framework/le tue risorse rispettino effettivamente il portafoglio extra. Altrimenti, preferisci l'opzione 2.


Passaggio 4: ricodificare le tabelle esterne che facevano riferimento a ESX identificatore

Tabelle tipiche da correggere:

  • veicoli_di_proprietà.proprietario → mappa verso cittadinanza (Quarto posto: player_vehicles.citizenid)
  • Qualsiasi tabella personalizzata contenente identificatore colonne (case, fatturazione, bande, aziende)

Veicoli (ESX → QB)

-- Se si mantiene ESX `owned_vehicles`, rinominare owner → citizenid per la compatibilità futura 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);

**Veicoli in QB**“ (campi minimi; adattare al proprio schema):

INSERISCI IGNORA IN player_vehicles (citizenid, targa, veicolo, stato, garage) SELEZIONA x.citizenid, v.targa, v.veicolo, 0 COME stato, 'A' COME garage DA owned_vehicles v UNISCI identifier_crosswalk x SU x.old_identifier = v.owner;

Convalida i nomi dei campi JSON (veicolo contro mod/oggetti di scena) e l'elenco delle colonne in base al tuo schema QB/QBOX effettivo.


Fase 5: vincoli, indici e controlli di integrità

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

Fase 6 — Suite di convalida

  1. Conteggio delle righe: COUNT(utenti)COUNT(giocatori) (entro i delta previsti).
  2. Saldo totale: Somma di ESX cash/bank ≈ Somma dei portafogli QB dopo la migrazione.
  3. Esempio di verifica: Scegli 10 giocatori per nome; verifica cittadinanza, bilance, veicoli.
  4. Test di accesso: Avvia il server in modalità staging; accedi ad alcuni giocatori noti; verifica le interfacce utente.

Esempi di controllo dei totali:

-- Totali ESX 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; -- Totali QB 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;

Fase 7 — Compatibilità runtime (adattatori)

Anche dopo la migrazione, alcuni script legacy potrebbero ancora fare riferimento a ESX identificatore. Mantieni il attraversamento pedonale e utilizzare un helper per risolvere " (o invertire) in fase di esecuzione.

Assistente Lua (server):

--- lookup_citizenid.lua funzione locale getCitizenIdByIdentifier(identificatore) risultato locale = MySQL.query.await('SELECT citizenid FROM identifier_crosswalk WHERE old_identifier = ? LIMIT 1', { identificatore }) se risultato e risultato[1] allora restituisci risultato[1].citizenid fine restituisci nil fine restituisci { getCitizenIdByIdentifier = getCitizenIdByIdentifier }

Utilizzare questa opzione nei gestori di eventi legacy finché tutti gli script non saranno nativi di QB/QBOX. Consultare l'articolo sui pattern degli adattatori per gli shim di interfaccia completi.


Strategia di rollback

  1. Mantenere identificatore_attraversamento pedonale e un backup pre-migrazione.
  2. Se qualcosa va storto, abbandona il nuovo giocatori righe create in questa finestra e ripristinare il backup.
  3. Ripetere la migrazione dopo aver corretto i casi limite dei dati.

Etichetta semplice per contrassegnare la tua finestra:

-- Tagga le nuove righe AGGIORNA i giocatori IMPOSTA metadati = JSON_MERGE_PATCH(COALESCE(metadati,'{}'), JSON_OBJECT('migration_tag','esx_to_qb_2025_08_16')) DOVE citizenid IN (SELECT citizenid FROM identifier_crosswalk);

Casi limite e suggerimenti

  • Più caratteri per essere umano: Se il tuo ESX ne ha utilizzato uno identificatore per account (niente personaggi multipli), ma se prevedi personaggi multipli su QB, valuta la possibilità di generare cittadini aggiuntivi in seguito tramite flussi di gioco, non qui.
  • Collisioni di nomi: Due utenti ESX con lo stesso nome/cognome vanno bene; cittadinanza è la chiave.
  • Mancante valori: Preferisci qualsiasi identificatore stabile tu abbia (vapore, licenza2, fivem). Popolare giocatori.licenza con il meglio disponibile.
  • Vecchio MySQL senza JSON: Utilizzare stringhe JSON in testo semplice e analizzarle nel codice dell'app; pianificare l'aggiornamento.
  • Politica sul denaro nero: Comunica la tua decisione. Se stai convertendo in articoli, esegui una migrazione separata e trasparente degli articoli.

Lista di controllo del passaggio (produzione)


Domande frequenti

D: Posso continuare a utilizzare ESX? ovunque?
A: Sì, ma trattalo come ereditàUtilizzare il crosswalk per risolvere il problema quando necessario e aggiornare gli script a citizenid il prima possibile.

D: QBOX richiede un SQL diverso?
R: Non per identificatori/denaro; QBOX tiene traccia da vicino dello schema di QB. Convalidare i nomi delle colonne prima dell'esecuzione.

D: E per quanto riguarda inventari, posti di lavoro, bande?
R: Esulano dall'ambito di questo articolo. Gestirli dopo che gli identificatori/il denaro si saranno stabilizzati. Utilizzare la guida Pillar per una copertura completa.


Prossimi passi


Appendice — Wrapper idempotenti

Incorporare le protezioni per UPDATE/INSERT critici in modo da poterli rieseguire in sicurezza.

-- Esempio di guardia: aggiorna solo i giocatori con denaro intatto 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;

Conserva le strisce pedonali per sempre. È la tua Stele di Rosetta per vecchi registri e manoscritti.

Luca
Luca

Mi chiamo Luke, sono un giocatore e amo scrivere di FiveM, GTA e giochi di ruolo. Gestisco una community di gioco di ruolo e ho circa 10 anni di esperienza nell'amministrazione di server.

Articoli: 436