EndCore Framework

Compatibility bridges

How en-core answers qb-core, qbx_core and es_extended exports so many existing scripts run on EndCore, what is covered, and the limits.

Plenty of FiveM scripts are written for QB-Core, Qbox or ESX. en-core includes compatibility bridges that let many of those scripts run on EndCore without edits. A QB script that calls exports['qb-core']:GetCoreObject() gets an object backed by EndCore's own players, money and jobs.

The bridges are for convenience, not full emulation. Simple scripts that read player data, move money and register usable items usually work. Scripts that depend on gangs, societies, billing or framework-specific events will need changes.

How it works

Three pieces work together:

  1. The manifest provides the names. en-core's fxmanifest.lua has provides { 'qb-core', 'qbx_core', 'es_extended' }, so a third-party manifest with dependency 'qb-core' is satisfied.
  2. Exports are answered under the other resource's name. FiveM resolves exports['qb-core']:GetCoreObject() by firing an internal event named __cfx_export_qb-core_GetCoreObject. The bridge listens for that event and hands back its own function. This is exactly what exports('GetCoreObject', fn) does internally, just pointed at another resource's name. The call reaches en-core even though qb-core is not installed.
  3. Each bridge checks whether it should run. If the real framework is started or starting, its bridge stays off and the console prints <resource> is running; skipping its compatibility bridge.

The library exposes the export trick as encore.provideExport(resource, name, fn):

lua
-- what encore.provideExport does
AddEventHandler(('__cfx_export_%s_%s'):format('qb-core', 'GetCoreObject'), function(setCB)
    setCB(function()
        return QBCore
    end)
end)
Note

The bridges never call exports() for these names. That would register the function under en-core itself and overwrite en-core's own exports, because Qbox uses the same export names, such as GetPlayer.

When a bridge is active, the console prints [EndCore] qb-core compatibility bridge active (and likewise for qbx_core and es_extended).

QB-Core

Shared

The global QBShared is always defined, even when the bridge is disabled.

FieldContents
JobsEndCore's shared/jobs.lua
Gangs{ none = { label = 'No Gang', ... } }
Vehicles, WeaponsEndCore's shared tables
ItemsEmpty. Items live in en-inventory.
GroupAsGang(group)Converts an EndCore group to a QB gang table
ForceJobUpdate, ForceGangUpdateAlso available as UpdateJob and UpdateGang. They fire qb-core:server:jobUpdate and qb-core:server:gangUpdate.
RandomStr, RandomInt, Trim, RoundHelpers

Server

Exports under qb-core: GetCoreObject, GetSharedObject, AddJob, UpdateJob, AddGang, UpdateGang. The global QBCoreObject is also set.

The QBCore object has:

AreaProvided
DataConfig (en-core's server config), Shared, and Players, a live view of the online registry
Player lookupsFunctions.GetPlayer, GetPlayerByCitizenId, GetOfflinePlayerByCitizenId, GetPlayers, GetQBPlayers (array), GetPlayerByPhone
CallbacksFunctions.CreateCallback, TriggerClientCallback, and TriggerCallback as an alias of TriggerClientCallback
ItemsFunctions.CreateUseableItem, routed to encore.inventory.registerUsable
AdminFunctions.Kick, HasPermission (ACE check, string or list), GetPermission (god, admin, mod or user via ACE), IsOptin (ACE admin), Notify
Jobs and gangsFunctions.AddJob, UpdateJob, AddGang, UpdateGang
CommandsCommands.Add(name, help, arguments, argsrequired, callback, permission)

Commands.Add splits the typed line into an array of strings. Any permission other than 'user' becomes restricted = 'group.<permission>'.

The player object is en-core's own player, so Player.Functions.AddMoney, SetJob, SetMetadata and the rest work unchanged. Player.PlayerData.gang is filled from the player's group on login and whenever the group changes:

SituationPlayerData.gang
In a group{ name = 'group_<id>', label = <group name>, isboss = <is leader>, grade = { name = <rank>, level = <grade> } }
Not in a group{ name = 'none', label = 'No Gang', ... }

Player.Functions.SetGang exists so scripts don't error, but it does nothing. It returns false and warns once.

Client

Exports under qb-core: GetCoreObject, GetSharedObject.

AreaProvided
DataQBCore.PlayerData, kept in sync
FunctionsGetPlayerData(cb?), Notify(text or { text, caption }, type, length) (primary becomes inform), CreateClientCallback, TriggerCallback
World helpersSpawnVehicle, DeleteVehicle, GetPlate, GetClosestPlayer, GetPlayersFromCoords, GetClosestVehicle, GetClosestPed, GetVehicles, GetPeds
Drawing and animationDrawText, DrawText3D, RequestAnimDict, PlayAnim, LoadModel
ProgressProgressbar(...), mapped onto encore.progress

Local events fired for QB scripts: QBCore:Client:OnPlayerLoaded, QBCore:Player:SetPlayerData, QBCore:Client:OnPlayerUnload, QBCore:Client:OnJobUpdate(job) and QBCore:Client:OnGangUpdate(gang).

QB limitations

  • No QB server events are fired. QBCore:Server:OnPlayerLoaded and similar never trigger. Use encore:server:onPlayerLoaded instead.
  • Server-side QBCore.Functions.Notify sends QBCore:Notify to the client, but no client handler is registered, so those notifications do not show.
  • No gangs beyond the group mapping above.
  • QBShared.Items is empty. Read item data from en-inventory.

Qbox (QBX)

Server exports under qbx_core

ExportNotes
GetPlayer, GetPlayerByCitizenId, GetOfflinePlayer, GetPlayersen-core's player objects
Login(source, citizenid?, newData?)Returns boolean. With no citizenid, creates a character from newData and logs in.
Logout, SaveAllPlayers
GetJobs, GetVehicles, GetWeaponsEndCore's shared tables
GetGangsReturns {}
GetPlayerCharacters(license)
DeleteCharacter(citizenid)Deletes the row directly. Unlike en-core's own DeleteCharacter, it does not remove the character from its group first.

Client exports under qbx_core

GetPlayerData, IsLoggedIn, GetJobs, GetGangs (returns {}), GetVehicles, GetWeapons. The global QBX.PlayerData is kept in sync.

QBX limitations

Only the exports listed above exist. Qbox's other exports, such as setting jobs or money by source and its group functions, are not provided. Use en-core's own exports for those.

ESX

Shared

ESX.Jobs (converted, with salary taken from payment), ESX.Gangs = {}, ESX.Vehicles, ESX.Weapons and ESX.GetConfig(). GetConfig returns a fixed table: its StartingAccountMoney is always 500 cash and 1000 bank and does not read your config.

The export es_extended:getSharedObject works on both server and client.

Server

FunctionNotes
ESX.GetPlayerFromId(source)Returns an xPlayer
ESX.GetPlayerFromIdentifier(license)The online player first, otherwise the most recently updated character as an offline xPlayer
ESX.GetExtendedPlayers(), ESX.GetPlayers()
ESX.RegisterServerCallback, ESX.TriggerClientCallback
ESX.RegisterUsableItemRouted to encore.inventory.registerUsable
ESX.RegisterCommand(name, group, cb, allowConsole, suggestion)Groups other than user become group.<name> principals. player arguments are converted to xPlayer objects.
ESX.SavePlayer, ESX.SavePlayers
ESX.UseItem, ESX.CreatePickupStubs that only print a warning

xPlayer

AreaProvided
Fieldssource, identifier (the license), license, name, job (ESX shape), coords
Identity and positiontriggerEvent, getIdentifier, getName, setCoords, getCoords, kick
MoneygetMoney, setMoney, addMoney, removeMoney, getAccount, getAccounts, addAccountMoney, removeAccountMoney, setAccountMoney
InventorygetInventoryItem, addInventoryItem, removeInventoryItem, setInventoryItem, canCarryItem, canSwapItem
WeightgetWeight always returns 0, getMaxWeight returns 24000
JobsetJob, getJob
NotificationsshowNotification, showAdvancedNotification, showHelpNotification
MetadatagetMeta, setMeta, getMetas, setMetas

ESX's money account is EndCore's cash (the cash item when cash is an item). ESX's bank is EndCore's bank.

Events translated for ESX scripts:

  • esx:playerLoaded(source, xPlayer) fires on the server and is sent to the client.
  • esx:playerDropped(source) fires when a character unloads.
  • esx:setJob is sent to the client on job changes.
  • esx:setAccountMoney({ name, money }) is sent to the client with the new balance.

Client

AreaProvided
DataESX.PlayerData, ESX.PlayerLoaded, ESX.GetPlayerData() (accounts money and bank), ESX.IsPlayerLoaded, ESX.SetPlayerData
NotificationsESX.ShowNotification, ESX.ShowAdvancedNotification, ESX.ShowHelpNotification, ESX.UI.ShowInventoryItemNotification
CallbacksESX.TriggerServerCallback, ESX.RegisterClientCallback
ESX.GameGetClosestPlayer, GetPlayersInArea, GetClosestVehicle, GetVehiclesInArea, GetClosestObject, SpawnVehicle, DeleteVehicle, Teleport, IsSpawnPointClear
ESX.StreamingRequestModel, RequestStreamedTextureDict, RequestStreamSet, RequestAnimDict, RequestAnimSet, RequestNamedPtfxAsset

Local events fired: esx:playerLoaded, esx:onPlayerLogout, esx:setJob, esx:setAccountMoney.

ESX limitations

  • No societies, billing, licences, properties or pickups.
  • No real inventory weight.
  • grade_name and grade_label on the job are approximations.
  • canSwapItem is approximate.

Examples

An unmodified QB script:

lua
local QBCore = exports['qb-core']:GetCoreObject()

RegisterNetEvent('myscript:server:payout', function()
    local Player = QBCore.Functions.GetPlayer(source)
    if Player then
        Player.Functions.AddMoney('bank', 200, 'qb-script payout')
    end
end)

An unmodified ESX script:

lua
local ESX = exports['es_extended']:getSharedObject()

ESX.RegisterUsableItem('bandage', function(source)
    local xPlayer = ESX.GetPlayerFromId(source)
    xPlayer.removeInventoryItem('bandage', 1)
    xPlayer.addAccountMoney('money', 50) -- 'money' is EndCore cash
end)

Disabling the bridges

To turn off all three bridges, add this to server.cfg:

cfg
setr encore:disablebridge true

Use setr, not set. The client bridges read the convar too, and only setr replicates it to clients. The QBShared global is still defined when bridges are disabled.

You do not need to disable a bridge to run the real framework: if qb-core, qbx_core or es_extended is actually started, en-core skips that bridge on its own.

For a step-by-step guide to porting scripts, see Running QB and ESX scripts.