Economize 20% hoje mesmo Use o código WELCOME ao finalizar a compra. BEM-VINDO

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:


O que muda entre ESX e QBCore/QBOX

TópicoESX (comum)QBCore / QBOX (comum)
Chave do jogador principalidentificador (por exemplo, licença:xxx ou legado vapor:xxx)identidade de cidadão (token gerado pelo servidor)
Identificadores Altusuários.identificador, às vezes um separado identificadores mesacolunas como licença, vapor, fivem armazenado ao lado identidade de cidadão
Modelo monetárioContas separadas (dinheiro/banco/dinheiro sujo) via usuários.contas (JSON) ou contas de usuário linhasSolteiro dinheiro JSON ativado jogadores (por exemplo, { "dinheiro": 0, "banco": 5000 }); carteiras extras opcionais
Veículosveículos_próprios.proprietário refere-se a ESX identificadorveí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)

  1. Congelar escreve durante a migração (pare o servidor do jogo + quaisquer bots externos que estejam tocando no DB).
  2. Backup completo e um despejo de estruturas de tabela. Armazene ambos com registros de data e hora.
  3. Trabalhar em uma transação por tabela, se possível; mantenha as etapas idempotentes.
  4. Crie uma faixa de pedestres (identificador_antigoidentidade 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 adicionam criptografia, 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 jogadores linhas, crie a faixa de pedestres selecionando-as licença/vapor e existente identidade de cidadão em 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 (CONCAT no MySQL) e funções JSON de acordo. Para MySQL 5.7, substitua OBJETO_JSON com 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 para identidade de cidadão (QB: veículos_jogador.id_cidadão)
  • Quaisquer tabelas personalizadas contendo identificador colunas (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ículo contra mods/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

  1. Contagens de linhas: COUNT(usuários)COUNT(jogadores) (dentro dos deltas esperados).
  2. Totais de saldo: Soma de dinheiro/banco ESX ≈ Soma de carteiras QB após a migração.
  3. Auditoria de amostra: Selecione 10 jogadores pelo nome; verifique identidade de cidadão, saldos, veículos.
  4. 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.


Estratégia de reversão

  1. Manter identificador_faixa de pedestres e um backup pré-migração.
  2. Se algo der errado, descarte o novo jogadores linhas criadas nesta janela e restaurar o backup.
  3. 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 identificador por 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). Preencher jogadores.licença com 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


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.

Lucas
Lucas

Eu sou Luke, sou um gamer e adoro escrever sobre FiveM, GTA e roleplay. Eu administro uma comunidade de roleplay e tenho cerca de 10 anos de experiência em administração de servidores.

Artigos: 570