Convertir un script de ESX a QBCore (y viceversa): mapa de funciones y puente
Porta un script de FiveM entre ESX y QBCore: una tabla de las llamadas comunes (jugador, dinero, trabajo, objetos, notificaciones, callbacks) y un fichero puente que soporta ambos.
Tienes un script escrito para ESX, y tu servidor ejecuta QBCore, o al revés. La mayoría del script es Lua simple y natives que no cambian. Solo cambian las llamadas del framework, y hay menos de las que esperas. Este artículo te da un mapa de ellas, y luego te muestra cómo aislarlas en un fichero puente.
Qué permanece y qué cambia
Natives, NUI, tus propios eventos, bucles, objetivos y menús funcionan en cualquier framework. Lo que cambia es una lista corta:
- cómo obtienes el objeto del framework
- cómo obtienes un jugador
- dinero, trabajos y objetos
- notificaciones
- objetos usables y callbacks
- las tablas de base de datos para jugadores
Busca ESX. y xPlayer en el script, y cada coincidencia es algo para convertir.
Mapa de funciones
| Tarea | ESX Legacy | QBCore |
|---|---|---|
| Obtener el objeto | ESX = exports['es_extended']:getSharedObject() |
local QBCore = exports['qb-core']:GetCoreObject() |
| Obtener un jugador (servidor) | ESX.GetPlayerFromId(src) |
QBCore.Functions.GetPlayer(src) |
| Identificador del jugador | xPlayer.identifier |
Player.PlayerData.citizenid |
| Saldo de efectivo | xPlayer.getMoney() |
Player.PlayerData.money.cash |
| Añadir efectivo | xPlayer.addMoney(n) |
Player.Functions.AddMoney('cash', n) |
| Eliminar efectivo | xPlayer.removeMoney(n) |
Player.Functions.RemoveMoney('cash', n) |
| Banco añadir | xPlayer.addAccountMoney('bank', n) |
Player.Functions.AddMoney('bank', n) |
| Banco eliminar | xPlayer.removeAccountMoney('bank', n) |
Player.Functions.RemoveMoney('bank', n) |
| Nombre del trabajo | xPlayer.job.name |
Player.PlayerData.job.name |
| Grado del trabajo | xPlayer.job.grade |
Player.PlayerData.job.grade.level |
| Añadir objeto | xPlayer.addInventoryItem(name, n) |
Player.Functions.AddItem(name, n) |
| Eliminar objeto | xPlayer.removeInventoryItem(name, n) |
Player.Functions.RemoveItem(name, n) |
| Contador de objetos | xPlayer.getInventoryItem(name).count |
Player.Functions.GetItemByName(name) (nil si no hay, luego .amount) |
| Objeto usable | 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') |
| Datos del jugador (cliente) | ESX.GetPlayerData() |
QBCore.Functions.GetPlayerData() |
| Evento de jugador cargado | esx:playerLoaded |
QBCore:Client:OnPlayerLoaded |
| Evento de trabajo cambiado | esx:setJob |
QBCore:Client:OnJobUpdate |
| Callback del servidor | ESX.RegisterServerCallback |
QBCore.Functions.CreateCallback |
Dos detalles engañan a la gente. Tipos de dinero: ESX tiene money para efectivo y bank, QBCore usa 'cash' y 'bank' como el primer argumento. Y el grado: ESX da un número simple, QBCore una tabla, así que job.grade.level.
Los callbacks, que cada segundo script usa, se comparan en detalle en server callbacks on ESX, QBCore and ox_lib.
La base de datos
Las tablas de jugadores difieren, así que cualquier SELECT o UPDATE en ellas necesita un cambio:
| ESX | QBCore | |
|---|---|---|
| Jugadores | users |
players |
| Clave de jugador | identifier (una cadena license:) |
citizenid |
| Vehículos | owned_vehicles |
player_vehicles |
Un script que almacena sus propios datos, con sus propias tablas, normalmente sigue funcionando, siempre y cuando clave las filas en un valor que puedas obtener de cualquiera de los dos frameworks. Usar el identificador de licencia para eso funciona en ambos.
QBox
QBox está más cerca de QBCore que de ESX. Su core es qbx_core, mantiene una capa de compatibilidad para exports['qb-core']:GetCoreObject(), y su inventario es ox_inventory. Un script convertido a QBCore normalmente se ejecuta en QBox con pocos cambios, aparte de las llamadas de inventario.
El enfoque del fichero puente
Si el script debe ejecutarse en ambos, no lo ensucies con if ESX then ... else ... end. Pon cada llamada del framework en un fichero, y haz que el resto del script llame a ese fichero.
-- 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
endEn el manifest, cárgalo antes de los ficheros que lo usan:
server_scripts {
'bridge.lua',
'server.lua',
}El script entonces se lee así, y nunca menciona un framework:
RegisterNetEvent('my_script:pay', function()
local src = source
local job = Bridge.GetJob(src)
if job == 'mechanic' then
Bridge.AddCash(src, 100)
end
end)Añade un puente del cliente para notificaciones y datos del jugador de la misma forma. Cuando añadas un nuevo framework, como QBox con qbx_core, añades una rama en el puente, y el script permanece como está.
Recuerda que un puente solo funciona si el framework inicia primero. Pon ensure es_extended o ensure qb-core arriba del script en server.cfg, y lee ESX is nil si el objeto viene de vuelta nil.
Lista de verificación
| Síntoma | Solución |
|---|---|
attempt to index a nil value (global 'ESX') en QBCore |
Queda una llamada de ESX; reemplázala del mapa anterior |
| Dinero o trabajo siempre nil | Lees PlayerData en ESX, o xPlayer.job en QBCore |
| Errores de contador de objetos en QBCore | GetItemByName devuelve nil cuando el jugador no tiene ninguno; compruébalo primero |
| Tabla incorrecta en una consulta | users e identifier en ESX, players y citizenid en QBCore |
| Callback no devuelve nada | Usa las llamadas de registro y disparo coincidentes para el framework |
| Necesita ambos frameworks | Mueve cada llamada del framework a un fichero puente |
Respuestas rápidas
¿Puedo convertir cualquier script de ESX a QBCore?
Los scripts que solo usan el framework para jugadores, dinero, trabajos, objetos y notificaciones se convierten bien. Los scripts construidos alrededor de recursos específicos del framework, como esx_society o qb-management, necesitan que esas partes se reescriban también.
¿Cambia la base de datos cuando cambio de framework?
Sí. ESX almacena jugadores en users con un identifier, y QBCore en players con un citizenid. Las consultas y nombres de tabla en el script deben seguir el framework que ejecutas.
¿Es mejor un fichero puente que convertir?
Si necesitas el script en ambos frameworks, sí: cambias un fichero, no el script completo. Si solo ejecutas un framework, convierte una vez y descarta la otra rama.
Scripts que evitan este problema
Item Creator V2Crea items usables con animaciones, props, efectos y más — sin escribir código.Ver script →
Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →
Advanced BoostingBoosting de vehículos desde una tablet: contratos de clase D a S+, crews y cola en vivo.Ver script →