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 )