Converter um script ESX para QBCore (e voltar): mapa de funções e bridge
Porte um script FiveM entre ESX e QBCore: uma tabela das chamadas comuns (jogador, dinheiro, job, itens, notificações, callbacks) e um arquivo bridge que suporta ambos.
Você tem um script escrito para ESX, e seu servidor executa QBCore, ou o inverso. A maioria do script é Lua simples e natives que não mudam. Apenas as chamadas do framework mudam, e há menos do que você espera. Este artigo fornece um mapa delas, e então mostra como isolá-las em um arquivo bridge.
O que fica e o que muda
Natives, NUI, seus próprios eventos, loops, targets e menus funcionam em qualquer framework. O que muda é uma lista curta:
- como você obtém o objeto do framework
- como você obtém um jogador
- dinheiro, jobs e itens
- notificações
- itens usáveis e callbacks
- as tabelas do banco de dados para jogadores
Pesquise o script por ESX. e xPlayer, e cada resultado é algo para converter.
Mapa de funções
| Tarefa | ESX Legacy | QBCore |
|---|---|---|
| Obter o objeto | ESX = exports['es_extended']:getSharedObject() |
local QBCore = exports['qb-core']:GetCoreObject() |
| Obter um jogador (servidor) | ESX.GetPlayerFromId(src) |
QBCore.Functions.GetPlayer(src) |
| Identificador de jogador | xPlayer.identifier |
Player.PlayerData.citizenid |
| Saldo de dinheiro | xPlayer.getMoney() |
Player.PlayerData.money.cash |
| Adicionar dinheiro | xPlayer.addMoney(n) |
Player.Functions.AddMoney('cash', n) |
| Remover dinheiro | xPlayer.removeMoney(n) |
Player.Functions.RemoveMoney('cash', n) |
| Adicionar ao banco | xPlayer.addAccountMoney('bank', n) |
Player.Functions.AddMoney('bank', n) |
| Remover do banco | xPlayer.removeAccountMoney('bank', n) |
Player.Functions.RemoveMoney('bank', n) |
| Nome de job | xPlayer.job.name |
Player.PlayerData.job.name |
| Grade de job | xPlayer.job.grade |
Player.PlayerData.job.grade.level |
| Adicionar item | xPlayer.addInventoryItem(name, n) |
Player.Functions.AddItem(name, n) |
| Remover item | xPlayer.removeInventoryItem(name, n) |
Player.Functions.RemoveItem(name, n) |
| Contagem de item | xPlayer.getInventoryItem(name).count |
Player.Functions.GetItemByName(name) (nil se nenhum, depois .amount) |
| Item usável | ESX.RegisterUsableItem(name, function(src) end) |
QBCore.Functions.CreateUseableItem(name, function(src, item) end) |
| Notificar (cliente) | ESX.ShowNotification(msg) |
QBCore.Functions.Notify(msg, 'success') |
| Notificar (servidor) | xPlayer.showNotification(msg) |
TriggerClientEvent('QBCore:Notify', src, msg, 'success') |
| Dados do jogador (cliente) | ESX.GetPlayerData() |
QBCore.Functions.GetPlayerData() |
| Evento de jogador carregado | esx:playerLoaded |
QBCore:Client:OnPlayerLoaded |
| Evento de job mudado | esx:setJob |
QBCore:Client:OnJobUpdate |
| Server callback | ESX.RegisterServerCallback |
QBCore.Functions.CreateCallback |
Dois detalhes enganam as pessoas. Tipos de dinheiro: ESX tem money para dinheiro e bank, QBCore usa 'cash' e 'bank' como o primeiro argumento. E a grade: ESX dá um número simples, QBCore uma tabela, então job.grade.level.
Callbacks, que todo segundo script usa, são comparados em detalhes em server callbacks on ESX, QBCore and ox_lib.
O banco de dados
As tabelas de jogador diferem, então qualquer SELECT ou UPDATE nelas precisa de uma mudança:
| ESX | QBCore | |
|---|---|---|
| Jogadores | users |
players |
| Chave de jogador | identifier (uma string license:) |
citizenid |
| Veículos | owned_vehicles |
player_vehicles |
Um script que armazena seus próprios dados, com suas próprias tabelas, geralmente continua funcionando, desde que ele chave as linhas em um valor que você pode obter de qualquer framework. Usar o identificador de license para isso funciona em ambos.
QBox
QBox é mais próximo de QBCore do que de ESX. Seu core é qbx_core, ele mantém uma camada de compatibilidade para exports['qb-core']:GetCoreObject(), e seu inventário é ox_inventory. Um script convertido para QBCore geralmente roda em QBox com poucas mudanças, além de chamadas de inventário.
A abordagem de arquivo bridge
Se o script deve rodar em ambos, não o polua com if ESX then ... else ... end. Coloque toda chamada do framework em um arquivo, e tenha o resto do script chamar esse arquivo.
-- bridge.lua (server)
Bridge = {}
local framework
if GetResourceState('es_extended') == 'started' then
framework = 'esx'
ESX = exports['es_extended']:getSharedObject()
elseif GetResourceState('qb-core') == 'started' then
framework = 'qb'
QBCore = exports['qb-core']:GetCoreObject()
end
function Bridge.GetPlayer(src)
if framework == 'esx' then return ESX.GetPlayerFromId(src) end
return QBCore.Functions.GetPlayer(src)
end
function Bridge.AddCash(src, amount)
local player = Bridge.GetPlayer(src)
if not player then return false end
if framework == 'esx' then
player.addMoney(amount)
else
player.Functions.AddMoney('cash', amount)
end
return true
end
function Bridge.GetJob(src)
local player = Bridge.GetPlayer(src)
if not player then return nil end
if framework == 'esx' then
return player.job.name, player.job.grade
end
return player.PlayerData.job.name, player.PlayerData.job.grade.level
endNo manifesto, carregue-o antes dos arquivos que o usam:
server_scripts {
'bridge.lua',
'server.lua',
}O script então lê assim, e nunca menciona um framework:
RegisterNetEvent('my_script:pay', function()
local src = source
local job = Bridge.GetJob(src)
if job == 'mechanic' then
Bridge.AddCash(src, 100)
end
end)Adicione um bridge de cliente para notificações e dados de jogador do mesmo jeito. Quando você adiciona um novo framework, como QBox com qbx_core, você adiciona um branch no bridge, e o script fica como está.
Lembre-se que um bridge só funciona se o framework inicia primeiro. Coloque ensure es_extended ou ensure qb-core acima do script em server.cfg, e leia ESX is nil se o objeto volta nil.
Checklist
| Sintoma | Solução |
|---|---|
attempt to index a nil value (global 'ESX') em QBCore |
Uma chamada ESX ficou; substitua-a pelo mapa acima |
| Dinheiro ou job sempre nil | Você lê PlayerData em ESX, ou xPlayer.job em QBCore |
| Erros de contagem de item em QBCore | GetItemByName retorna nil quando o jogador não tem nenhum; verifique primeiro |
| Tabela errada em uma query | users e identifier em ESX, players e citizenid em QBCore |
| Callback retorna nada | Use as chamadas de register e trigger correspondentes para o framework |
| Precisa de ambos os frameworks | Mova toda chamada de framework para um arquivo bridge |
Respostas rápidas
Posso converter qualquer script ESX para QBCore?
Scripts que apenas usam o framework para jogadores, dinheiro, jobs, itens e notificações se convertem bem. Scripts construídos ao redor de recursos específicos do framework, como esx_society ou qb-management, precisam daquelas partes reescritas também.
O banco de dados muda quando mudo de framework?
Sim. ESX armazena jogadores em users com um identifier, e QBCore em players com um citizenid. Queries e nomes de tabela no script devem seguir o framework que você executa.
Um arquivo bridge é melhor que converter?
Se você precisa do script em ambos os frameworks, sim: você muda um arquivo, não o script inteiro. Se você apenas executa um framework, converta uma vez e descarte o outro branch.
Scripts sem esse problema
Item Creator V2Crie itens usáveis com animações, props, efeitos e muito mais — sem escrever código.Ver script →
Shop CreatorMonte uma loja em menos de um minuto — donos, funcionários, cofres e assaltos inclusos.Ver script →
Advanced BoostingBoosting de veículos pelo tablet: contratos da classe D à S+, crews e fila ao vivo.Ver script →