Migração de SQL e Identificadores: steam/license → citize&#…
Caso de uso: Caso de uso: Você está migrando do ESX para o QBCore ou QBOX (qbx_core) e precisa de uma migração limpa e auditável de identificadores e saldos de jogadores. Este guia fornece SQL pronto para produção, um plano reversível e etapas de validação.
Leituras relacionadas:
- Padrões de Adaptador: ESX↔QBCore↔QBOX (Exportações, Eventos e Modelos de Jogador) — https://fivemx.com/adapter-patterns/
- Convertendo scripts FiveM – ESX, QBCore, QBOX (Guia de Framework) — https://fivemx.com/converting-fivem-scripts/ (Pilar)
O que muda entre ESX e QBCore/QBOX
| Tópico | ESX (comum) | QBCore / QBOX (comum) |
|---|---|---|
| Chave do jogador principal | identificador (por exemplo, licença:xxx ou legado vapor:xxx) | identidade de cidadão (token gerado pelo servidor) |
| Identificadores Alt | usuários.identificador, às vezes um separado identificadores mesa | colunas como licença, vapor, fivem armazenado ao lado identidade de cidadão |
| Modelo monetário | Contas separadas (dinheiro/banco/dinheiro sujo) via usuários.contas (JSON) ou contas de usuário linhas | Solteiro dinheiro JSON ativado jogadores (por exemplo, { "dinheiro": 0, "banco": 5000 }); carteiras extras opcionais |
| Veículos | veículos_próprios.proprietário refere-se a ESX identificador | veículos_jogador.id_cidadão (ou licença em alguns garfos) |
O QBOX geralmente segue o formato do banco de dados do QB. Trate o QBOX como "esquema QB + adições qbx". Sempre diferencie seu esquema ativo.
Regras de ouro (não pule)
- Congelar escreve durante a migração (pare o servidor do jogo + quaisquer bots externos que estejam tocando no DB).
- Backup completo e um despejo de estruturas de tabela. Armazene ambos com registros de data e hora.
- Trabalhar em uma transação por tabela, se possível; mantenha as etapas idempotentes.
- Crie uma faixa de pedestres (
identificador_antigo→identidade de cidadão) que você pode reutilizar ou reverter.
Alvo que você está mirando (linha de base QB/QBOX)
Um típico jogadores tabela (as colunas variam de acordo com o garfo):
-- Inspecione seu esquema atual e ajuste. DESCREVA os jogadores; -- Espere colunas como: citizenid, licença, nome, dinheiro, charinfo, emprego, gangue, metadados
- identidade de cidadão: chave primária usada em QB/QBOX.
- licença/steam: manter para fins forenses e revinculação.
- dinheiro (JSON): por exemplo
{"dinheiro":123,"banco":456}. Alguns servidores adicionamcriptografia,sujo, etc.
Etapa 0 — Instantâneo e preparação
# Backup do MySQL/MariaDB mysqldump -u root -p --routines --triggers yourdb > yourdb_$(date +%F_%H%M).sql # Opcional: snapshot somente da estrutura mysqldump -u root -p --no-data yourdb > yourdb_schema_$(date +%F_%H%M).sql
Crie uma cópia de preparação. Execute tudo lá primeiro
Etapa 1 — Construir o faixa de pedestres mesa
Mapearemos todos os ESX identificador para um novo identidade de cidadão. Se você já tem um jogadores tabela com citizenids, você inverterá o mapeamento (veja Jogadores QB existentes nota abaixo).
-- 1) Criar faixa de pedestres CRIAR TABELA SE NÃO EXISTIR identifier_crosswalk ( old_identifier VARCHAR(60) CHAVE PRIMÁRIA, citizenid VARCHAR(20) NÃO NULO, licença VARCHAR(60) NULO, steam VARCHAR(60) NULO, created_at TIMESTAMP PADRÃO TIMESTAMP ATUAL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 2) Seed de usuários ESX (ajuste os nomes das tabelas/colunas de acordo com seu ESX) -- O ESX comum tem `users.identifier` contendo license:xxx ou 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 você tiver uma tabela `identifiers` separada, mescle os valores mais conhecidos -- Exemplo (opcional): prefira licença quando disponível 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 identificadores 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) Unicidade e índices ALTER TABLE identifier_crosswalk ADD UNIQUE KEY ux_cid (citizenid), ADD KEY ix_license (licença), ADD KEY ix_steam (steam);
Já tem algum QB? Se você já tem
jogadoreslinhas, crie a faixa de pedestres selecionando-aslicença/vapore existenteidentidade de cidadãoem vez de gerar novos. Sua faixa de pedestres nunca deve atribuir um novo CitizenID a um jogador QB existente.
Etapa 2 — Normalizar/preparar o alvo jogadores linhas
Crie qualquer faltante jogadores linhas baseadas em ESX Usuários.
-- Certifique-se de que `players` existe e inspecione suas colunas primeiro. -- Inseriremos shells apenas para cidadãos ausentes. 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.sobrenome, '') AS nome_como, '{"dinheiro":0,"banco":0}' AS dinheiro, JSON_OBJECT( 'primeiroNome', COALESCE(u.primeiroNome,''), 'sobrenome', COALESCE(u.sobrenome,''), 'data de nascimento', COALESCE(u.datadenascimento,''), 'sexo', COALESCE(u.sexo,'') ) AS charinfo, JSON_OBJECT('identificador_esx', u.identificador) AS metadados DE usuários u JOIN identificador_faixa_de_pedestres x ON x.identificador_antigo = u.identificador LEFT JOIN jogadores p ON p.idcidadão = x.idcidadão ONDE p.idcidadão É NULO;
Observação: Use a concat de string do seu tipo SQL (
CONCATno MySQL) e funções JSON de acordo. Para MySQL 5.7, substituaOBJETO_JSONcom construção manual de strings, se necessário.
Variante segura do MySQL:
INSIRA EM jogadores (id do cidadão, licença, nome, dinheiro, charinfo, metadados) SELECIONE x.id do cidadão, COALESCE(NULLIF(x.license,''), NULLIF(x.steam,'')) COMO license_like, TRIM(CONCAT(COALESCE(u.firstname,''), ' ', COALESCE(u.lastname,''))) COMO name_like, '{"cash":0,"bank":0}' COMO money, CONCAT('{', '"firstName":"', REPLACE(COALESCE(u.firstname,''),'"','\"'), '",', '"lastName":"', REPLACE(COALESCE(u.lastname,''),'"','\"'), '",', '"birthdate":"', SUBSTITUIR(COALESCE(u.dateofbirth,''),'"','\"'),'",', '"gender":"', SUBSTITUIR(COALESCE(u.sex,''),'"','\"'), '"', '}') COMO charinfo, CONCAT('{', '"esx_identifier":"', SUBSTITUIR(u.identifier,'"','\"'), '"', '}') COMO metadados DE usuários u JUNTAR identificador_faixa_de_pedestres x EM x.old_identifier = u.identifier ESQUERDA JUNTAR jogadores p EM p.citizenid = x.citizenid ONDE p.citizenid É NULO;
Etapa 3 — Migrar Contas → Dinheiro
Existem dois padrões ESX comuns:
A) O ESX armazena saldos dentro usuários.contas JSON
-- Exemplo: users.accounts = '{"bank":5000, "money":750, "black_money":200}' -- 1) Extraia do JSON do ESX com segurança -- Crie uma tabela/visualização temporária com números analisados 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) Mesclar no JSON de dinheiro QB/QBOX -- Decida como lidar com black_money (veja as opções abaixo) ATUALIZAR jogadores 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 sem operações JSON nativas (ou versão antiga): construir strings JSON usando CONCAT.
B) O ESX armazena saldos em contas de usuário linhas
-- Exemplo: user_accounts(identificador, conta, dinheiro) CRIAR TABELA TEMPORÁRIA esx_balances COMO SELECIONAR ua.identifier, SOMA(CASE QUANDO ua.account='dinheiro' ENTÃO ua.money SENÃO 0 FIM) COMO esx_cash, SOMA(CASE QUANDO ua.account='banco' ENTÃO ua.money SENÃO 0 FIM) COMO esx_bank, SOMA(CASE QUANDO ua.account='dinheiro_preto' ENTÃO ua.money SENÃO 0 FIM) COMO esx_black DE user_accounts ua GROUP BY ua.identifier; ATUALIZAR jogadores p JUNTAR identificador_faixa de pedestres x LIGADO x.cidadão = p.cidadão JUNTAR esx_balances b LIGADO b.identificador = x.identificador_antigo DEFINIR p.dinheiro = JSON_OBJECT( 'dinheiro', CAST(b.esx_cash AS UNSIGNED), 'banco', CAST(b.esx_banco AS UNSIGNED) );
Manuseio dinheiro preto (escolha uma)
- Opção 1 (recomendada): Crie uma chave de carteira dedicada no QB money JSON, por exemplo
"sujo". - Opção 2: Converta em itens (por exemplo, notas marcadas) e credite no inventário (requer migração de itens; fora do escopo aqui).
- Opção 3: Zere-o (fortemente desencorajado, a menos que você tenha anunciado uma limpeza).
Implementação da opção 1:
-- Adicionar carteira suja em JSON (servidores que suportam carteiras extras) ATUALIZAR jogadores 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)));
Certifique-se de que sua estrutura/recursos realmente respeitem a carteira extra. Caso contrário, prefira a Opção 2.
Etapa 4 — Redigite tabelas estrangeiras que referenciam o ESX identificador
Tabelas típicas para corrigir:
veículos_próprios.proprietário→ mapa paraidentidade de cidadão(QB:veículos_jogador.id_cidadão)- Quaisquer tabelas personalizadas contendo
identificadorcolunas (casas, faturamento, gangues, empresas)
Veículos (ESX → QB)
-- Se você mantiver o ESX `owned_vehicles`, renomeie owner → citizenid para compatibilidade 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);
**Veículos em QB's **“ (campos mínimos; ajuste ao seu esquema):
INSERIR IGNORE EM player_vehicles (citizenid, placa, veículo, estado, garagem) SELECIONE x.citizenid, v.plate, v.vehicle, 0 AS state, 'A' AS garage FROM owned_vehicles v JUNTE identifier_crosswalk x ON x.old_identifier = v.owner;
Validar nomes de campos JSON (
veículocontramods/adereços) e lista de colunas em relação ao seu esquema QB/QBOX atual.
Etapa 5 — Restrições, índices e verificações de integridade
-- Garantir chaves primárias/únicas ALTER TABLE players ADD UNIQUE KEY ux_players_citizenid (citizenid); -- Opcional: manter uma consulta rápida por licença/Steam ALTER TABLE players ADD KEY ix_players_license (license); -- Identificar cruzamentos órfãos (sem linha de jogadores) SELECT x.* FROM identifier_crosswalk x LEFT JOIN players p ON p.citizenid = x.citizenid WHERE p.citizenid IS NULL; -- Identificar jogadores com carteiras zeradas (verificação de integridade) SELECT citizenid, money FROM players WHERE JSON_EXTRACT(money, '$.cash') IS NULL OR JSON_EXTRACT(money, '$.bank') IS NULL; -- Detectar duplicados (mesmo humano com múltiplos identificadores) SELECT old_identifier, COUNT(*) FROM identifier_crosswalk GROUP BY old_identifier HAVING COUNT(*) > 1;
Etapa 6 — Conjunto de validação
- Contagens de linhas:
COUNT(usuários)≈COUNT(jogadores)(dentro dos deltas esperados). - Totais de saldo: Soma de dinheiro/banco ESX ≈ Soma de carteiras QB após a migração.
- Auditoria de amostra: Selecione 10 jogadores pelo nome; verifique
identidade de cidadão, saldos, veículos. - Teste de login: Coloque o servidor em fase de preparação; faça login em alguns jogadores conhecidos; verifique as interfaces de usuário.
Exemplos de verificação de totais:
-- Totais ESX SELECT SUM(COALESCE(JSON_EXTRACT(contas,'$.money'),0)) AS esx_cash_total, SUM(COALESCE(JSON_EXTRACT(contas,'$.bank'),0)) AS esx_bank_total FROM usuários; -- Totais QB SELECT SUM(COALESCE(JSON_EXTRACT(dinheiro,'$.cash'),0)) AS qb_cash_total, SUM(COALESCE(JSON_EXTRACT(dinheiro,'$.bank'),0)) AS qb_bank_total FROM jogadores;
Etapa 7 — Compatibilidade de tempo de execução (adaptadores)
Mesmo após a migração, alguns scripts legados ainda podem fazer referência ao ESX identificador. Mantenha o faixa de pedestres e usar um auxiliar para resolver “ (ou inverso) em tempo de execução.
Auxiliar Lua (servidor):
--- lookup_citizenid.lua função local getCitizenIdByIdentifier(identificador) resultado local = MySQL.query.await('SELECT citizenid FROM identifier_crosswalk WHERE old_identifier = ? LIMIT 1', { identificador }) se resultado e resultado[1] então retorna resultado[1].citizenid fim retornar nulo fim retornar { getCitizenIdByIdentifier = getCitizenIdByIdentifier }
Use isso em manipuladores de eventos legados até que todos os scripts sejam nativos do QB/QBOX. Consulte o artigo sobre padrões de adaptadores para obter as correções completas da interface.
- Padrões de adaptadores: https://fivemx.com/adapter-patterns/
- Guia completo de conversão (Pilar): https://fivemx.com/converting-fivem-scripts/
Estratégia de reversão
- Manter
identificador_faixa de pedestrese um backup pré-migração. - Se algo der errado, descarte o novo
jogadoreslinhas criadas nesta janela e restaurar o backup. - Execute a migração novamente após corrigir casos extremos de dados.
Etiqueta simples para marcar sua janela:
-- Marcar novas linhas ATUALIZAR jogadores DEFINIR metadados = JSON_MERGE_PATCH(COALESCE(metadata,'{}'), JSON_OBJECT('migration_tag','esx_to_qb_2025_08_16')) ONDE citizenid IN (SELECT citizenid FROM identifier_crosswalk);
Casos extremos e dicas
- Vários caracteres por humano: Se o seu ESX usou um
identificadorpor conta (sem multi-char), mas você planeja multi-char no QB, considere gerar cidadãos adicionais mais tarde por meio de fluxos no jogo, não aqui. - Colisões de nomes: Dois usuários ESX com o mesmo nome/sobrenome estão ok; identidade de cidadão é a chave.
- Ausente “ valores: Prefira qualquer identificador estável que você tenha (
vapor,licença2,fivem). Preencherjogadores.licençacom o melhor disponível. - MySQL antigo sem JSON: Use strings JSON de texto simples e analise no código do aplicativo; planeje atualizar.
- Política de dinheiro sujo: Comunique sua decisão. Se for converter itens, execute uma migração de itens separada e transparente.
Lista de verificação de transição (produção)
Perguntas frequentes
P: Posso continuar usando o ESX “ em qualquer lugar?
R: Sim, mas trate-o como legado. Use o crosswalk para resolver quando necessário e atualize os scripts para citizenid o mais rápido possível.
P: O QBOX requer SQL diferente?
R: Não para identificadores/dinheiro; o QBOX rastreia o esquema do QB de perto. Valide os nomes das colunas antes da execução.
P: E quanto a estoques, empregos, gangues?
R: Fora do escopo deste artigo. Lide com eles depois que os identificadores/dinheiro se estabilizarem. Use o guia do Pilar para obter informações completas.
Próximos passos
- Implementar shims de tempo de execução de Padrões de Adaptadores: https://fivemx.com/adapter-patterns/
- Conclua sua migração completa usando o Guia de Estrutura (Pilar): https://fivemx.com/converting-fivem-scripts/
- Documente seus desvios locais (carteiras personalizadas, colunas extras) dentro do seu repositório.
Apêndice — Envoltórios idempotentes
Envolva ATUALIZAÇÕES/INSERÇÕES críticas com proteções para que você possa executá-las novamente com segurança.
-- Exemplo de guarda: atualizar somente jogadores com dinheiro intocado ATUALIZAR jogadores 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;
Guarde a faixa de pedestres para sempre. Ela é a sua Pedra de Roseta para registros e roteiros antigos.






