2025-06-06 00:50:13 +01:00
--[[
Cached Resource Initialization Module
--------------------------------------
2025-06-11 16:30:29 +01:00
Initializes shared resource data ( Items , Vehicles , Jobs , Gangs ) once and caches it
to improve performance and reduce redundant data fetching across scripts .
2025-06-06 00:50:13 +01:00
] ]
2025-06-11 16:30:29 +01:00
-- Define resource names used in different frameworks
2025-06-06 00:50:13 +01:00
local Exports = {
QBExport = " qb-core " ,
QBXExport = " qbx_core " ,
ESXExport = " es_extended " ,
OXCoreExport = " ox_core " ,
OXInv = " ox_inventory " ,
QBInv = " qb-inventory " ,
PSInv = " ps-inventory " ,
QSInv = " qs-inventory " ,
CoreInv = " core_inventory " ,
CodeMInv = " codem-inventory " ,
OrigenInv = " origen_inventory " ,
TgiannInv = " tgiann-inventory " ,
2025-06-13 23:25:32 +01:00
JPRInv = " jpr-inventory " ,
2025-06-06 00:50:13 +01:00
OXLibExport = " ox_lib " ,
QBMenuExport = " qb-menu " ,
QBTargetExport = " qb-target " ,
OXTargetExport = " ox_target " ,
-- REDM
RSGExport = " rsg-core " ,
RSGInv = " rsg-inventory "
}
2025-06-11 16:30:29 +01:00
-- Prevent reloading if cache is already initialized
2025-06-26 20:03:12 +01:00
local cache = {
Items = { } ,
Vehicles = { } ,
Jobs = { } ,
Gangs = { } ,
}
2025-06-06 00:50:13 +01:00
2025-06-11 16:30:29 +01:00
-- Helper function to check if resource exists in server (instead of if it is already started)
local function checkExists ( resourceName )
2025-06-06 00:50:13 +01:00
return GetResourceState ( resourceName ) : find ( " start " ) or GetResourceState ( resourceName ) : find ( " stopped " )
end
2025-06-11 16:30:29 +01:00
-- Ensure oxmysql resource is loaded
2025-06-06 00:50:13 +01:00
local fileLoader = assert ( load ( LoadResourceFile ( " oxmysql " , ( ' lib/MySQL.lua ' ) ) , ( ' @@oxmysql/lib/MySQL.lua ' ) ) )
fileLoader ( )
2025-06-11 16:30:29 +01:00
2025-06-06 00:50:13 +01:00
if checkExists ( Exports.OXCoreExport ) then
-- Detected OX_Core in server, wait for it to be started if needed
while GetResourceState ( Exports.OXCoreExport ) ~= " started " do Wait ( 100 ) end
local fileLoader = assert ( load ( LoadResourceFile ( Exports.OXCoreExport , ( ' lib/init.lua ' ) ) , ( ' @@ ' .. Exports.OXCoreExport .. ' /lib/init.lua ' ) ) )
fileLoader ( )
end
if checkExists ( Exports.ESXExport ) then
-- Detected ESX in server, wait for it to be started if needed
while GetResourceState ( Exports.ESXExport ) ~= " started " do Wait ( 100 ) end
local fileLoader = assert ( load ( LoadResourceFile ( Exports.ESXExport , ( ' /imports.lua ' ) ) , ( ' @@ ' .. Exports.ESXExport .. ' /imports.lua ' ) ) )
fileLoader ( )
end
2025-06-11 16:30:29 +01:00
-- Initialize variables for caching
2025-06-20 17:47:54 +01:00
local itemResource , jobResource , vehResource = " N/A " , " N/A " , " N/A "
2025-06-06 00:50:13 +01:00
-- Print just to announce it knows the exports/scripts exist in the server
for _ , v in pairs ( Exports ) do
if checkExists ( v ) then
print ( " ^6Bridge^7: '^3 " .. v .. " ^7' detected " )
end
end
---------------------
---- Load Items -----
---------------------
2025-06-11 16:30:29 +01:00
-- Items initialization based on detected inventory system
2025-06-06 00:50:13 +01:00
if checkExists ( Exports.OXInv ) then
-- Wait for OX Inventory to start if it's not already started
while GetResourceState ( Exports.OXInv ) ~= " started " do Wait ( 100 ) end
itemResource = Exports.OXInv
2025-06-20 17:47:54 +01:00
local success , result = pcall ( function ( )
return exports [ Exports.OXInv ] : Items ( )
end )
if success and result then
2025-06-26 20:03:12 +01:00
cache.Items = result
2025-06-20 17:47:54 +01:00
end
2025-06-06 19:36:12 +01:00
-- Get Weapon info and duplicate them if they are uppercase
-- (duplicate incase anything checks for the uppercase version)
2025-06-26 20:03:12 +01:00
for k , v in pairs ( cache.Items ) do
2025-06-20 17:47:54 +01:00
if type ( k ) == " string " then
if k : find ( " WEAPON " ) then
2025-06-26 20:03:12 +01:00
cache.Items [ k : lower ( ) ] = cache.Items [ k ]
2025-06-20 17:47:54 +01:00
end
else
print ( " ^1ERROR^7: ^1Possible table inside a table, check your items.lua^7? " )
print ( " ^1Possible Issue found^7: " )
print ( json.encode ( Items [ k ] , { indent = true } ) )
2025-06-06 19:36:12 +01:00
end
2025-06-06 00:50:13 +01:00
end
elseif checkExists ( Exports.QBExport ) then
while GetResourceState ( Exports.QBExport ) ~= " started " do Wait ( 100 ) end
itemResource = Exports.QBExport
2025-06-26 20:03:12 +01:00
cache.Items = exports [ Exports.QBExport ] : GetCoreObject ( ) . Shared.Items
2025-06-06 00:50:13 +01:00
elseif checkExists ( Exports.ESXExport ) then
itemResource = Exports.ESXExport
if GetResourceState ( Exports.QSInv ) : find ( " start " ) then
Items = exports [ Exports.QSInv ] : GetItemList ( )
else
2025-06-26 20:03:12 +01:00
cache.Items = ESX.GetItems ( )
while not next ( cache.Items ) do
cache.Items = ESX.GetItems ( )
2025-06-06 00:50:13 +01:00
Wait ( 1000 )
end
end
elseif checkExists ( Exports.RSGExport ) then
while GetResourceState ( Exports.RSGExport ) ~= " started " do Wait ( 100 ) end
itemResource = Exports.RSGExport
2025-06-26 20:03:12 +01:00
cache.Items = exports [ Exports.RSGExport ] : GetCoreObject ( ) . Shared.Items
2025-06-06 00:50:13 +01:00
end
---------------------
--- Load Vehicles ---
---------------------
2025-06-11 16:30:29 +01:00
-- Vehicle loading depending on framework
2025-06-26 20:03:12 +01:00
if checkExists ( Exports.QBXExport ) then
vehResource = Exports.QBXExport
cache.Vehicles = exports [ Exports.QBExport ] : GetCoreObject ( ) . Shared.Vehicles
elseif checkExists ( Exports.QBExport ) then
2025-06-06 00:50:13 +01:00
vehResource = Exports.QBExport
2025-06-26 20:03:12 +01:00
cache.Vehicles = exports [ Exports.QBExport ] : GetCoreObject ( ) . Shared.Vehicles
2025-06-06 00:50:13 +01:00
elseif checkExists ( Exports.OXCoreExport ) then
vehResource = Exports.OXCoreExport
2025-06-26 20:03:12 +01:00
cache.Vehicles = { }
2025-06-06 00:50:13 +01:00
for k , v in pairs ( Ox.GetVehicleData ( ) ) do
2025-06-26 20:03:12 +01:00
cache.Vehicles [ k ] = {
2025-06-06 00:50:13 +01:00
model = k , hash = GetHashKey ( k ) ,
price = v.price ,
name = v.name ,
brand = v.make
}
end
elseif checkExists ( Exports.ESXExport ) then
vehResource = Exports.ESXExport
while not MySQL do Wait ( 1000 ) end
for _ , v in pairs ( MySQL.query . await ( ' SELECT model, price, name FROM vehicles ' ) ) do
2025-06-26 20:03:12 +01:00
cache.Vehicles [ v.model ] = {
2025-06-06 00:50:13 +01:00
model = v.model ,
hash = GetHashKey ( v.model ) ,
price = v.price ,
name = v.name ,
}
end
elseif checkExists ( Exports.RSGExport ) then
vehResource = Exports.RSGExport
2025-06-26 20:03:12 +01:00
cache.Vehicles = exports [ Exports.RSGExport ] : GetCoreObject ( ) . Shared.Vehicles
2025-06-10 18:29:10 +01:00
2025-06-06 00:50:13 +01:00
end
---------------------
----- Load Jobs -----
---------------------
2025-06-11 16:30:29 +01:00
-- Jobs loading based on framework
2025-06-06 00:50:13 +01:00
if checkExists ( Exports.QBXExport ) then
jobResource = Exports.QBXExport
2025-06-26 20:03:12 +01:00
cache.Jobs , cache.Gangs = exports [ Exports.QBXExport ] : GetJobs ( ) , exports [ Exports.QBXExport ] : GetGangs ( )
elseif checkExists ( Exports.QBExport ) then
jobResource = Exports.QBExport
cache.Jobs , cache.Gangs = exports [ Exports.QBExport ] : GetCoreObject ( ) . Shared.Jobs , exports [ Exports.QBExport ] : GetCoreObject ( ) . Shared.Gangs
2025-06-06 00:50:13 +01:00
elseif checkExists ( Exports.OXCoreExport ) then
jobResource = Exports.OXCoreExport
while not MySQL do Wait ( 1000 ) end
local tempJobs = MySQL.query . await ( ' SELECT * FROM `ox_groups` ' )
local tempGrades = MySQL.query . await ( ' SELECT * FROM `ox_group_grades` ' )
local gradeMap = { }
for _ , grade in pairs ( tempGrades ) do
gradeMap [ grade.group ] = gradeMap [ grade.group ] or { }
gradeMap [ grade.group ] [ grade.grade ] = { name = grade.label }
end
for _ , job in pairs ( tempJobs ) do
2025-06-26 20:03:12 +01:00
cache.Jobs [ job.name ] = {
2025-06-06 00:50:13 +01:00
label = job.label ,
grades = gradeMap [ job.name ] or { }
}
end
2025-06-26 20:03:12 +01:00
cache.Gangs = cache.Jobs
2025-06-06 00:50:13 +01:00
elseif checkExists ( Exports.ESXExport ) then
jobResource = Exports.ESXExport
ESX = exports [ Exports.ESXExport ] : getSharedObject ( )
2025-06-26 20:03:12 +01:00
cache.Jobs = ESX.GetJobs ( )
while not next ( cache.Jobs ) do
2025-06-06 00:50:13 +01:00
Wait ( 100 )
2025-06-26 20:03:12 +01:00
cache.Jobs = ESX.GetJobs ( )
2025-06-06 00:50:13 +01:00
end
2025-06-26 20:03:12 +01:00
for Role , Grades in pairs ( cache.Jobs ) do
2025-06-06 00:50:13 +01:00
-- Check for if user has added grades
if Grades.grades == nil or not next ( Grades.grades ) then
goto continue
end
for grade , info in pairs ( Grades.grades ) do
if info.label and info.label : find ( " [Bb]oss " ) then
2025-06-26 20:03:12 +01:00
cache.Jobs [ Role ] . grades [ grade ] . isBoss = true
2025-06-06 00:50:13 +01:00
goto continue
end
end
local highestGrade = nil
for k in pairs ( Grades.grades ) do
local num = tonumber ( k )
if num and ( not highestGrade or num > highestGrade ) then
highestGrade = num
end
end
if highestGrade then
2025-06-26 20:03:12 +01:00
cache.Jobs [ Role ] . grades [ tostring ( highestGrade ) ] . isBoss = true
2025-06-06 00:50:13 +01:00
end
:: continue ::
end
2025-06-26 20:03:12 +01:00
cache.Gangs = cache.Jobs
2025-06-06 00:50:13 +01:00
elseif checkExists ( Exports.RSGExport ) then
jobResource = Exports.RSGExport
2025-06-26 20:03:12 +01:00
cache.Jobs , cache.Gangs = exports [ Exports.RSGExport ] : GetCoreObject ( ) . Shared.Jobs , exports [ Exports.RSGExport ] : GetCoreObject ( ) . Shared.Gangs
end
-- Fallback if nil or empty
if cache.Items == nil or not next ( cache.Items ) then
print ( " ^1--------------------------------------------^7 " )
print ( " ^1ERROR^7: ^1Can NOT find " .. itemResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^7Items ^1list^7, ^1possible error in that file or is it empty^7? " )
print ( " ^1--------------------------------------------^7 " )
cache.Items = { } -- Fallback to an empty table
end
if cache.Vehicles == nil or not next ( cache.Vehicles ) then
print ( " ^1--------------------------------------------^7 " )
print ( " ^1ERROR^7: ^1Can NOT find " .. vehResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^7Vehicles ^1list^7, ^1possible error in that file or is it empty^7? " )
print ( " ^1--------------------------------------------^7 " )
cache.Vehicles = { } -- Fallback to an empty table
end
if cache.Jobs == nil or not next ( cache.Jobs ) then
print ( " ^1--------------------------------------------^7 " )
print ( " ^1ERROR^7: ^1Can NOT find " .. jobResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^7job ^1list^7, ^1possible error in that file or is it empty^7? " )
print ( " ^1--------------------------------------------^7 " )
cache.Jobs = { } -- Fallback to an empty table
end
-- Auto Detection of Inventory Weight -- **EXPERIMENTAL**
-- Forcefully load the the specified config file from inventory scripts
-- This allows to get information required for certain functions that need to detect how much space is left in a players inventory
-- This is born from too many tickets of me needing to explain that they need to change "InventoryWeight" to match their inv setting
local function getInventoryConfig ( resource , data )
if data.convars then
return function ( path )
local key = path [ 1 ]
local convar = key == " MaxWeight " and data.convars . weight or key == " MaxSlots " and data.convars . slots
return convar and GetConvarInt ( convar.key , convar.default ) or nil , " Unsupported convar path: " .. table.concat ( path , " . " )
end
2025-06-10 18:29:10 +01:00
end
2025-06-26 20:03:12 +01:00
local content = LoadResourceFile ( resource , data.file )
if not content then return nil , " Failed to load file " end
local env = {
GetConvar = GetConvar , vector3 = vector3 , Citizen = Citizen ,
GetResourceState = GetResourceState , exports = exports ,
}
local fn , err = load ( content , ' @ ' .. data.file , ' t ' , env )
if not fn then return nil , " Failed to compile config: " .. err end
if not pcall ( fn ) then return nil , " Error executing config file " end
local cfg = env.Config or env.config
if not cfg then return nil , " Config table not found " end
return function ( path )
local ref = cfg
for _ , k in ipairs ( path ) do
if type ( ref ) ~= " table " then return nil , " Path invalid at: " .. tostring ( k ) end
ref = ref [ k ]
end
return ref
end
end
-- Inventory table
local invWeightTable = {
[ Exports.OXInv ] = { convars = {
weight = { key = " inventory:weight " , default = 30000 } ,
slots = { key = " inventory:slots " , default = 40 }
} } ,
[ Exports.QBInv ] = { file = " config/config.lua " , path = { " MaxWeight " } , slotPath = { " MaxSlots " } } ,
[ Exports.JPRInv ] = { file = " configs/main_config.lua " , path = { " MaxInventoryWeight " } , slotPath = { " MaxInventorySlots " } } ,
[ Exports.PSInv ] = { file = " config.lua " , path = { " MaxInventoryWeight " } , slotPath = { " MaxInventorySlots " } } ,
[ Exports.QSInv ] = { file = " config/config.lua " , path = { " InventoryWeight " , " weight " } , slotPath = { " InventoryWeight " , " slots " } } ,
[ Exports.TgiannInv ] = { file = " configs/config.lua " , path = { " slotsMaxWeights " , " player " , " maxWeight " } , slotPath = { " slotsMaxWeights " , " player " , " slots " } } ,
[ Exports.CodeMInv ] = { file = " config/config.lua " , path = { " MaxWeight " } , slotPath = { " MaxSlots " } } ,
[ Exports.RSGInv ] = { file = " config/config.lua " , path = { " MaxWeight " } , slotPath = { " MaxSlots " } } ,
--[Exports.OrigenInv] = { file = "config.lua", path = { "MaxWeight" } },
}
-- Run config detection
local invResource = " "
for script , data in pairs ( invWeightTable ) do
if checkExists ( script ) then
if script == Exports.QBInv and GetResourceState ( Exports.JPRInv ) : find ( " start " ) then return end
local lookup , err = getInventoryConfig ( script , data )
if not lookup then
print ( ( " ^1ERROR^7: ^1Config loader failed from ^5%s^7: ^1%s^7 " ) : format ( script , err or " unknown " ) )
return
end
local function resolve ( label , path )
local val , perr = lookup ( path )
if val then
cache [ " Inventory " .. label ] = val
return
end
local warnType = label == " Weight " and " ^1ERROR " or " ^3WARNING "
print ( ( " %s^7: ^1Failed to get ^7Inventory%s ^1from ^5%s^7: ^1%s^7 " ) : format ( warnType , label , script , perr or " unknown " ) )
end
resolve ( " Weight " , data.path or { " MaxWeight " } )
resolve ( " Slots " , data.slotPath or { " MaxSlots " } )
invResource = script : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " )
break
end
2025-06-06 00:50:13 +01:00
end
CreateThread ( function ( )
local counts = {
2025-06-26 20:03:12 +01:00
Items = 0 , Vehicles = 0 , Jobs = 0 , Gangs = 0 ,
2025-06-06 00:50:13 +01:00
}
for k , v in pairs ( cache ) do
2025-06-26 20:03:12 +01:00
if type ( v ) ~= " number " then for count in pairs ( v ) do counts [ k ] += 1 end end
end
if cache.InventoryWeight then
print ( " ^6FrameWorkCache^7: ^4 " .. invResource .. " ^2 InventoryWeight^7: ^3 " .. cache.InventoryWeight .. " ^7 (^3 " .. ( cache.InventoryWeight / 1000 ) .. " kg^7) " )
end
if cache.InventorySlots then
print ( " ^6FrameWorkCache^7: ^4 " .. invResource .. " ^2 InventorySlots^7: ^3 " .. cache.InventorySlots .. " ^7 " )
2025-06-06 00:50:13 +01:00
end
2025-06-26 20:03:12 +01:00
print ( " ^6FrameworkCache^7: ^4 " .. itemResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^2 Loaded ^3 " .. tostring ( counts.Items ) .. " ^2 Items^7 " )
print ( " ^6FrameworkCache^7: ^4 " .. vehResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^2 Loaded ^3 " .. tostring ( counts.Vehicles ) .. " ^2 Vehicles^7 " )
print ( " ^6FrameworkCache^7: ^4 " .. jobResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^2 Loaded ^3 " .. tostring ( counts.Jobs ) .. " ^2 Jobs^7 " )
print ( " ^6FrameworkCache^7: ^4 " .. jobResource : gsub ( " - " , " ^7-^4 " ) : gsub ( " _ " , " ^7_^4 " ) .. " ^2 Loaded ^3 " .. tostring ( counts.Gangs ) .. " ^2 Gangs^7 " )
2025-06-06 00:50:13 +01:00
end )
RegisterNetEvent ( " jim_bridge:requestCache " , function ( )
local src = source
2025-06-26 20:03:12 +01:00
TriggerClientEvent ( " jim_bridge:receiveCache " , src , cache )
2025-06-06 00:50:13 +01:00
end )
exports ( " GetSharedData " , function ( )
-- Wait for data to be ready before returning it
local timeout = GetGameTimer ( ) + 5000
while (
2025-06-26 20:03:12 +01:00
not cache or
( not cache.Items or next ( cache.Items ) == nil ) or
( not cache.Vehicles or next ( cache.Vehicles ) == nil ) or
( not cache.Jobs or next ( cache.Jobs ) == nil )
2025-06-06 00:50:13 +01:00
) and GetGameTimer ( ) < timeout do
Wait ( 50 )
end
2025-06-26 20:03:12 +01:00
return cache
2025-06-06 00:50:13 +01:00
end )