Explorer
KNOW-PAT-102

FiveM - Dual Framework Bridge Functions : ESX/QBCore wrapper unifie

Domaine
framework
Type
pattern
Priorité
P1

Parent : [[INDEX-FIVEM-FRAMEWORK]]

FiveM - Dual Framework Bridge Functions : ESX/QBCore wrapper unifié

Problème

Un script doit fonctionner avec ESX et QBCore mais les APIs diffèrent : xPlayer.getMoney() vs player.PlayerData.money.cash, ESX.GetPlayerFromId vs QBCore.Functions.GetPlayer.

Solution

Abstraction complète avec fonctions wrapper GetPlayer, GetPlayerIdentifier, GetPlayerName, GetPlayerCash/Bank, Add/Remove Money, centralisées dans un fichier bridge.

-- bridge.lua
local FrameworkName = nil
local Framework = nil

local function DetectFramework()
    if GetResourceState('es_extended') == 'started' then
        FrameworkName = 'esx'
        Framework = exports['es_extended']:getSharedObject()
    elseif GetResourceState('qb-core') == 'started' then
        FrameworkName = 'qbcore'
        Framework = exports['qb-core']:GetCoreObject()
    end
end

-- Attendre le chargement du framework
CreateThread(function()
    while FrameworkName == nil do
        DetectFramework()
        Wait(1000)
    end
    print('[^2INFO^7] Framework detected: ' .. FrameworkName)
end)

-- Player getters
function GetPlayer(source)
    if FrameworkName == 'esx' then
        return Framework.GetPlayerFromId(source)
    elseif FrameworkName == 'qbcore' then
        return Framework.Functions.GetPlayer(source)
    end
end

function GetPlayerIdentifier(player)
    if FrameworkName == 'esx' then
        return player.getIdentifier()
    elseif FrameworkName == 'qbcore' then
        return player.PlayerData.citizenid
    end
end

function GetPlayerName(player)
    if FrameworkName == 'esx' then
        return player.getName()
    elseif FrameworkName == 'qbcore' then
        return player.PlayerData.charinfo.firstname .. ' ' .. player.PlayerData.charinfo.lastname
    end
end

-- Money getters/setters
function GetPlayerCash(player)
    if FrameworkName == 'esx' then
        return player.getMoney()
    elseif FrameworkName == 'qbcore' then
        return player.PlayerData.money.cash
    end
end

function GetPlayerBank(player)
    if FrameworkName == 'esx' then
        return player.getAccount('bank').money
    elseif FrameworkName == 'qbcore' then
        return player.PlayerData.money.bank
    end
end

function AddPlayerMoney(player, amount, moneyType)
    moneyType = moneyType or 'cash'
    if FrameworkName == 'esx' then
        if moneyType == 'cash' then
            player.addMoney(amount)
        else
            player.addAccountMoney(moneyType, amount)
        end
    elseif FrameworkName == 'qbcore' then
        player.Functions.AddMoney(moneyType, amount)
    end
end

function RemovePlayerMoney(player, amount, moneyType)
    moneyType = moneyType or 'cash'
    if FrameworkName == 'esx' then
        if moneyType == 'cash' then
            player.removeMoney(amount)
        else
            player.removeAccountMoney(moneyType, amount)
        end
    elseif FrameworkName == 'qbcore' then
        player.Functions.RemoveMoney(moneyType, amount)
    end
end

-- Notification
function Notify(source, msg, type)
    if FrameworkName == 'esx' then
        TriggerClientEvent('esx:showNotification', source, msg)
    elseif FrameworkName == 'qbcore' then
        TriggerClientEvent('QBCore:Notify', source, msg, type or 'primary')
    end
end

Pourquoi

  • Un seul fichier bridge à modifier si l'API change
  • Le reste du script utilise GetPlayer, AddPlayerMoney → framework-agnostic
  • Détection automatique au démarrage

Quant l'utiliser

  • Scripts multi-framework (commercial, open-source)
  • Scripts internes qui pourraient migrer de framework

Quand NE PAS l'utiliser

  • Scripts ESX-only ou QB-only (overhead inutile)

Exemples

Correct

-- Usage dans le script métier
RegisterNetEvent('banking:transfer')
AddEventHandler('banking:transfer', function(targetId, amount)
    local src = source
    local sender = GetPlayer(src)
    local receiver = GetPlayer(targetId)
    
    if not sender or not receiver then return end
    
    local senderBank = GetPlayerBank(sender)
    if senderBank < amount then
        Notify(src, 'Solde insuffisant', 'error')
        return
    end
    
    RemovePlayerMoney(sender, amount, 'bank')
    AddPlayerMoney(receiver, amount, 'bank')
    
    Notify(src, ('Transféré $%s'):format(amount), 'success')
    Notify(targetId, ('Reçu $%s'):format(amount), 'success')
end)

Incorrect

-- Hardcodé ESX → incompatible QBCore
RegisterNetEvent('banking:transfer')
AddEventHandler('banking:transfer', function(target, amount)
    local xPlayer = ESX.GetPlayerFromId(source)
    local xTarget = ESX.GetPlayerFromId(target)
    if xPlayer.getAccount('bank').money >= amount then
        xPlayer.removeAccountMoney('bank', amount)
        xTarget.addAccountMoney('bank', amount)
    end
end)

Références

  • omes_banking/server.lua L1-80
  • omes_banking/client.lua L1-80
  • KNOW-PAT-084 (Framework Bridge Auto-Detect)