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.

lua
-- 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
end

En el manifest, cárgalo antes de los ficheros que lo usan:

lua
server_scripts {
    'bridge.lua',
    'server.lua',
}

El script entonces se lee así, y nunca menciona un framework:

lua
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 →

Sigue leyendo