Ahorra hoy mismo en 20%. Usa el código WELCOME al finalizar la compra. WELCOME

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

Caso de uso: Caso práctico: Está migrando de ESX a QBCore o QBOX (qbx_core) y necesita una migración limpia y auditable de los identificadores y saldos de los jugadores. Esta guía le ofrece SQL listo para producción, un plan reversible y los pasos de validación.

Lecturas relacionadas:


¿Qué cambios hay entre ESX y QBCore/QBOX?

TemaESX (común)QBCore / QBOX (común)
Clave de jugador principalidentificador (p.ej, licencia:xxx o legado vapor:xxx)ID de ciudadano (token generado por el servidor)
Identificadores Altidentificador de usuarios, a veces una separada identificadores mesacolumnas como licencia, vapor, fivem almacenado junto ID de ciudadano
Modelo monetarioCuentas separadas (efectivo/banco/dinero negro) a través de usuarios.cuentas (JSON) o cuentas de usuario filasSoltero dinero JSON en jugadores (p.ej, { "efectivo": 0, "banco": 5000 }); carteras adicionales opcionales
Vehículosvehículos_propiedad.propietario se refiere a ESX identificadorplayer_vehicles.citizenid (o licencia en algunas horquillas)

QBOX generalmente sigue la estructura de la base de datos de QB. Trate QBOX como "esquema QB + adiciones qbx". Siempre compare su esquema en vivo.


Reglas de oro (no te las saltes)

  1. Congelar escrituras durante la migración (detener el servidor del juego + cualquier bot externo que toque la base de datos).
  2. Copia de seguridad completa y un volcado de estructuras de tablasGuarde ambos con marcas de tiempo.
  3. Trabajar en una transacción por mesa si es posible, manteniendo los escalones idempotentes.
  4. Crear un paso de peatones (identificador antiguoID de ciudadano) puedes reutilizarlo o revertirlo.

Objetivo al que apuntas (línea base QB/QBOX)

Un típico jugadores tabla (las columnas varían según la bifurcación):

-- Inspeccione su esquema actual y ajústelo. DESCRIBA a los jugadores; -- Espere columnas como: ID de ciudadano, licencia, nombre, dinero, información del personaje, trabajo, pandilla, metadatos.
  • ID de ciudadano:clave principal utilizada en QB/QBOX.
  • licencia/steam:conservar para análisis forense y revinculación.
  • dinero (JSON): p.ej {"efectivo":123,"banco":456}Algunos servidores agregan cripto, sucio, etc.

Paso 0: Instantánea y puesta en escena

# 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

Crea una copia de prueba. Ejecútala primero.

Paso 1 — Construir el paso de peatones mesa

Mapearemos cada ESX identificador a un nuevo ID de ciudadanoSi ya tienes una jugadores tabla con los ID de ciudadanos, invertirá el mapeo (ver Jugadores QB existentes nota a continuación).

-- 1) Crear cruce de peatones CREAR TABLA SI NO EXISTE identificador_cruce de peatones ( identificador_antiguo VARCHAR(60) CLAVE PRINCIPAL, id_de_ciudadano VARCHAR(20) NO NULO, licencia VARCHAR(60) NULO, vapor VARCHAR(60) NULO, creado_en MARCA DE TIEMPO PREDETERMINADA MARCA_DE_TIEMPO ACTUAL ) MOTOR=InnoDB JUEGO DE CARACTERES PREDETERMINADO=utf8mb4; -- 2) Semilla de usuarios de ESX (ajuste los nombres de tabla/columna a su versión de ESX) -- EsX común tiene `users.identifier` que 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) Si tiene una tabla de `identificadores` independiente, combine los valores más conocidos -- Ejemplo (opcional): preferir licencia cuando esté disponible 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) Unicidad e índices ALTER TABLE identifier_crosswalk ADD UNIQUE KEY ux_cid (citizenid), ADD KEY ix_license (license), ADD KEY ix_steam (steam);

¿Jugadores QB existentes? Si ya tienes jugadores filas, crear el paso de peatones seleccionando su licencia/vapor y existente ID de ciudadano En lugar de generar nuevos. Tu cruce de caminos nunca debe asignar un nuevo ID de ciudadano a un QB existente.


Paso 2: Normalizar/preparar el objetivo jugadores filas

Crea cualquier faltante jugadores filas basadas en ESX usuarios.

-- Asegúrese de que `players` exista e inspeccione primero sus columnas. -- Insertaremos shells solo para los ciudadanos faltantes. 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.apellido, '') COMO name_like, '{"cash":0,"bank":0}' COMO money, JSON_OBJECT( 'nombre', COALESCE(u.nombre,''), 'apellido', COALESCE(u.apellido,''), 'fecha de nacimiento', COALESCE(u.fechadenacimiento,''), 'género', COALESCE(u.sexo,'') ) COMO charinfo, JSON_OBJECT('esx_identifier', u.identifier) COMO metadatos DE usuarios u UNIRSE identificador_cruce de caminos x EN x.old_identifier = u.identifier IZQUIERDA UNIRSE jugadores p EN p.citizenid = x.citizenid DONDE p.citizenid ES NULO;

Nota: Utilice la cadena concat de su variante SQL (CONCAT en MySQL) y las funciones JSON correspondientemente. Para MySQL 5.7, reemplace OBJETO JSON con construcción manual de cuerdas si es necesario.

Variante segura para MySQL:

INSERTAR EN jugadores (idciudadano, licencia, nombre, dinero, charinfo, metadatos) SELECCIONAR x.idciudadano, COALESCE(NULLIF(x.licencia,''), NULLIF(x.vapor,'')) COMO similar a licencia, RECORTAR(CONCAT(COALESCE(u.nombre,''), ' ', COALESCE(u.apellido,''))) COMO similar a nombre, '{"efectivo":0,"banco":0}' COMO dinero, CONCAT('{', '"nombre":"', REEMPLAZAR(COALESCE(u.nombre,''),'"','\"'), '",', '"apellido":"', REEMPLAZAR(COALESCE(u.apellido,''),'"','\"'), '",', '"fechanacimiento":"', REEMPLAZAR(COALESCE(u.fechanacimiento,''),'"','\"'),'",', '"género":"', REEMPLAZAR(COALESCE(u.sexo,''),'"','\"'), '"', '}') COMO charinfo, CONCAT('{', '"esx_identifier":"', REEMPLAZAR(u.identificador,'"','\"'), '"', '}') COMO metadatos DESDE usuarios u UNIRSE identificador_cruce x EN x.antiguo_identificador = u.identificador IZQUIERDA UNIRSE jugadores p EN p.ciudadanoid = x.ciudadanoid DONDE p.ciudadanoid ES NULO;

Paso 3 — Migrar Cuentas → Dinero

Hay dos patrones ESX comunes:

A) ESX almacena los saldos en su interior usuarios.cuentas JSON

-- Ejemplo: users.accounts = '{"bank":5000, "money":750, "black_money":200}' -- 1) Extraer de JSON de ESX de forma segura -- Crear una vista/tabla temporal con números analizados 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) Fusionar en JSON de dinero QB/QBOX -- Decide cómo manejar black_money (ver Opciones a continuación) ACTUALIZAR jugadores p UNIRSE a identifier_crosswalk x ON x.citizenid = p.citizenid UNIRSE a esx_balances b ON b.identifier = x.old_identifier ESTABLECER p.money = JSON_OBJECT( 'cash', CAST(b.esx_cash AS UNSIGNED), 'bank', CAST(b.esx_bank AS UNSIGNED) );

Si MySQL no tiene operaciones JSON nativas (o versión anterior): crear cadenas JSON usando CONCAT.

B) ESX almacena los saldos en cuentas de usuario filas

-- Ejemplo: user_accounts(identificador, cuenta, dinero) CREAR TABLA TEMPORAL esx_balances COMO SELECCIONAR ua.identificador, SUMA(CASO CUANDO ua.cuenta='dinero' ENTONCES ua.dinero SI NO 0 FIN) COMO esx_efectivo, SUMA(CASO CUANDO ua.cuenta='banco' ENTONCES ua.dinero SI NO 0 FIN) COMO esx_banco, SUMA(CASO CUANDO ua.cuenta='negro_dinero' ENTONCES ua.dinero SI NO 0 FIN) COMO esx_negro DE user_accounts ua AGRUPAR POR ua.identificador; ACTUALIZAR jugadores p UNIRSE a identificador_crosswalk x ENCENDIDO x.citizenid = p.citizenid UNIRSE a esx_balances b ENCENDIDO b.identificador = x.old_identifier ESTABLECER p.dinero = JSON_OBJECT( 'efectivo', CAST(b.esx_cash COMO SIN FIRMAR), 'banco', CAST(b.esx_bank COMO SIN FIRMAR) );

Manejo dinero negro (elige uno)

  • Opción 1 (recomendada): Cree una clave de billetera dedicada en QB money JSON, por ejemplo "sucio".
  • Opción 2: Convertir a artículos (por ejemplo, facturas marcadas) y acreditar el inventario en su lugar (requiere migración de artículos; fuera del alcance aquí).
  • Opción 3: Póngalo a cero (fuertemente desaconsejado a menos que haya anunciado un borrado).

Implementación de la opción 1:

-- Agregar billetera sucia en JSON (servidores que admiten billeteras adicionales) ACTUALIZAR jugadores p UNIRSE a identifier_crosswalk x ON x.citizenid = p.citizenid UNIRSE a 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)));

Asegúrese de que su marco de trabajo/recursos realmente respeten la billetera adicional. De lo contrario, prefiera la opción 2.


Paso 4: Vuelva a crear las claves de las tablas externas que hacen referencia a ESX identificador

Tablas típicas para arreglar:

  • vehículos_propiedad.propietario → mapa a ID de ciudadano (QB: player_vehicles.citizenid)
  • Cualquier tabla personalizada que contenga identificador columnas (casas, facturación, pandillas, negocios)

Vehículos (ESX → QB)

-- Si conserva ESX `owned_vehicles`, vuelva a codificar owner → citizenid para compatibilidad 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);

**Vehículos en QB** (campos mínimos; ajuste a su esquema):

INSERTAR IGNORAR EN player_vehicles (citizenid, placa, vehículo, estado, garaje) SELECCIONAR x.citizenid, v.placa, v.vehicle, 0 COMO estado, 'A' COMO garaje DESDE owned_vehicles v UNIR identificador_cruce de peatones x EN x.old_identifier = v.owner;

Validar nombres de campos JSON (vehículo contra mods/accesorios) y la lista de columnas contra su esquema QB/QBOX real.


Paso 5: Restricciones, índices y comprobaciones de integridad

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

Paso 6 — Conjunto de validación

  1. Número de filas: COUNT(usuarios)CONTAR(jugadores) (dentro de los deltas esperados).
  2. Totales de saldo: Suma del efectivo/banco ESX ≈ Suma de las billeteras QB después de la migración.
  3. Ejemplo de auditoría: Elige 10 jugadores por nombre; verifica ID de ciudadano, balanzas, vehículos.
  4. Prueba de inicio de sesión: Poner el servidor en modo staging, iniciar sesión con algunos jugadores conocidos, verificar las IU.

Ejemplos de comprobación de totales:

-- Totales de ESX SELECCIONAR SUMA(COALESCE(JSON_EXTRACT(cuentas,'$.dinero'),0)) COMO esx_cash_total, SUM(COALESCE(JSON_EXTRACT(cuentas,'$.banco'),0)) COMO esx_bank_total DE usuarios; -- Totales de QB SELECCIONAR SUMA(COALESCE(JSON_EXTRACT(dinero,'$.dinero'),0)) COMO qb_cash_total, SUM(COALESCE(JSON_EXTRACT(dinero,'$.banco'),0)) COMO qb_bank_total DE jugadores;

Paso 7: Compatibilidad en tiempo de ejecución (adaptadores)

Incluso después de la migración, algunos scripts heredados aún pueden hacer referencia a ESX identificadorMantener el paso de peatones y use un ayudante para resolver " (o inverso) en tiempo de ejecución.

Ayudante de Lua (servidor):

--- lookup_citizenid.lua función local getCitizenIdByIdentifier(identificador) resultado local = MySQL.query.await('SELECT citizenid FROM identifier_crosswalk WHERE old_identifier = ? LIMIT 1', { identificador }) si resultado y resultado[1] entonces devuelve resultado[1].citizenid fin devuelve nulo fin devuelve { getCitizenIdByIdentifier = getCitizenIdByIdentifier }

Use esto en los controladores de eventos heredados hasta que todos los scripts sean nativos de QB/QBOX. Consulte el artículo sobre patrones de adaptador para obtener información completa sobre las correcciones de interfaz.


Estrategia de reversión

  1. Mantener identificador_cruce de peatones y un copia de seguridad previa a la migración.
  2. Si algo sale mal, descarte el nuevo. jugadores filas creadas en esta ventana y restaurar la copia de seguridad.
  3. Vuelva a ejecutar la migración después de corregir los casos extremos de los datos.

Etiqueta sencilla para marcar tu ventana:

-- Etiquetar nuevas filas ACTUALIZAR jugadores ESTABLECER metadatos = JSON_MERGE_PATCH(COALESCE(metadata,'{}'), JSON_OBJECT('migration_tag','esx_to_qb_2025_08_16')) DONDE citizenid EN (SELECCIONAR citizenid DE identifier_crosswalk);

Casos extremos y consejos

  • Varios caracteres por humano: Si su ESX usó uno identificador por cuenta (sin multi-char), pero planeas tener multi-char en QB, considera generar ciudadanos adicionales más adelante a través de flujos en el juego, no aquí.
  • Colisiones de nombres: Dos usuarios de ESX con el mismo nombre y apellido están bien; ID de ciudadano es la clave
  • Desaparecido valores: Prefiera cualquier identificador estable que tenga (vapor, licencia2, fivem). Rellenar jugadores.licencia con lo mejor disponible.
  • MySQL antiguo sin JSON: Utilice cadenas JSON de texto simple y analícelas en el código de la aplicación; planifique actualizar.
  • Política de dinero negro: Comunique su decisión. Si se convierte a elementos, realice una migración de elementos independiente y transparente.

Lista de verificación de corte (producción)


Preguntas frecuentes

P: ¿Puedo seguir usando ESX? ¿en cualquier lugar?
A: Sí, pero trátalo como legadoUtilice la pasarela para resolver cuando sea necesario y actualice los scripts a citizenid lo antes posible.

P: ¿QBOX requiere SQL diferente?
R: No para identificadores ni dinero; QBOX sigue de cerca el esquema de QB. Valide los nombres de las columnas antes de ejecutar.

P: ¿Qué pasa con los inventarios, los trabajos y las pandillas?
R: Fuera del alcance de este artículo. Se deben abordar después de que los identificadores y el dinero se estabilicen. Consulte la guía de Pilares para obtener una cobertura completa.


Próximos pasos


Apéndice — Envoltorios idempotentes

Envuelva las ACTUALIZACIONES/INSERCIONES críticas con protectores para poder ejecutarlas nuevamente de manera segura.

-- Ejemplo de guardia: solo actualiza los jugadores con dinero intacto UPDATE jugadores 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;

Conserve el paso de peatones para siempre. Es su Rosetta Stone para registros y guiones antiguos.

Lucas
Lucas

Soy Luke, gamer y me encanta escribir sobre FiveM, GTA y juegos de rol. Dirijo una comunidad de juegos de rol y tengo unos 10 años de experiencia administrando servidores.

Artículos: 436