🌉Resource Bridge

Installation Guide for the `xmmx_bridge` resource!

The resource bridge is a free, powerful and extensible bridge utility built for FiveM developers and server owners. It serves as a central integration hub that abstracts and standardizes key gameplay systems such as framework interaction, inventory usage, target zone handling, progress bars, notifications, and draw text systems when using my resources.

Config

In the xmmx_bridge/shared/config.lua you must configure a few basic settings in order to prevent errors in my resources or the bridge itself.

Config.VersionCheck     = true
Config.Debug            = false

Config.Notify           = "qb"      -- "qb", "esx", "ox", "qbx", "custom"         -- edit in shared/notifications.lua
Config.Progress         = "qb"      -- "qb", "esx", "ox", "custom"         -- edit in client/utils/progress.lua
Config.TextUi           = "ox"       -- "ox", "qb", "custom"                -- edit in client/utils/drawtext.lua

-- This option only matters for qb-inventory users. Everyone else ignore.
Config.QBInventory      = "new"     -- "new" for new QB or "old" for older QB.

If using xmmx_bridge in other resources/scripts, you must declare at the XM variable at the top of your script. Example:

if GetResourceState('xmmx_bridge') == "started" then
    XM = exports.xmmx_bridge:ReturnObject()
end

This will allow use of any my functions found within the bridge resource.


Notifications

in the xmmx_bridge/shared/notifications.lua is where you can configure or customize notifications triggered from my resources. It is pre-configured for "qb", "esx", "ox", and "qbx. Note that iServer is passed as a variable so that you may add your server-sided exports or events for notifications.


XM.Notification = function(isServer, source, msg, _type, time)
    if Config.Notify == "qb" then
        if isServer then 
            if _type == "inform" then 
                QBCore.Functions.Notify(source, msg, "primary", time or 5000)
            else
                QBCore.Functions.Notify(source, msg, _type, time or 5000)
            end  
        else
            if _type == "inform" then 
                QBCore.Functions.Notify(msg, "primary", time or 5000)
            else
                QBCore.Functions.Notify(msg, _type, time or 5000)
            end      
        end  
    elseif Config.Notify == "qbx" then
        if isServer then 
            exports.qbx_core:Notify(source, msg, "inform", time or 5000)
        else
            exports.qbx_core:Notify(msg, "inform", time or 5000)
        end
    elseif Config.Notify == "esx" then
        if isServer then
            if _type == "inform" then 
                TriggerClientEvent('esx:showNotification', source, msg, 'info', time or 5000)
            else
                TriggerClientEvent('esx:showNotification', source, msg, _type, time or 5000)
            end  
        else
            if _type == "inform" then 
                ESX.ShowNotification(msg, 'info', time or 5000)
            else
                ESX.ShowNotification(msg, _type, time or 5000)
            end      
        end         
    elseif Config.Notify == "ox" then
        if isServer then 
            local data = {
                title = '', 
                description = msg, 
                type = _type, 
                duration = time or 5000, 
                style = { 
                    backgroundColor = '#000000a2', 
                    color = '#d6d6d6' 
                }, 
                position = "right-center",
            }
            TriggerClientEvent('ox_lib:notify', source, data)            
        else
            lib.notify({ 
                title = '', 
                description = msg, 
                type = _type, 
                duration = time or 5000, 
                style = { 
                    backgroundColor = '#000000a2', 
                    color = '#d6d6d6' 
                }, 
                position = "right-center", 
            })            
        end  
    elseif Config.Notify == "custom" then        
        -- Configure your notification here:
        if isServer then 
            -- uses source if triggered server-sided
        else

        end 
    end
end

Boss Menu & Duty Toggle

This module provides unified support for opening boss menus, toggling duty status, and checking duty status across different popular FiveM frameworks (qb-core, qbx_core, and es_extended). The functions dynamically detect the active framework and execute the appropriate logic, enabling compatibility and flexibility in multi-framework environments. Located in xmmx_bridge/client/editable/bossmenu.lua:

Functions:

XM.OpenBossMenu(data)

Opens the boss management menu for the player's job, depending on the active framework:

  • qb-core: Triggers qb-bossmenu:client:OpenMenu.

  • qbx_core: Uses exports.qbx_management:OpenBossMenu.

  • es_extended: Opens the ESX society boss menu via esx_society:openBossMenu.

XM.ToggleDuty()

Toggles the player's on-duty/off-duty status:

  • qb-core: Calls the standard QBCore:ToggleDuty server event.

  • qbx_core: Checks current duty state using GetPlayerData() and toggles it using a custom bridge event xmmx_bridge:server:qbxduty.

  • es_extended: Prints a debug message as duty toggling is not supported by default.

XM.CheckDuty()

Returns a boolean indicating whether the player is currently on duty:

  • qb-core and qbx_core: Reads the player's job status from GetPlayerData().

  • Fallback: Returns true if framework is unsupported or not explicitly handled.

XM.OpenBossMenu = function(data)
	if GetResourceState('qb-core') == "started" then 
		TriggerEvent("qb-bossmenu:client:OpenMenu")
    elseif GetResourceState('qbx_core') == "started" then
        exports.qbx_management:OpenBossMenu('job')
	elseif GetResourceState('es_extended') == "started" then
		TriggerEvent('esx_society:openBossMenu', data.society, function (menu)
			ESX.CloseContext() 
		end, {wash = false}) 
	end
end

XM.ToggleDuty = function()
	if GetResourceState('qb-core') == "started" then 
		TriggerServerEvent("QBCore:ToggleDuty")
    elseif GetResourceState('es_extended') == "started" then
        Print("[XMMX-DEBUG] Not Available by Default in ESX")
    elseif GetResourceState('qbx_core') == "started" then
        local playerData = exports.qbx_core:GetPlayerData()
        if playerData and playerData.job then
            local isOnDuty = playerData.job.onduty
            if isOnDuty then
                TriggerServerEvent("xmmx_bridge:server:qbxduty", false) -- go off duty
            else
                TriggerServerEvent("xmmx_bridge:server:qbxduty", true) -- go on duty
            end
        end        
	end
end

XM.CheckDuty = function()
    if GetResourceState('qb-core') == "started" then 
        local PlayerData = QBCore.Functions.GetPlayerData()
        if PlayerData and PlayerData.job then 
            return PlayerData.job.onduty
        end
        return false
    elseif GetResourceState('qbx_core') == "started" then 
        local PlayerData = exports.qbx_core:GetPlayerData()
        if PlayerData and PlayerData.job then
            return PlayerData.job.onduty
        end
        return false
    end
	return true
end

Callbacks

These functions provide a unified abstraction layer for triggering and registering server callbacks across multiple frameworks. By detecting which framework is active (qb-core, qbx_core, or es_extended), the bridge ensures compatibility and reduces the need for conditional logic elsewhere in your codebase.

Here’s a concise documentation description for the two functions found in xmmx_bridge/client/editable/callbacks.lua and xmmx_bridge/server/functions.lua:

Client-Side

XM.TriggerCallback(name, cb, ...)

Triggers a server callback based on the active framework:

  • qb-core: Uses QBCore.Functions.TriggerCallback.

  • qbx_core: Uses lib.callback.

  • es_extended: Uses ESX.TriggerServerCallback.

Parameters:

  • name (string) – The name of the callback to trigger.

  • cb (function) – The function to execute upon receiving the callback result.

  • ... – Additional arguments to pass to the server-side callback.

Server-Side

XM.CreateCallback(name, cb)

Registers a server callback function that can be triggered from the client:

  • qb-core: Uses QBCore.Functions.CreateCallback.

  • qbx_core: Uses lib.callback.register.

  • es_extended: Uses ESX.RegisterServerCallback.

Parameters:

  • name (string) – The identifier for the callback.

  • cb (function) – The server function to execute when the callback is called.

-- client:
XM.TriggerCallback = function(name, cb, ...)
    if GetResourceState('qb-core') == "started" then
        QBCore.Functions.TriggerCallback(name, cb, ...)
    elseif GetResourceState('qbx_core') == "started" then
        lib.callback(name, cb, ...)
    elseif GetResourceState('es_extended') == "started" then
        ESX.TriggerServerCallback(name, cb, ...)
    end
end

-- server:
XM.CreateCallback = function(name, cb)
    if GetResourceState('qb-core') == "started" then
        QBCore.Functions.CreateCallback(name, cb)
    elseif GetResourceState("qbx_core") == 'started' then
        lib.callback.register(name, cb)
    elseif GetResourceState('es_extended') == "started" then
        ESX.RegisterServerCallback(name, cb)
    end
end

Consumables

XM.Consume Function

The XM.Consume function handles consumption logic for items classified as food, drink, or alcohol across different FiveM frameworks (qb-core, qbx_core, and es_extended). It deducts the item from the player’s inventory, displays a progress bar, and applies corresponding effects like hunger, thirst, and stress relief.

Function Signature

XM.Consume(_type, itemName, Label, info, amount, playerId)

Parameters

  • _type (string) – Type of consumable: "eat", "drink", or "alcohol".

  • itemName (string) – The internal name of the item being consumed.

  • Label (string) – A readable name displayed during the progress bar animation.

  • info (table) – Optional metadata or animation info passed to the XM.Progress function.

  • amount (number) – The value to add to hunger/thirst or other needs.

  • playerId (number) – Server ID of the player consuming the item.

Behavior

  1. Item Deduction: Triggers xmmx_bridge:server:toggleItem to remove 1 unit of the item from the player's inventory.

  2. Progress Display: Shows a progress animation with a message based on the item type (e.g., "Eating Burger...").

  3. Effect Application: Once progress is complete, the function applies effects based on the active framework:

    • Food ("eat"):

      • Increases hunger (or esx_status equivalent).

    • Drink / Alcohol ("drink" / "alcohol"):

      • Increases thirst.

    • All types relieve a random amount of stress (2–4).

  4. Framework Handling:

    • qb-core / qbx_core:

      • Adjusts hunger or thirst using consumables:server:addHunger / addThirst.

      • Triggers stress relief via hud:server:RelieveStress.

    • es_extended:

      • Uses esx_status:add for hunger/thirst.

      • Still triggers the same stress relief event.

Returns

  • true if the item was successfully consumed and effects applied.

  • nil if the progress was interrupted or an unsupported type was passed.

XM.Consume = function(_type, itemName, Label, info, amount, playerId)
    if _type == "eat" then 
        TriggerServerEvent("xmmx_bridge:server:toggleItem", playerId, false, itemName, 1)

        local progDone = XM.Progress("Eating " .. Label .. " . . .", info)

        if progDone then     
            if GetResourceState('qb-core') == "started" then
                
                TriggerServerEvent("consumables:server:addHunger", QBCore.Functions.GetPlayerData().metadata["hunger"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('qbx_core') == "started" then
    
                TriggerServerEvent("consumables:server:addHunger", QBCore.Functions.GetPlayerData().metadata["hunger"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('es_extended') == "started" then
    
                TriggerEvent('esx_status:add', 'hunger', amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))            
            end

            return true
        end
    elseif _type == "drink" then
        TriggerServerEvent("xmmx_bridge:server:toggleItem", playerId, false, itemName, 1)

        local progDone = XM.Progress("Drinking " .. Label .. " . . .", info)

        if progDone then
            if GetResourceState('qb-core') == "started" then

                TriggerServerEvent("consumables:server:addThirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('qbx_core') == "started" then

                TriggerServerEvent("consumables:server:addThirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('es_extended') == "started" then

                TriggerEvent('esx_status:add', 'thirst', amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))            
            end

            return true
        end
    elseif _type == "alcohol" then 
        TriggerServerEvent("xmmx_bridge:server:toggleItem", playerId, false, itemName, 1)
        
        local progDone = XM.Progress("Drinking " .. Label .. " . . .", info)

        if progDone then
            if GetResourceState('qb-core') == "started" then

                TriggerServerEvent("consumables:server:addThirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('qbx_core') == "started" then

                TriggerServerEvent("consumables:server:addThirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))
            
            elseif GetResourceState('es_extended') == "started" then

                TriggerEvent('esx_status:add', 'thirst', amount)
                TriggerServerEvent('hud:server:RelieveStress', math.random(2, 4))            
            end

            return true
        end
    end
end

DrawText

Text UI Display Functions

These functions abstract the process of showing and hiding text-based UI prompts, allowing compatibility with multiple user interface systems (qb-core, ox_lib, es_extended). They provide a framework-agnostic way to display contextual messages on screen.

Function: XM.ShowText(data)

Displays a text-based UI message to the player, using the method defined in Config.TextUi.

Parameters:

  • data (table) – Contains the following fields:

    • text (string, required): The message to display.

    • position (string, optional): Screen position (e.g., "right", "right-center"). Defaults vary by system.

    • icon (string, optional): Icon to use (only for ox).

    • style (table, optional): CSS-like styling options (only for ox).

Framework-Specific Behavior:

  • qb: Uses qb-core’s DrawText with basic positioning.

  • ox: Uses lib.showTextUI with additional support for icons and styling.

  • esx: Calls ESX.TextUI with just the text string.

Function: XM.HideText()

Hides the currently displayed text UI, based on the configured UI framework.

Framework-Specific Behavior:

  • qb: Hides text using qb-core:HideText.

  • ox: Calls lib.hideTextUI.

  • esx: Uses ESX.HideUI.

These functions make switching between UI libraries seamless and centralized, ideal for projects that want to support multiple frameworks or migrate between them easily.


XM.ShowText = function(data)
    if Config.TextUi == "qb" then 
        exports['qb-core']:DrawText(data.text, data.position or "right")
    elseif Config.TextUi == "ox" then
        lib.showTextUI(data.text, {
            position = data.position or "right-center",
            icon = data.icon,
            style = data.style,
        })
    elseif Config.TextUi == "esx" then
        ESX.TextUI(data.text)
    end
end

XM.HideText = function()
    if Config.TextUi == "qb" then 
        exports['qb-core']:HideText()
    elseif Config.TextUi == "ox" then
        lib.hideTextUI()
    elseif Config.TextUi == "esx" then
        ESX.HideUI() 
    end
end

Hud / Radar Toggles

Function: XM.ShowHud(bool)

This function is responsible for toggling the visibility of the player HUD (Heads-Up Display) based on the currently active HUD resource. It supports multiple popular HUD systems and includes extension points for custom HUDs.

Parameters

  • bool (boolean)

    • true: Show the HUD.

    • false: Hide the HUD.

Framework-Specific Behavior

Depending on which HUD resource is active (i.e., its state is "started"), the function executes the corresponding visibility control:

Supported HUDs:

  • 17mov_Hud

    • ToggleDisplay(true/false): Shows or hides the entire HUD.

    • HideRadar(false/true): Shows or hides the minimap.

  • 0r-hud-v3

    • ToggleVisible(true/false): Inverted logic; false shows HUD, true hides it.

  • tgiann-lumihud

    • Triggers the event "tgiann-lumihud:ui" with a true or false value.

  • your_hud

    • Placeholder for custom HUD integration. Developers can add the appropriate export or event logic to support additional HUD systems.

Usage Example

XM.ShowHud(true)  -- Show HUD
XM.ShowHud(false) -- Hide HUD

This function simplifies HUD toggling by abstracting resource-specific commands and encouraging modular extension.


XM.ShowHud = function(bool)
    if bool then 
        if GetResourceState("17mov_Hud") == "started" then

            exports["17mov_Hud"]:ToggleDisplay(true) -- should hud be displayed?
            exports["17mov_Hud"]:HideRadar(false) -- should minimap be hidden?

        elseif GetResourceState("0r-hud-v3") == "started" then

            exports['0r-hud-v3']:ToggleVisible(false)

        elseif GetResourceState("tgiann-lumihud") == "started" then

            TriggerEvent("tgiann-lumihud:ui", true)

        elseif GetResourceState("your_hud") == "started" then -- add your hud name here

            -- add your hud export here to show your hud.

        end
    else
        if GetResourceState("17mov_Hud") == "started" then

            exports["17mov_Hud"]:ToggleDisplay(false) -- should hud be displayed?
            exports["17mov_Hud"]:HideRadar(true) -- should minimap be hidden?

        elseif GetResourceState("0r-hud-v3") == "started" then

            exports['0r-hud-v3']:ToggleVisible(true)

        elseif GetResourceState("tgiann-lumihud") == "started" then

            TriggerEvent("tgiann-lumihud:ui", false)

        elseif GetResourceState("your_hud") == "started" then -- add your hud name here

            -- add your hud export here to hide your hud.
            
        end
    end
end

Inventory Utilities

These functions are part of a cross-framework abstraction layer for managing inventory states, visibility, and item metadata across various supported inventory systems in FiveM.

Function: XM.InventoryBusy(bool)

Temporarily enables or disables the player's ability to interact with the inventory UI and hotkeys. This is useful during animations, progress bars, or any scripted events where the inventory should be locked.

Parameters

  • bool (boolean)true to mark the inventory as busy, false to re-enable access.

Framework-Specific Behavior

  • ox_inventory: Uses LocalPlayer.state.invBusy and invHotkeys to lock/unlock.

  • qb-inventory / codem-inventory (QB): Sets inv_busy flag via LocalPlayer.state:set.

  • qs-inventory: Uses setInventoryDisabled(true/false).

  • codem-inventory (ESX): Uses LocalPlayer.state.invBusy.

Function: XM.CloseInv()

Closes the player's inventory interface, depending on the inventory type defined in Config.Inventory.

Behavior by Config Setting

  • ox: Calls ox_inventory:closeInventory().

  • newQB, oldQB, codem: Executes closeinv command.

  • qs: Stubbed (does not call any close method due to ineffective documentation).

Function: XM.ConsumedItem(itemName)

Fetches the label (display name) of an item by its internal name to be used in UI feedback such as progress bars.

Parameters

  • itemName (string) – The internal name of the item to look up.

Returns

  • (string) – The label of the item, or "Something" if not found.

Framework-Specific Behavior

  • qb-core:

    • With ox_inventory: Calls ox_inventory:GetPlayerItems() and loops through to find item.

    • Otherwise: Retrieves from QBCore.Shared.Items.

  • qbx_core: Always uses ox_inventory.

  • es_extended:

    • Uses appropriate getInventory() export based on Config.Inventory: ox, qs, or codem.

These functions streamline inventory interactions in multi-framework environments, helping developers write framework-agnostic logic for immersive and safe player interactions.

XM.InventoryBusy = function(bool)
    if bool then 
        if GetResourceState('ox_inventory') == "started"then 
            LocalPlayer.state.invBusy = true
            LocalPlayer.state.invHotkeys = false
        elseif GetResourceState('qb-inventory') == "started" then 
            LocalPlayer.state:set('inv_busy', true, true)
        elseif Config.Inventory == "qs" then 
            exports['qs-inventory']:setInventoryDisabled(true)
        elseif (GetResourceState('codem-inventory') == "started" and GetResourceState("qb-core") == "started") then 
            LocalPlayer.state:set('inv_busy', true, true)
        elseif (GetResourceState('codem-inventory') == "started" and GetResourceState("es_extended") == "started") then 
            LocalPlayer.state.invBusy = true
        end
    else
        if GetResourceState('ox_inventory') == "started"then 
            LocalPlayer.state.invBusy = false
            LocalPlayer.state.invHotkeys = true
        elseif GetResourceState('qb-inventory') == "started" then 
            LocalPlayer.state:set('inv_busy', false, true)
        elseif Config.Inventory == "qs" then 
            exports['qs-inventory']:setInventoryDisabled(false)
        elseif (GetResourceState('codem-inventory') == "started" and GetResourceState("qb-core") == "started") then 
            LocalPlayer.state:set('inv_busy', false, true)
        elseif (GetResourceState('codem-inventory') == "started" and GetResourceState("es_extended") == "started") then 
            LocalPlayer.state.invBusy = false
        end
    end
end

XM.CloseInv = function()
    if Config.Inventory == "ox" then 
        exports.ox_inventory:closeInventory()
    elseif Config.Inventory == "newQB" or Config.Inventory == "oldQB" then 
        ExecuteCommand('closeinv')
    elseif Config.Inventory == "qs" then 
        -- ExecuteCommand('closeinv') -- in their docs but doesn't seem to work.
    elseif Config.Inventory == "codem" then 
        ExecuteCommand('closeinv')
    end
end

XM.ConsumedItem = function(itemName)   -- Function to get the label of item to show in progressbars.
    local itemLabel = "Something"
    if GetResourceState('qb-core') == "started" then
        if Config.Inventory == "ox" then 
            local playerItems = exports.ox_inventory:GetPlayerItems()
            for k, item in pairs(playerItems) do
                if item.name == itemName then
                    itemLabel = item.label
                    break
                end
            end
        else
            local item = QBCore.Shared.Items[itemName]
            if item then
                itemLabel = item.label
            end
        end
    elseif GetResourceState('qbx_core') == "started" then
        local playerItems = exports.ox_inventory:GetPlayerItems()
        for k, item in pairs(playerItems) do
            if item.name == itemName then
                itemLabel = item.label
                break
            end
        end
    elseif GetResourceState('es_extended') == "started" then
        if Config.Inventory == "ox" then 
            local playerItems = exports.ox_inventory:GetPlayerItems()
            for k, item in pairs(playerItems) do
                if item.name == itemName then
                    itemLabel = item.label
                    break
                end
            end
        elseif Config.Inventory == "qs" then 
            local playerItems = exports['qs-inventory']:getUserInventory()
            for k, item in pairs(playerItems) do
                if item.name == itemName then
                    itemLabel = item.label
                    break
                end
            end
        elseif Config.Inventory == "codem" then 
            local playerItems = exports['codem-inventory']:GetClientPlayerInventory()
            for k, item in pairs(playerItems) do
                if item.name == itemName then
                    itemLabel = item.label
                    break
                end
            end
        end
    end
    return itemLabel
end

Minigame

Function: XM.MiniGame(data)

Launches a mini key-press game using the xmmx_keysgame resource, providing a simple and configurable way to add skill-based interactions to your scripts (e.g., lockpicking, hacking, crafting, etc.).

Parameters

  • data (table) – Configuration for the mini-game, including:

    • mode (string) – Game difficulty or mode (as defined by the xmmx_keysgame export).

    • keys (number) – Number of keys the player must press.

    • time (number) – Total time allowed (in milliseconds or seconds, based on xmmx_keysgame implementation).

    • label (string) – Text displayed during the game.

    • color1 (string, optional) – Primary color in hex (default: "#00a79c").

    • color2 (string, optional) – Secondary color in hex (default: "#70c0bb").

Returns

  • success (boolean)true if the player completed the mini-game successfully, false if failed or aborted.

Example Usage

local result = XM.MiniGame({
    mode = "arrows",
    keys = 7,
    time = 5000,
    label = "Unlocking...",
    color1 = "#ffcc00",
    color2 = "#ff9900"
})

if result then
    print("Mini-game passed!")
else
    print("Mini-game failed.")
end

This function simplifies integrating interactive mini-games into your gameplay loop using standardized input and visuals.


XM.MiniGame = function(data)
    local success = exports.xmmx_keysgame:StartKeyGame(data.mode, data.keys, data.time, data.label, data.color1 or "#00a79c", data.color2 or"#70c0bb")

    return success
end

Player Job

These functions provide standardized, framework-agnostic ways to retrieve the player’s job and validate if they are an employee for a given in-world location. This supports dynamic job-based systems such as business management, shop zones, or role-restricted interactions.

Function: XM.EmployeeJob()

Returns the current job name of the player across supported frameworks.

Returns

  • (string or nil) – The player’s current job name (e.g., "police", "burgershot"), or nil if unavailable.

Framework Support

  • qb-core: Uses QBCore.Functions.GetPlayerData().

  • qbx_core: Uses exports.qbx_core:GetPlayerData().

  • es_extended: Uses ESX.GetPlayerData().

Function: XM.IsEmployee(Location)

Checks if the player is employed at any of the provided job locations within a 100.0 unit radius.

Parameters

  • Location (table) – A list of location/job definitions, each containing:

    • job (string) – The job name to match.

    • coords (vector3) – The location to check proximity against.

Returns

  • (boolean)true if the player has one of the specified jobs and is within range of the respective location, otherwise false.

Distance Check

  • A distance threshold of 100.0 units is used to determine if the player is "at" the location.

Example Location Table Format

local locations = {
    { job = "burgershot", coords = vector3(-1193.0, -892.0, 14.0) },
    { job = "mechanic", coords = vector3(544.0, -195.0, 54.0) }
}

These functions are useful for determining job-restricted access to areas, triggering boss menu logic, or controlling role-specific gameplay elements based on proximity and role.

XM.EmployeeJob = function()
    if GetResourceState('qb-core') == "started" then 
        local PlayerData = QBCore.Functions.GetPlayerData()
        return PlayerData.job.name 

    elseif GetResourceState('qbx_core') == "started" then 
        local PlayerData = exports.qbx_core:GetPlayerData()
        return PlayerData.job.name 

    elseif GetResourceState('es_extended') == "started" then
        local playerData = ESX.GetPlayerData()
        if playerData and playerData.job then
            return playerData.job.name
        end
    end
end

XM.IsEmployee = function(Location)
    local hasJob = false
    local playerPed = PlayerPedId()
    local playerCoords = GetEntityCoords(playerPed)

    for _, v in pairs(Location) do
        if v.job and v.coords then
            local distance = #(playerCoords - v.coords)
            local distanceThreshold = 100.0

            if distance <= distanceThreshold then
                if GetResourceState('qb-core') == "started" then 
                    local PlayerData = QBCore.Functions.GetPlayerData()
                    if PlayerData.job.name == v.job then 
                        hasJob = true
                    end

                elseif GetResourceState('qbx_core') == "started" then 
                    local PlayerData = exports.qbx_core:GetPlayerData()
                    if PlayerData.job.name == v.job then
                        hasJob = true
                    end

                elseif GetResourceState('es_extended') == "started" then
                    local playerData = ESX.GetPlayerData()
                    if playerData and playerData.job and playerData.job.name == v.job then 
                        hasJob = true
                    end
                end
            end
        end
    end

    return hasJob
end

Progress Bar

Function: XM.Progress(label, info)

Displays a progress bar animation tailored to the active framework (qb, ox, or esx). This function unifies progressbar behavior, including optional animations, props, and player interaction settings. It ensures the inventory is marked as "busy" during interaction to prevent conflicts with item usage.

Parameters

  • label (string) The message shown on the progress bar.

  • info (table, required) A configuration table containing animation and behavior options:

    • Time (number) – Duration of the progress (default: 5000 ms).

    • Move (boolean) – Whether player movement is allowed (default: false).

    • Dict, Clip, Flag (string/number) – Animation dictionary, clip name, and animation flags.

    • Prop, Prop2 (string) – Model names for props to attach.

    • Bone, Bone2 (number) – Bones to attach props to.

    • Coord, Coord2 (vector3) – World offsets for prop attachment.

    • Rot, Rot2 (vector3) – Rotation values for props.

Supported Frameworks & Behavior

qb-core

  • Uses QBCore.Functions.Progressbar.

  • Temporarily disables inventory via XM.InventoryBusy(true) during progress.

  • Supports animations and up to 2 props.

  • Synchronous behavior via while not isCompleted loop.

ox_lib

  • Uses lib.progressCircle.

  • Configurable props, position, and animations.

  • Returns immediately with success state.

es_extended

  • Uses ESX.Progressbar.

  • Supports freezing player and basic animation.

  • Uses onFinish/onCancel callbacks for result handling.

☑️ custom

  • Placeholder for custom progress implementation.

❌ Unsupported

  • Logs an error if Config.Progress is not recognized.

Returns

  • (boolean)true if the progress completed successfully, false if it failed or was aborted.

Example Usage

local result = XM.Progress("Making coffee...", {
    Time = 4000,
    Move = false,
    Dict = "amb@world_human_drinking@coffee@male@idle_a",
    Clip = "idle_c",
    Flag = 49,
    Prop = "prop_coffee_cup_trailer",
    Bone = 28422
})

if result then
    print("Progress completed!")
else
    print("Progress interrupted.")
end

This function enables immersive, framework-aware progress feedback for any interaction scenario.

XM.Progress = function(label, info)
    XM.TargetInactive()
    local success = false
    local isCompleted = false

    if not info or type(info) ~= "table" then
        print("^1[ERROR] Invalid info table passed to Progress function!^0")
        return false
    end

    local time = info.Time or 5000
    local move = info.Move or false

    if not Config or not Config.Progress then
        print("^1[ERROR] Config.Progress is nil!^0")
        return false
    end

    if Config.Progress == "qb" then
        XM.InventoryBusy(true)
        if not QBCore or not QBCore.Functions or not QBCore.Functions.Progressbar then
            print("^1[ERROR] QBCore Progressbar function is missing!^0")
            return false
        end

        QBCore.Functions.Progressbar("doing_bites", label, time, false, false, { 
            disableMovement = move, 
            disableCarMovement = false, 
            disableMouse = false, 
            disableCombat = false, 
        }, {
            animDict = info.Dict or "", 
            anim = info.Clip or "", 
            flags = info.Flag or 0, 
        }, {
            model = info.Prop or nil,
            bone = info.Bone or 0,
            coords = info.Coord and { 
                x = info.Coord.x or 0.0, 
                y = info.Coord.y or 0.0, 
                z = info.Coord.z or 0.0 
            } or nil,
            rotation = info.Rot and {
                x = info.Rot.x or 0.0,
                y = info.Rot.y or 0.0,
                z = info.Rot.z or 0.0
            } or nil,
        }, {
            model = info.Prop2 or nil,
            bone = info.Bone2 or 0,
            coords = info.Coord2 and { 
                x = info.Coord2.x or 0.0, 
                y = info.Coord2.y or 0.0, 
                z = info.Coord2.z or 0.0 
            } or nil,
            rotation = info.Rot2 and {
                x = info.Rot2.x or 0.0,
                y = info.Rot2.y or 0.0,
                z = info.Rot2.z or 0.0
            } or nil,
        }, function()
            success = true
            isCompleted = true
            XM.InventoryBusy(false)
        end) 

        while not isCompleted do
            Citizen.Wait(100)
        end

    elseif Config.Progress == "ox" then
        -- Ensure lib.progressCircle exists
        if not lib or not lib.progressCircle then
            print("^1[ERROR] lib.progressCircle function is missing!^0")
            return false
        end

        local options = {}

        if info.Prop then 
            table.insert(options, {
                model = info.Prop,
                bone = info.Bone or 0,
                pos = info.Coord or vector3(0.0, 0.0, 0.0),
                rot = info.Rot or vector3(0.0, 0.0, 0.0)
            })
        end

        if info.Prop2 then 
            table.insert(options, {
                model = info.Prop2,
                bone = info.Bone2 or 0,
                pos = info.Coord2 or vector3(0.0, 0.0, 0.0),
                rot = info.Rot2 or vector3(0.0, 0.0, 0.0)
            })
        end

        success = lib.progressCircle({
            duration = time,
            position = 'bottom',
            label = label,
            useWhileDead = false,
            canCancel = false,
            disable = {
                car = false,
                move = move,
            },
            anim = {
                dict = info.Dict or "",
                clip = info.Clip or "", 
                flag = info.Flag or 0,
                lockX = false,
                lockY = false,
                lockZ = false,
            },
            prop = options,
        }) 
    elseif Config.Progress == "esx" then
        if not ESX or not ESX.Progressbar then
            print("^1[ERROR] ESX.Progressbar function is missing!^0")
            return false
        end
    
        local animDict = info.Dict or ""
        local animClip = info.Clip or ""
        local freeze   = not (info.Move or false)
    
        ESX.Progressbar(label, time, {
            FreezePlayer = freeze,
            animation = {
                type = 'anim',
                dict = animDict,
                lib = animClip
            },
            onFinish = function()
                success = true
            end,
            onCancel = function()
                success = false
            end
        })
    
        while success == nil do
            Wait(100)
        end
    elseif Config.Progress == "custom" then
        -- add your custom progressbar here
    else
        print("^1[ERROR] Unsupported Progress Type: " .. tostring(Config.Progress) .. "^0")
    end  

    XM.TargetActive()
    return success
end

Targeting Controls

These functions enable or disable interaction targeting systems during scripted events, such as progress bars, animations, or menus—where interaction interruptions must be prevented. They support several popular targeting frameworks.

Function: XM.TargetInactive()

Disables player targeting temporarily by detecting the active targeting resource and calling its appropriate method.

Supported Frameworks & Behavior

  • ox_target: Calls disableTargeting(true)

  • qb-target: Calls AllowTargeting(false)

  • qtarget: Also uses AllowTargeting(false) (likely interchangeable export name)

Function: XM.TargetActive()

Re-enables player targeting after it has been disabled. Intended to be used following XM.TargetInactive() once the task (e.g., progress bar) is complete.

Supported Frameworks & Behavior

  • ox_target: Calls disableTargeting(false)

  • qb-target: Calls AllowTargeting(true)

  • qtarget: Calls AllowTargeting(true)

Use Case Example

Used alongside other functions (e.g., XM.Progress) to prevent interaction conflicts:

XM.TargetInactive()
-- show a progress bar, animation, etc.
XM.TargetActive()

These utilities provide seamless and framework-aware control over player interaction targeting to maintain immersion and prevent unintended interruptions.


XM.TargetInactive = function()
    if GetResourceState('ox_target') == 'started' then
        exports.ox_target:disableTargeting(true) 
    elseif GetResourceState('qb-target') == 'started' then 
        exports['qb-target']:AllowTargeting(false) 
    elseif GetResourceState('qtarget') == 'started' then 
        exports['qb-target']:AllowTargeting(false) 
    end
end

XM.TargetActive = function()
    if GetResourceState('ox_target') == 'started' then
        exports.ox_target:disableTargeting(false) 
    elseif GetResourceState('qb-target') == 'started' then 
        exports['qb-target']:AllowTargeting(true) 
    elseif GetResourceState('qtarget') == 'started' then 
        exports['qtarget']:AllowTargeting(true) 
    end
end

Target Box Zones

This system abstracts and standardizes interaction zones ("target zones") using popular target libraries in FiveM: ox_target, qb-target, and qtarget. It supports dynamic creation and removal of box-style target zones with custom options.

🔄 Initialization

CreateThread(function()
    -- Detect active target system
end)

Upon script startup, it detects the first available and supported target system (ox_target, qb-target, qtarget) and sets TargetSystem accordingly. If no target system is found, it logs a warning.

🔧 Function: XM.AddTargetZone(data)

Creates a new box-style interaction zone using the active target system.

Parameters

  • data (table) – Zone configuration:

    • name (string) – Unique identifier for the zone (required).

    • coords (vector3) – Zone center coordinates.

    • size (vector2) – Zone dimensions (x, y).

    • rotation (number) – Rotation angle in degrees.

    • debug (boolean) – Optional debug drawing.

    • drawSprite (boolean) – OX-specific: show sprite marker (default: true).

    • options (table) – List of target interaction options.

    • distance (number) – Interaction distance (for qb/qtarget only).

Returns

  • (string) – The name of the zone added, or nil if unsuccessful.

Behavior by Target System

  • ox_target: Uses ox_target:addBoxZone() with full support for size, rotation, sprite, and interaction options.

  • qb-target / qtarget: Uses AddBoxZone() with a heading and bounding box (minZ, maxZ), and adapts the options list:

    • Converts groups to job field.

    • Converts onSelect to action.

🗑️ Function: XM.RemoveTargetZone(name)

Removes a previously added target zone from the active target system.

Parameters

  • name (string) – The name of the zone to remove.

Behavior

  • ox_target: Calls removeZone(name)

  • qb-target / qtarget: Calls RemoveZone(name)

  • Removes the zone from the TargetZones registry.

🛑 Event Cleanup: onResourceStop

Automatically removes all registered target zones when the resource stops, ensuring no orphaned zones are left behind.

AddEventHandler("onResourceStop", function(resource)
    -- Clean up all active zones
end)

Example Usage

XM.AddTargetZone({
    name = "coffeeshop_register",
    coords = vector3(-1193.0, -892.0, 14.0),
    size = vector2(1.5, 1.5),
    rotation = 180.0,
    debug = true,
    options = {
        {
            icon = "fa-solid fa-mug-hot",
            label = "Order Coffee",
            onSelect = function() print("Coffee ordered!") end
        }
    }
})

Located in xmmx_bridge/client/targets/boxzones.lua:

local TargetZones = {}
local TargetSystem = nil

CreateThread(function()
    if GetResourceState("ox_target") == "started" then
        TargetSystem = "ox_target"
    elseif GetResourceState("qb-target") == "started" then
        TargetSystem = "qb-target"
    elseif GetResourceState("qtarget") == "started" then
        TargetSystem = "qtarget"
    else
        print("^1[XM][Target]^0 No supported target system found.")
    end
end)

XM.AddTargetZone = function(data)
    if not data or not data.name then return nil end
    if not TargetSystem then return nil end

    TargetZones[data.name] = true

    if TargetSystem == "ox_target" then
        exports.ox_target:addBoxZone({
            name       = data.name,
            coords     = data.coords,
            size       = data.size,
            rotation   = data.rotation,
            debug      = data.debug or false,
            drawSprite = data.drawSprite or true,
            options    = data.options or {}
        })

    elseif TargetSystem == "qb-target" or TargetSystem == "qtarget" then
        local options = {}

        for _, option in pairs(data.options or {}) do
            if option.groups then
                option.job = next(option.groups)
                option.groups = nil
            end

            if option.onSelect and not option.action then
                local callback = option.onSelect
                option.action = function(entity)
                    callback(option)
                end
                option.onSelect = nil
            end

            table.insert(options, option)
        end

        exports[TargetSystem]:AddBoxZone(data.name, data.coords, data.size.y, data.size.x, {
            name = data.name,
            heading = data.rotation,
            debugPoly = data.debug or false,
            minZ = data.coords.z - 1.25,
            maxZ = data.coords.z + 0.25
        }, {
            options = options,
            distance = data.distance or 2.0
        })
    end

    return data.name
end

XM.RemoveTargetZone = function(name)
    if not TargetZones[name] then return end

    if TargetSystem == "ox_target" then
        exports.ox_target:removeZone(name)
    elseif TargetSystem == "qb-target" or TargetSystem == "qtarget" then
        exports[TargetSystem]:RemoveZone(name)
    end

    TargetZones[name] = nil
end

AddEventHandler("onResourceStop", function(resource)
    if resource ~= GetCurrentResourceName() then return end

    for zone in pairs(TargetZones) do
        XM.RemoveTargetZone(zone)
    end
end)

Target Entities

This system allows you to dynamically assign interactive target options to entities (peds, vehicles, props, etc.) across supported target frameworks: ox_target, qb-target, and qtarget. It ensures clean initialization, unified behavior, and proper cleanup.

🔄 Initialization

CreateThread(function()
    -- Detect and set the first available supported target system
end)

When the script starts, it automatically sets TargetSystem based on which target resource is active. If none are found, a warning is printed to the console.

Function: XM.AddTargetEntity(entity, options)

Adds interactive targeting options to a specific entity (e.g., ped, object, or vehicle).

Parameters

  • entity (Entity handle) – The in-game entity to target (must exist).

  • options (table) – A list of targeting options:

    • label (string) – Text shown when aiming at the entity.

    • icon (string) – Icon shown in the target UI.

    • onSelect (function) – Callback triggered on selection (auto-converted to action for qb/qtarget).

    • groups (table, optional) – Jobs allowed to see this option.

    • distance (number) – Optional interaction distance (defaults to 2.0).

Behavior by Target System

  • ox_target:

    • Calls ox_target:addLocalEntity(entity, options) directly.

  • qb-target / qtarget:

    • Converts groups to job field.

    • Converts onSelect to action function.

    • Wraps options in a distance field and calls AddTargetEntity.

Example Usage

XM.AddTargetEntity(vehicle, {
    {
        icon = "fa-solid fa-key",
        label = "Unlock Car",
        distance = 2.0,
        onSelect = function()
            print("Car unlocked!")
        end
    }
})

Function: XM.RemoveTargetEntity(entity)

Removes an entity's targeting options if they were previously registered.

Parameters

  • entity (Entity handle) – The entity to deregister.

Behavior

  • Calls:

    • ox_target:removeLocalEntity(entity)

    • or qb-target/qtarget:RemoveTargetEntity(entity)

  • Cleans up EntityTargets table entry.

🧼 Resource Cleanup: onResourceStop

Automatically removes all entity targets registered during the resource's lifecycle when the script stops:

AddEventHandler("onResourceStop", function(resource)
    -- Remove all entity targets added by this script
end)

🗂️ Internals

  • EntityTargets (table) – Tracks all added entities for cleanup.

  • TargetSystem (string) – Holds the name of the active target resource (ox_target, qb-target, qtarget).

This system enables framework-agnostic entity targeting and maintains clean behavior through proper conversion and lifecycle handling. Located in xmmx_bridge/client/targets/targetentity.lua:

local EntityTargets = {}
local TargetSystem = nil

CreateThread(function()
    if GetResourceState("ox_target") == "started" then
        TargetSystem = "ox_target"
    elseif GetResourceState("qb-target") == "started" then
        TargetSystem = "qb-target"
    elseif GetResourceState("qtarget") == "started" then
        TargetSystem = "qtarget"
    else
        print("^1[XMMX-BRIDGE][Target]^0 No supported target system found.")
    end
end)

XM.AddTargetEntity = function(entity, options)
    if not entity or not DoesEntityExist(entity) or not options then return end

    local system = TargetSystem
    if not system then return end

    EntityTargets[entity] = true

    if system == "ox_target" then
        exports.ox_target:addLocalEntity(entity, options)

    elseif system == "qb-target" or system == "qtarget" then
        local parsedOptions = {}

        for _, option in ipairs(options) do
            if option.groups then
                option.job = next(option.groups)
                option.groups = nil
            end

            -- Convert onSelect to action for qb/qtarget
            if option.onSelect and not option.action then
                option.action = option.onSelect
                option.onSelect = nil
            end

            table.insert(parsedOptions, option)
        end

        exports[system]:AddTargetEntity(entity, {
            options = parsedOptions,
            distance = options[1] and options[1].distance or 2.0
        })
    end
end



XM.RemoveTargetEntity = function(entity)
    if not entity or not EntityTargets[entity] then return end

    local system = TargetSystem
    if not system then return end

    if system == "ox_target" then
        exports.ox_target:removeLocalEntity(entity)
    elseif system == "qb-target" or system == "qtarget" then
        exports[system]:RemoveTargetEntity(entity)
    end

    EntityTargets[entity] = nil
end

AddEventHandler("onResourceStop", function(resource)
    if resource ~= GetCurrentResourceName() then return end
    for ent in pairs(EntityTargets) do
        XM.RemoveTargetEntity(ent)
    end
end)

Interact

This module integrates with the interact resource to support model- and entity-based interaction zones in a structured and maintainable way. It allows defining interaction prompts tied to in-game models or entities and ensures automatic cleanup when the resource stops.

🧠 Internal State

  • InteractReady: Boolean flag to confirm the interact resource is active.

  • InteractTargets: Tracks registered interactions (model or entity) for cleanup.

🔁 Initialization

CreateThread(function()
    if GetResourceState("interact") == "started" then
        InteractReady = true
    else
        print("[XMMX-BRIDGE][INTERACT] 'interact' system not found or not started.")
    end
end)

Checks for interact resource availability and enables interaction functionality accordingly.

Function XM.AddInteractModel(data)

Registers an interaction target for one or more models.

Parameters

  • data (table) – Interaction configuration:

    • model (string or table) – One or more model names.

    • options (table) – List of interaction options (icon, label, onSelect, etc.).

    • offset, bone, distance, interactDst, groups, name, id (optional) – Additional interaction parameters.

Behavior

  • Iterates through all provided model names.

  • Generates a unique or custom interactionId for each.

  • Calls interact:AddModelInteraction for each model.

Function XM.AddInteractEntity(entity, data)

Registers an interaction for a specific game entity.

Parameters

  • entity (entity handle) – The in-game entity (ped, vehicle, prop, etc.).

  • data (table) – Includes:

    • options, distance, interactDst, name, offset, bone, ignoreLos, groups, id (optional)

Behavior

  • Builds an interaction configuration and calls interact:AddLocalEntityInteraction.

  • Stores reference in InteractTargets for later cleanup.

Function XM.RemoveInteract(id)

Removes a previously registered model or entity interaction.

Parameters

  • id (string) – The unique interaction ID to remove.

Behavior

  • If it was a model-based interaction: calls RemoveModelInteraction.

  • If it was entity-based: calls RemoveLocalEntityInteraction.

🧼 Event Handler onResourceStop Cleanup

Ensures all interact targets are properly removed if the resource is stopped:

AddEventHandler("onResourceStop", function(resource)
    for id in pairs(InteractTargets) do
        XM.RemoveInteract(id)
    end
end)

🧪 Example: Add Interaction to Trash Can Model

XM.AddInteractModel({
    model = "prop_bin_05a",
    name = "trash_interact",
    options = {
        {
            icon = "fas fa-trash",
            label = "Search Trash",
            onSelect = function()
                print("Searching the bin...")
            end
        }
    }
})

This integration provides a modular, extensible, and resource-safe method of handling interactions via the interact resource, promoting consistency and ease of use across your server scripts.

Located in xmmx_bridge/client/target/interact.lua:

if GetResourceState("interact") ~= "started" then return end

local InteractTargets = {}
local InteractReady = false

CreateThread(function()
    if GetResourceState("interact") == "started" then
        InteractReady = true
    else
        print("^1[XMMX-BRIDGE][INTERACT]^0 'interact' system not found or not started.")
    end
end)

---@param data table
---@param data.model can be a string or array of strings (model names)
---@param data.options = table of interaction options
---@param data.offset, distance, interactDst, name, id, etc. are optional
XM.AddInteractModel = function(data)
    if not InteractReady or not data or not data.model or not data.options then return end

    local models = type(data.model) == "table" and data.model or { data.model }

    for _, model in pairs(models) do
        local interactionId = data.id or ('XM_INTERACT_' .. model .. '_' .. tostring(math.random(1111, 9999)))

        InteractTargets[interactionId] = { type = "model", model = model }

        exports.interact:AddModelInteraction({
            model = model,
            offset = data.offset or vec3(0, 0, 0),
            bone = data.bone,
            name = data.name or interactionId,
            id = interactionId,
            distance = data.distance or 5.0,
            interactDst = data.interactDst or 2.0,
            groups = data.groups,
            options = data.options
        })
    end
end

XM.AddInteractEntity = function(entity, data)
    if not InteractReady or not entity or not data or not data.options then return end

    local interactionId = data.id or ('XM_ENTITY_INTERACT_' .. entity)
    InteractTargets[interactionId] = { type = "entity", entity = entity }

    exports.interact:AddLocalEntityInteraction({
        entity = entity,
        id = interactionId,
        name = data.name or interactionId,
        distance = data.distance or 5.0,
        interactDst = data.interactDst or 2.0,
        ignoreLos = data.ignoreLos or false,
        offset = data.offset or vec3(0, 0, 0),
        bone = data.bone,
        groups = data.groups,
        options = data.options
    })
end

XM.RemoveInteract = function(id)
    if not InteractTargets[id] then return end
    local target = InteractTargets[id]
    if target.type == "model" then
        exports.interact:RemoveModelInteraction(target.model, id)
    elseif target.type == "entity" then
        exports.interact:RemoveLocalEntityInteraction(target.entity, id)
    end
    InteractTargets[id] = nil
end

AddEventHandler("onResourceStop", function(resource)
    if resource ~= GetCurrentResourceName() then return end
    for id in pairs(InteractTargets) do
        XM.RemoveInteract(id)
    end
end)

Society Funds

These functions provide a unified way to add to or remove from a job/society’s shared account across various banking systems used in FiveM (qb, esx, okok, Renewed, etc.). This abstraction allows for compatibility across different frameworks without needing to change the business logic.

Function: XM.AddSocietyMoney(src, society, amount)

Adds funds to a society or job’s shared account.

Parameters

  • src (number) – Player source (unused in current logic, but may be useful for auditing or custom implementation).

  • society (string) – Name of the society or job (e.g., "police").

  • amount (number) – Amount of money to add.

Supported Banking Resources

  • qb-banking: AddMoney(society, amount)

  • fd_banking: AddMoney(society, amount)

  • Renewed-Banking: addAccountMoney(society, amount)

  • esx_society: esx_addonaccount:getSharedAccount("society_<name>")addMoney(amount)

  • okokbanking: AddMoney(society, amount)

  • your_society: Placeholder for custom implementation

Function: XM.RemoveSocietyMoney(society, amount, reason)

Removes funds from a society’s shared account.

Parameters

  • society (string) – Society or job name.

  • amount (number) – Amount to remove.

  • reason (string) – Reason for the deduction (used in some banking systems).

Supported Banking Resources

  • qb-banking: RemoveMoney(society, amount, reason)

  • fd_banking: RemoveMoney(society, amount, reason)

  • Renewed-Banking: RemoveAccountMoney(society, amount)

  • esx_society: esx_addonaccount:getSharedAccount("society_<name>")removeMoney(amount)

  • okokbanking: RemoveMoney(society, amount)

  • your_society: Placeholder for custom removal logic

🧪 Example Usage

XM.AddSocietyMoney(source, "mechanic", 500)
XM.RemoveSocietyMoney("mechanic", 250, "Parts Purchased")

These functions allow seamless switching or support for multiple banking frameworks without modifying core game logic. Let me know if you'd like helper logging or Discord webhooks added for auditing transactions! Located in xmmx_bridge/server/societies/society.lua:

XM.AddSocietyMoney = function(src, society, amount)
    if GetResourceState("qb-banking") == 'started' then            
        exports['qb-banking']:AddMoney(society, amount)
    elseif GetResourceState("fd_banking") == 'started' then            
        exports.fd_banking:AddMoney(society, amount)
    elseif GetResourceState("Renewed-Banking") == 'started' then            
        exports['Renewed-Banking']:addAccountMoney(society, amount)
    elseif GetResourceState("esx_society") == 'started' then       
        TriggerEvent('esx_addonaccount:getSharedAccount', 'society_'..society, function(account)
            if account then account.addMoney(amount) end
		end)
    elseif GetResourceState("okokbanking") == 'started' then
        exports['okokBanking']:AddMoney(society, amount)
    elseif GetResourceState("your_society") == 'started' then -- Add your society/banking script name here
        -- add your society Add Money export here.
    end
end

XM.RemoveSocietyMoney = function(society, amount, reason)
    if GetResourceState("qb-banking") == 'started' then            
        exports['qb-banking']:RemoveMoney(society, amount, reason)
    elseif GetResourceState("fd_banking") == 'started' then            
        exports.fd_banking:RemoveMoney(society, amount, reason)
    elseif GetResourceState("Renewed-Banking") == 'started' then            
        exports['Renewed-Banking']:RemoveAccountMoney(society, amount)
    elseif GetResourceState("esx_society") == 'started' then            
        TriggerEvent('esx_addonaccount:getSharedAccount', 'society_'..society, function(account)
            if account then account.removeMoney(amount) end
		end)
    elseif GetResourceState("okokbanking") == 'started' then
        exports['okokBanking']:RemoveMoney(society, amount)
    elseif GetResourceState("your_society") == 'started' then -- Add your society/banking script name here
        -- add your society Remove Money export here.
    end
end

🛡️Anti-Cheat

Function XM.ValidateCaller(expected, src, actionLabel) This function serves as a security utility to validate that a triggering source (src) matches the expected source (expected). It's designed to prevent unauthorized or spoofed event usage, acting as a lightweight anti-cheat mechanism.

Purpose

Ensures that sensitive server-side events are only executed by their rightful triggering client (e.g., validating a player's own action). If the validation fails, the player can be optionally dropped from the server, and a Discord webhook notification can be sent for admin review.

🔧 Parameters

Name
Type
Description

expected

number

The expected player source ID (usually the one originally associated with the action).

src

number

The actual player source that triggered the server event.

actionLabel

string

A label or name of the action being validated (used in logs/webhook).

🔁 Behavior

  1. Mismatch Detection If src does not match expected, an anti-cheat alert is triggered.

  2. Logging & Punishment

    • Prints a message to the server console.

    • Optionally drops the player from the server using DropPlayer.

    • Sends a Discord webhook alert if a valid Webhook URL is defined.

  3. Webhook Payload Structure

    • Player name, source ID, attempted event/action, and the expected source.

    • Timestamped for log tracking.

💬 Example Use

RegisterNetEvent("xmmx:secureAction", function(targetId)
    local src = source
    if not XM.ValidateCaller(targetId, src, "Secure Action Attempt") then return end

    -- Proceed with secure logic...
end)

🔒 Security Notes

  • Always use this check for sensitive actions triggered by the client (e.g., money handling, item giving, job switching).

  • Be cautious with DropPlayer() usage in development environments.

🧪 Optional Webhook Setup

To enable Discord alerts, configure the global Webhook variable:

local Webhook = "https://discord.com/api/webhooks/your_webhook_url"

Located in xmmx_bridge/server/anticheat/anticheat.lua:

local Webhook = ''

XM.ValidateCaller = function(expected, src, actionLabel)
    if expected ~= src then
        local playerName = GetPlayerName(src) or "unknown"
        local msg = ("^1[ANTI-CHEAT] ^3%s (%s) attempted unauthorized access to '%s'. Expected Source ID: (%s)^0"):format(playerName, src, actionLabel, expected)
        
        print(msg)

        DropPlayer(src, "[Anti-Cheat] Unauthorized server event usage detected.") -- drops player from server, remove if not needed!!!

        if Webhook and Webhook ~= '' then
            local payload = {
                username = "Anti-Cheat Logger",
                embeds = {{
                    title = "Unauthorized Server Event Usage",
                    color = 16711680,
                    fields = {
                        { name = "Player Name", value = playerName, inline = true },
                        { name = "Source", value = tostring(src), inline = true },
                        { name = "Event", value = actionLabel, inline = false },
                        { name = "Expected", value = tostring(expected), inline = true },
                    },
                    footer = { text = "XMMX Security" },
                    timestamp = os.date("!%Y-%m-%dT%H:%M:%SZ")
                }}
            }

            PerformHttpRequest(Webhook, function() end, "POST", json.encode(payload), {
                ["Content-Type"] = "application/json"
            })
        end

        return false
    end

    return true
end

Framework Functions

🧰 XM Server-Side Framework Utilities

These functions abstract common player-related actions across multiple popular FiveM frameworks—QBCore, ESX, and QB-X—providing a consistent interface for:

  • Cash and bank transactions

  • Inventory item management

  • Meta item support

  • Toggling items

  • Duty toggling (for qbx_core)

Each framework implementation is conditionally loaded depending on the active resources.

🧾 Common Function Definitions

Each framework implements these shared utility functions:

Function XM.AddMoney(src, amount, reason)

Adds money to the player’s cash wallet.

Function XM.RemoveMoney(src, amount, reason)

Removes money from the player’s wallet.

Function XM.AddBank(src, amount, reason)

Adds money to the player’s bank account.

Function XM.RemoveBank(src, amount, reason)

Removes money from the player’s bank account.

Function XM.AddItem(src, item, amount)

Gives the player a specific item.

Function XM.RemoveItem(src, item, amount)

Removes an item from the player's inventory.

Function XM.AddMetaItem(src, item, amount, meta)

Adds an item with metadata (supports various inventories like ox, qs, codem).

Function XM.ToggleItem(src, bool, item, amount)

Adds or removes an item based on the bool value.

🔷 QBCore Implementation

  • Framework: qb-core

  • Inventory support check: qb-inventory (for item box animation)

  • Meta item support: Handles ox_inventory, qs, codem, ak47, and qb-inventory

  • Uses QBCore.Functions.GetPlayer(src)

-- Add money
Player.Functions.AddMoney('cash', amount, reason)
-- Add item
Player.Functions.AddItem(item, amount)
-- Show item box (client)
TriggerClientEvent('qb-inventory:client:ItemBox', ...)

🔶 ESX Implementation

  • Framework: es_extended

  • Uses ESX.GetPlayerFromId(src)

  • Meta item support includes: ox_inventory, qs, codem, ak47_inventory

Player.addAccountMoney('money', amount)
Player.addInventoryItem(item, amount)

🔷 QBX (QBCore Extended) Implementation

  • Framework: qbx_core

  • Uses exports.qbx_core:*

  • Inventory: Primarily relies on ox_inventory

  • Duty toggle supported via:

    exports.qbx_core:SetJobDuty(src, bool)

❗ Note:

  • AddBank and RemoveBank are placeholders and need implementation based on your banking system.

🧪 Example Usage

XM.AddMoney(playerSrc, 500, "Mission Complete")
XM.RemoveItem(playerSrc, "lockpick", 1)
XM.AddMetaItem(playerSrc, "document", 1, { owner = "John Doe" })
XM.ToggleItem(playerSrc, true, "sandwich", 2) -- add item

Framework Bridge

This script enables a unified server-side interface to handle player-related logic like money, items, job checks, and usable items across QBCore, QB-X, and ESX frameworks. It allows modular and framework-agnostic scripting.

🔍 Framework Detection

local framework = nil

On resource load, it sets the framework variable based on what’s running:

  • "qb-core"

  • "qbx_core"

  • "es_extended"

🧰 XM Utility Functions

Function XM.GetPlayer(src)

Returns the player object from the active framework.

Function XM.GetPlayerCash(src)

Gets the player's current cash amount.

Function XM.GetPlayerBank(src)

Gets the player's bank account balance.

🧩 Function XM.CreateCallback(name, cb)

Registers a server callback compatible with the active framework:

  • QBCore.Functions.CreateCallback

  • lib.callback.register

  • ESX.RegisterServerCallback

🧪 Function XM.RegisterItemUseable(itemName, eventName)

Registers a usable item that triggers a client event.

  • Framework-specific registration:

    • QBCore.Functions.CreateUseableItem

    • exports.qbx_core:CreateUseableItem

    • ESX.RegisterUsableItem

👥 Function XM.GetPlayersNotInJobs(excludedJobs)

Returns a list of player IDs who are not in any of the specified jobs.

  • Useful for excluding certain roles (e.g., police or EMS) from missions.

👮 Function XM.PoliceCheck(jobs, count)

Registers a server callback xmmx_moneyruns:server:GetCopCount to verify if there are enough on-duty police (or specified jobs) on the server.

  • Works differently per framework, respecting each framework's job/duty system.

  • Returns true or false to the client via callback.

🔄 Inventory Toggle Handler

RegisterServerEvent("xmmx_bridge:server:toggleItem")

Server event used to add or remove an item from a player's inventory:

  • Performs anti-cheat validation with XM.ValidateCaller

  • Calls XM.ToggleItem() to process the item change

🛡️ Security Reminder

All sensitive server events like toggleItem are protected by XM.ValidateCaller, which ensures only the legitimate player can trigger the event.

✅ Example Usages

-- Add usable item
XM.RegisterItemUseable("medkit", "my_script:useMedkit")

-- Get player balance
local cash = XM.GetPlayerCash(src)

-- Trigger item toggle (server-side)
TriggerEvent("xmmx_bridge:server:toggleItem", src, true, "lockpick", 1)

-- Register server-side callback
XM.CreateCallback("my_script:getBalance", function(src, cb)
    cb(XM.GetPlayerBank(src))
end)

Last updated