Compare commits

..

9 Commits

Author SHA1 Message Date
Jim Shield
b07c089946 Version Bump
`2.0.18` - `2.0.19`
2025-06-28 20:08:51 +01:00
Jim Shield
d603067c77 Remove files i was testing with (forgot) 2025-06-28 20:04:50 +01:00
Jim Shield
5a97b8f83a Fix script break stop if config file not found 2025-06-28 11:22:12 +01:00
Jim Shield
d0a09871c7 Fix for if using QBOX without ox_inventory
Realised that if using older (and custom) versions of qbox, the item list can't be retrieved through `GetCoreObject()`

This fixes that and also (if needed) grabs the item lists from inventories directly if needed
2025-06-27 13:35:28 +01:00
Jim Shield
e87aa8d74d Fix server lag when loading scripts
This was my bad, set up functions that requested the cache from server, didn't realise it was duplicating it and spamming it

This moves the requests to a separate file and uses an export instead

Also hopefully fixes ox_lib images being an arse
2025-06-26 23:15:33 +01:00
Jim Shield
e204e6153d remove debug prints
oops
2025-06-26 20:08:31 +01:00
Jim Shield
5c69f8dd28 add new function hasFreeInventorySlots()
using the function to grab slot info, this can now check if you have enough inventory slots for items to be given, allowing it to check before TRYING to add them
2025-06-26 20:04:32 +01:00
Jim Shield
f3fa309dbf Revamp framework caching
People were reporting issues with scripts claiming items didn't exist,  I believe this was due to their servers having slot starts (eg. alot of clothes or vehicles being loaded)

I've rewritten it to fully delay loading until everything is correctly cached and let the scripts continue. Also hopefully reducing memory footprint for it too.

---
Also slipped in an `experimental` function that grabs inventory config files to check their set InventoryWeight and InventorySlots to automatically apply it for other functions
---
2025-06-26 20:03:12 +01:00
Jim Shield
9d74e7cb46 Update setMetadata server event
Before it was JUST for durability but fixed it to work for all item metadata
2025-06-26 19:57:03 +01:00
8 changed files with 314 additions and 204 deletions

32
clientFrameworkCache.lua Normal file
View File

@@ -0,0 +1,32 @@
if _G.__bridge_cache_loaded then return end
_G.__bridge_cache_loaded = true
_G.BridgeCache = {
hasCache = false,
data = nil,
queue = {},
}
RegisterNetEvent("jim_bridge:receiveCache", function(data)
if _G.BridgeCache.hasCache then return end
_G.BridgeCache.data = data
_G.BridgeCache.hasCache = true
for _, cb in ipairs(_G.BridgeCache.queue) do
cb(data)
end
_G.BridgeCache.queue = nil
end)
-- Only send the request ONCE per client session
TriggerServerEvent("jim_bridge:requestCache")
-- Exported async function
function GetBridgeCache(callback)
if _G.BridgeCache.hasCache then
callback(_G.BridgeCache.data)
else
table.insert(_G.BridgeCache.queue, callback)
end
end
exports("GetBridgeCache", GetBridgeCache)

View File

@@ -35,9 +35,12 @@ local Exports = {
} }
-- Prevent reloading if cache is already initialized -- Prevent reloading if cache is already initialized
if _G.__jimBridgeDataCache then return end local cache = {
_G.__jimBridgeDataCache = {} Items = {},
local cache = _G.__jimBridgeDataCache Vehicles = {},
Jobs = {},
Gangs = {},
}
-- Helper function to check if resource exists in server (instead of if it is already started) -- Helper function to check if resource exists in server (instead of if it is already started)
local function checkExists(resourceName) local function checkExists(resourceName)
@@ -62,7 +65,6 @@ if checkExists(Exports.ESXExport) then
end end
-- Initialize variables for caching -- Initialize variables for caching
local Items, Vehicles, Jobs, Gangs, Core = {}, {}, {}, {}, nil
local itemResource, jobResource, vehResource = "N/A", "N/A", "N/A" local itemResource, jobResource, vehResource = "N/A", "N/A", "N/A"
-- Print just to announce it knows the exports/scripts exist in the server -- Print just to announce it knows the exports/scripts exist in the server
@@ -85,22 +87,15 @@ if checkExists(Exports.OXInv) then
return exports[Exports.OXInv]:Items() return exports[Exports.OXInv]:Items()
end) end)
if success and result then if success and result then
Items = result cache.Items = result
end
if Items == nil or not next(Items) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find "..Exports.OXInv.." ^7Items ^1list^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Items = {} -- Fallback to an empty table
end end
-- Get Weapon info and duplicate them if they are uppercase -- Get Weapon info and duplicate them if they are uppercase
-- (duplicate incase anything checks for the uppercase version) -- (duplicate incase anything checks for the uppercase version)
for k, v in pairs(Items) do for k, v in pairs(cache.Items) do
if type(k) == "string" then if type(k) == "string" then
if k:find("WEAPON") then if k:find("WEAPON") then
Items[k:lower()] = Items[k] cache.Items[k:lower()] = cache.Items[k]
end end
else else
print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?") print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?")
@@ -109,27 +104,48 @@ if checkExists(Exports.OXInv) then
end end
end end
elseif checkExists(Exports.QBXExport) then
while GetResourceState(Exports.QBXExport) ~= "started" do Wait(100) end
itemResource = Exports.QBXExport
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
-- If this is nil, they need to update to qbx_core 1.23.0+
if not cache.Items then
-- if their inventory doesn't allow that (they refuse to update their butchered core replacement):
if GetResourceState(Exports.QSInv):find("start") then
itemResource = Exports.QSInv
cache.Items = exports[Exports.QSInv]:GetItemList()
elseif GetResourceState(Exports.OrigenInv):find("start") then
itemResource = Exports.OrigenInv
cache.Items = exports[Exports.OrigenInv]:Items()
elseif GetResourceState(Exports.CodeMInv):find("start") then
itemResource = Exports.CodeMInv
cache.Items = exports[Exports.CodeMInv]:GetItemList()
elseif GetResourceState(Exports.CoreInv):find("start") then
itemResource = Exports.CoreInv
cache.Items = exports[Exports.CoreInv]:getItemsList()
elseif GetResourceState(Exports.TgiannInv):find("start") then
itemResource = Exports.TgiannInv
cache.Items = exports[Exports.TgiannInv]:Items()
end
end
elseif checkExists(Exports.QBExport) then elseif checkExists(Exports.QBExport) then
while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end
itemResource = Exports.QBExport itemResource = Exports.QBExport
Core = exports[Exports.QBExport]:GetCoreObject() cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
Items = Core.Shared.Items
if Items == nil or not next(Items) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find "..Exports.QBExport.." ^7Items ^1list^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Items = {} -- Fallback to an empty table
end
elseif checkExists(Exports.ESXExport) then elseif checkExists(Exports.ESXExport) then
itemResource = Exports.ESXExport itemResource = Exports.ESXExport
if GetResourceState(Exports.QSInv):find("start") then if GetResourceState(Exports.QSInv):find("start") then
Items = exports[Exports.QSInv]:GetItemList() cache.Items = exports[Exports.QSInv]:GetItemList()
else else
Items = ESX.GetItems() cache.Items = ESX.GetItems()
while not next(Items) do while not next(cache.Items) do
Items = ESX.GetItems() cache.Items = ESX.GetItems()
Wait(1000) Wait(1000)
end end
end end
@@ -137,37 +153,26 @@ elseif checkExists(Exports.ESXExport) then
elseif checkExists(Exports.RSGExport) then elseif checkExists(Exports.RSGExport) then
while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end
itemResource = Exports.RSGExport itemResource = Exports.RSGExport
Core = exports[Exports.RSGExport]:GetCoreObject() cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
Items = Core.Shared.Items
if Items == nil or not next(Items) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find "..Exports.RSGExport.." ^7Items ^1list^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Items = {} -- Fallback to an empty table
end
end end
--------------------- ---------------------
--- Load Vehicles --- --- Load Vehicles ---
--------------------- ---------------------
-- Vehicle loading depending on framework -- Vehicle loading depending on framework
if checkExists(Exports.QBXExport) or checkExists(Exports.QBExport) then if checkExists(Exports.QBXExport) then
vehResource = Exports.QBXExport
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
elseif checkExists(Exports.QBExport)then
vehResource = Exports.QBExport vehResource = Exports.QBExport
Core = Core or exports[Exports.QBExport]:GetCoreObject() cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
Vehicles = Core.Shared.Vehicles
if Vehicles == nil or not next(Vehicles) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find "..Exports.QBExport.." ^7Vehicles ^1list^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Vehicles = {} -- Fallback to an empty table
end
elseif checkExists(Exports.OXCoreExport) then elseif checkExists(Exports.OXCoreExport) then
vehResource = Exports.OXCoreExport vehResource = Exports.OXCoreExport
Vehicles = {} cache.Vehicles = {}
for k, v in pairs(Ox.GetVehicleData()) do for k, v in pairs(Ox.GetVehicleData()) do
Vehicles[k] = { cache.Vehicles[k] = {
model = k, hash = GetHashKey(k), model = k, hash = GetHashKey(k),
price = v.price, price = v.price,
name = v.name, name = v.name,
@@ -178,9 +183,8 @@ elseif checkExists(Exports.OXCoreExport) then
elseif checkExists(Exports.ESXExport) then elseif checkExists(Exports.ESXExport) then
vehResource = Exports.ESXExport vehResource = Exports.ESXExport
while not MySQL do Wait(1000) end while not MySQL do Wait(1000) end
Vehicles = {}
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
Vehicles[v.model] = { cache.Vehicles[v.model] = {
model = v.model, model = v.model,
hash = GetHashKey(v.model), hash = GetHashKey(v.model),
price = v.price, price = v.price,
@@ -190,14 +194,7 @@ elseif checkExists(Exports.ESXExport) then
elseif checkExists(Exports.RSGExport) then elseif checkExists(Exports.RSGExport) then
vehResource = Exports.RSGExport vehResource = Exports.RSGExport
Core = Core or exports[Exports.RSGExport]:GetCoreObject() cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
Vehicles = Core.Shared.Vehicles
if Vehicles == nil or not next(Vehicles) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find shared ^7Vehicles ^1table^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Vehicles = {} -- Fallback to an empty table
end
end end
@@ -207,18 +204,14 @@ end
-- Jobs loading based on framework -- Jobs loading based on framework
if checkExists(Exports.QBXExport) then if checkExists(Exports.QBXExport) then
jobResource = Exports.QBXExport jobResource = Exports.QBXExport
Core = Core or exports[Exports.QBXExport]:GetCoreObject() cache.Jobs, cache.Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
Jobs, Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
if Jobs == nil or not next(Jobs) then elseif checkExists(Exports.QBExport) then
print("^1--------------------------------------------^7") jobResource = Exports.QBExport
print("^1ERROR^7: ^1Can NOT find shared ^7Jobs ^1table^7, ^1possible error in that file^7?") cache.Jobs, cache.Gangs = exports[Exports.QBExport]:GetCoreObject().Shared.Jobs, exports[Exports.QBExport]:GetCoreObject().Shared.Gangs
print("^1--------------------------------------------^7")
Jobs = {} -- Fallback to an empty table
end
elseif checkExists(Exports.OXCoreExport) then elseif checkExists(Exports.OXCoreExport) then
jobResource = Exports.OXCoreExport jobResource = Exports.OXCoreExport
Jobs = {}
while not MySQL do Wait(1000) end while not MySQL do Wait(1000) end
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`') local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`') local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
@@ -228,34 +221,29 @@ elseif checkExists(Exports.OXCoreExport) then
gradeMap[grade.group][grade.grade] = { name = grade.label } gradeMap[grade.group][grade.grade] = { name = grade.label }
end end
for _, job in pairs(tempJobs) do for _, job in pairs(tempJobs) do
Jobs[job.name] = { cache.Jobs[job.name] = {
label = job.label, label = job.label,
grades = gradeMap[job.name] or {} grades = gradeMap[job.name] or {}
} }
end end
Gangs = Jobs cache.Gangs = cache.Jobs
elseif checkExists(Exports.QBExport) then
jobResource = Exports.QBExport
Core = Core or exports[Exports.QBExport]:GetCoreObject()
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
elseif checkExists(Exports.ESXExport) then elseif checkExists(Exports.ESXExport) then
jobResource = Exports.ESXExport jobResource = Exports.ESXExport
ESX = exports[Exports.ESXExport]:getSharedObject() ESX = exports[Exports.ESXExport]:getSharedObject()
Jobs = ESX.GetJobs() cache.Jobs = ESX.GetJobs()
while not next(Jobs) do while not next(cache.Jobs) do
Wait(100) Wait(100)
Jobs = ESX.GetJobs() cache.Jobs = ESX.GetJobs()
end end
for Role, Grades in pairs(Jobs) do for Role, Grades in pairs(cache.Jobs) do
-- Check for if user has added grades -- Check for if user has added grades
if Grades.grades == nil or not next(Grades.grades) then if Grades.grades == nil or not next(Grades.grades) then
goto continue goto continue
end end
for grade, info in pairs(Grades.grades) do for grade, info in pairs(Grades.grades) do
if info.label and info.label:find("[Bb]oss") then if info.label and info.label:find("[Bb]oss") then
Jobs[Role].grades[grade].isBoss = true cache.Jobs[Role].grades[grade].isBoss = true
goto continue goto continue
end end
end end
@@ -268,64 +256,159 @@ elseif checkExists(Exports.ESXExport) then
end end
if highestGrade then if highestGrade then
Jobs[Role].grades[tostring(highestGrade)].isBoss = true cache.Jobs[Role].grades[tostring(highestGrade)].isBoss = true
end end
::continue:: ::continue::
end end
Gangs = Jobs cache.Gangs = cache.Jobs
elseif checkExists(Exports.RSGExport) then elseif checkExists(Exports.RSGExport) then
jobResource = Exports.RSGExport jobResource = Exports.RSGExport
Core = Core or exports[Exports.RSGExport]:GetCoreObject() cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
if Jobs == nil or not next(Jobs) then
print("^1--------------------------------------------^7")
print("^1ERROR^7: ^1Can NOT find shared ^7Jobs ^1table^7, ^1possible error in that file^7?")
print("^1--------------------------------------------^7")
Jobs = {} -- Fallback to an empty table
end
end end
-- Save to global cache -- Fallback if nil or empty
cache.Items = Items if cache.Items == nil or not next(cache.Items) then
cache.Vehicles = Vehicles print("^1--------------------------------------------^7")
cache.Jobs = Jobs print("^1ERROR^7: ^1Can NOT find "..itemResource.." ^7Items ^1list^7, ^1possible error in that file or is it empty^7?")
cache.Gangs = Gangs 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.." ^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.." ^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
end
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,
DependencyCheck = DependencyCheck or function() return nil end,
}
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"))
break
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
end
CreateThread(function() CreateThread(function()
local counts = { local counts = {
Items = 0, Items = 0, Vehicles = 0, Jobs = 0, Gangs = 0,
Vehicles = 0,
Jobs = 0,
Gangs = 0,
} }
for k, v in pairs(cache) do for k, v in pairs(cache) do
for count in pairs(v) do if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end
counts[k] += 1
end
end end
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Items).."^2 Items from ^7"..itemResource) if cache.InventoryWeight then
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Vehicles).."^2 Vehicles from ^7"..vehResource) print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventoryWeight^7: ^3"..cache.InventoryWeight.."^7 (^3"..(cache.InventoryWeight / 1000).."kg^7)")
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Jobs).."^2 Jobs from ^7"..jobResource) end
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Gangs).."^2 Gangs from ^7"..jobResource) if cache.InventorySlots then
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventorySlots^7: ^3"..cache.InventorySlots.."^7")
end
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")
end) end)
RegisterNetEvent("jim_bridge:requestCache", function() RegisterNetEvent("jim_bridge:requestCache", function()
local src = source local src = source
TriggerClientEvent("jim_bridge:receiveCache", src, _G.__jimBridgeDataCache) TriggerClientEvent("jim_bridge:receiveCache", src, cache)
end) end)
exports("GetSharedData", function() exports("GetSharedData", function()
-- Wait for data to be ready before returning it -- Wait for data to be ready before returning it
local timeout = GetGameTimer() + 5000 local timeout = GetGameTimer() + 5000
while ( while (
not _G.__jimBridgeDataCache or not cache or
(not _G.__jimBridgeDataCache.Items or next(_G.__jimBridgeDataCache.Items) == nil) or (not cache.Items or next(cache.Items) == nil) or
(not _G.__jimBridgeDataCache.Vehicles or next(_G.__jimBridgeDataCache.Vehicles) == nil) or (not cache.Vehicles or next(cache.Vehicles) == nil) or
(not _G.__jimBridgeDataCache.Jobs or next(_G.__jimBridgeDataCache.Jobs) == nil) (not cache.Jobs or next(cache.Jobs) == nil)
) and GetGameTimer() < timeout do ) and GetGameTimer() < timeout do
Wait(50) Wait(50)
end end
return _G.__jimBridgeDataCache return cache
end) end)

View File

@@ -1,6 +1,6 @@
name "Jim_Bridge" name "Jim_Bridge"
author "Jimathy" author "Jimathy"
version "2.0.18" version "2.0.19"
description "Framework Bridge By Jimathy" description "Framework Bridge By Jimathy"
fx_version "cerulean" fx_version "cerulean"
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
@@ -18,9 +18,10 @@ files {
-- Version checker -- Version checker
server_scripts { server_scripts {
'frameworkCache.lua', 'frameworkCache.lua',
'_versioncheck.lua' '_versioncheck.lua',
} }
client_scripts { client_scripts {
'ui_modules/*.lua' 'clientFrameworkCache.lua',
'ui_modules/*.lua',
} }

View File

@@ -197,22 +197,23 @@ function waitForSharedLoad()
local timeout = 100000 -- 10 seconds in milliseconds local timeout = 100000 -- 10 seconds in milliseconds
local startTime = GetGameTimer() local startTime = GetGameTimer()
local loaded = true local loaded = true
--Wait(1000)
local count = {}
local loop = 0 local loop = 0
while ((not Jobs or not next(Jobs)) and (not Items or not next(Items)) and (not Vehicles or not next(Vehicles))) and (GetGameTimer() - startTime) < timeout do while ((not Jobs or not next(Jobs)) or
if not messageShown then (not Items or not next(Items)) or
(not Vehicles or not next(Vehicles))
) and ((GetGameTimer() - startTime) < timeout) do
if loop >= 3 and not messageShown then
if (not Jobs or not next(Jobs)) then if (not Jobs or not next(Jobs)) then
debugPrint("^4Debug^7: ^2Waiting for Jobs to be loaded") print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded")
end end
if (not Items or not next(Items)) then if (not Items or not next(Items)) then
debugPrint("^4Debug^7: ^2Waiting for Items to be loaded") print("^4Debug^7: ^2Waiting for ^7Items^2 to be loaded")
end end
if (not Vehicles or not next(Vehicles)) then if (not Vehicles or not next(Vehicles)) then
debugPrint("^4Debug^7: ^2Waiting for Vehicles to be loaded") print("^4Debug^7: ^2Waiting for ^7Vehicles^2 to be loaded")
end end
messageShown = true
end end
messageShown = true
--print((GetGameTimer() - startTime) < timeout) --print((GetGameTimer() - startTime) < timeout)
Wait(1000) Wait(1000)
if Jobs and Items and Vehicles then if Jobs and Items and Vehicles then

View File

@@ -1,4 +1,4 @@
Items, Vehicles, Jobs, Gangs, Core = {}, nil, nil, nil, nil Items, Vehicles, Jobs, Gangs = nil, nil, nil, nil
-- Shared Exports Initialization -- Shared Exports Initialization
Exports.PSInv = isStarted("lj-inventory") and "lj-inventory" or Exports.PSInv Exports.PSInv = isStarted("lj-inventory") and "lj-inventory" or Exports.PSInv
@@ -33,80 +33,72 @@ end
if IsDuplicityVersion() then if IsDuplicityVersion() then
CreateThread(function() local cache = nil
local cache = nil local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
-- Wait until jim_bridge is started and export is available -- Wait until jim_bridge is started and export is available
while not cache and GetGameTimer() < timeout do while not cache and GetGameTimer() < timeout do
if GetResourceState("jim_bridge"):find("start") then if GetResourceState("jim_bridge"):find("start") then
local success, result = pcall(function() local success, result = pcall(function()
return exports["jim_bridge"]:GetSharedData() return exports["jim_bridge"]:GetSharedData()
end) end)
if success and result then if success and result then
cache = result cache = result
--print(json.encode(cache, {indent = true}))
end
end end
Wait(100)
end end
Wait(100)
end
if not cache then if not cache then
print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.") print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.")
return return
end end
Items = cache.Items Items = cache.Items
Vehicles = cache.Vehicles Vehicles = cache.Vehicles
Jobs = cache.Jobs Jobs = cache.Jobs
Gangs = cache.Gangs Gangs = cache.Gangs
InventoryWeight = cache.InventoryWeight or InventoryWeight
InventorySlots = cache.InventorySlots or 40
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.") debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
end) --print(countTable(Items), countTable(Vehicles), countTable(Jobs))
else else
local hasCache = false -- Shared bridge cache - prevent duplicate listeners
-- 🔹 Client Side: Request from server exports["jim_bridge"]:GetBridgeCache(function(cache)
_G.__jimBridgeDataCache = {}
RegisterNetEvent("jim_bridge:receiveCache", function(data)
if not hasCache then
_G.__jimBridgeDataCache = data
hasCache = true
else
return
end
end)
TriggerServerEvent("jim_bridge:requestCache")
CreateThread(function()
while not _G.__jimBridgeDataCache or not next(_G.__jimBridgeDataCache) do Wait(50) end
local cache = _G.__jimBridgeDataCache
Items = cache.Items or {} Items = cache.Items or {}
Vehicles = cache.Vehicles or {} Vehicles = cache.Vehicles or {}
Jobs = cache.Jobs or {} Jobs = cache.Jobs or {}
Gangs = cache.Gangs or {} Gangs = cache.Gangs or {}
InventoryWeight = cache.InventoryWeight or InventoryWeight
InventorySlots = cache.InventorySlots or 50
if isStarted(ESXExport) then CreateThread(function()
for _, v in pairs(Vehicles) do if isStarted(ESXExport) then
Vehicles[v.model] = { for _, v in pairs(Vehicles) do
model = v.model, Vehicles[v.model] = {
hash = v.hash, model = v.model,
price = v.price, hash = v.hash,
name = v.name, price = v.price,
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper) name = v.name,
} brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
end }
end
if isStarted(OXInv) then
for k, v in pairsByKeys(Items) do
local tempInfo = exports[OXInv]:Items(k)
if tempInfo and tempInfo.client then
Items[k].image = (tempInfo.client and tempInfo.client.image) and tempInfo.client.image:gsub("nui://"..OXInv.."/web/images/", "") or k..".png"
Items[k].hunger = tempInfo.client and tempInfo.client.hunger
Items[k].thirst = tempInfo.client and tempInfo.client.thirst
end end
end end
end
if isStarted(OXInv) then
for k, v in pairs(Items) do
local tempInfo = exports[OXInv]:Items(k)
Items[k].image = k..".png" -- Set default image
if tempInfo and tempInfo.client then -- if client info, check for hard set image
Items[k].image = (tempInfo.client.image) and tempInfo.client.image:gsub("nui://"..OXInv.."/web/images/", "") or Items[k].image
Items[k].hunger = tempInfo.client.hunger
Items[k].thirst = tempInfo.client.thirst
end
end
end
end)
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
end) end)
end end

View File

@@ -516,17 +516,19 @@ end
--- ```lua --- ```lua
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } }) --- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
--- ``` --- ```
RegisterNetEvent(getScript()..":server:setMetaData", function(data) RegisterNetEvent(getScript()..":server:setMetaData", function(data, src)
local src = source local src = src or source
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
debugPrint(src, data.item, 1, data.slot) --debugPrint(src, data.item, 1, data.slot)
local Player = Core.Functions.GetPlayer(src) local Player = Core.Functions.GetPlayer(src)
Player.PlayerData.items[data.slot].info = data.metadata Player.PlayerData.items[data.slot].info = data.metadata
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability if data.metadata.durability then
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
end
Player.Functions.SetInventory(Player.PlayerData.items) Player.Functions.SetInventory(Player.PlayerData.items)
elseif isStarted(OXInv) then elseif isStarted(OXInv) then
exports[OXInv]:SetDurability(src, data.slot, data.metadata.durability) exports[OXInv]:SetMetadata(src, data.slot, data.metadata)
elseif isStarted(QSInv) then elseif isStarted(QSInv) then
exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata) exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
@@ -868,6 +870,14 @@ function getCurrentInvWeight()
return weight return weight
end end
function hasFreeInventorySlots(slots, src)
local inv = getPlayerInv(src)
if (countTable(inv) + slots) <= InventorySlots then
return true
end
return false
end
------------------------------------------------------------- -------------------------------------------------------------
-- Check Item Existance - return boolean(true,false) -- Check Item Existance - return boolean(true,false)
------------------------------------------------------------- -------------------------------------------------------------

View File

@@ -225,7 +225,7 @@ if isStarted(PSInv) then
return true return true
end end
if versionCompare(GetResourceMetadata("ps-inventory", "version", 0), "1.0.6") then if versionCompare(GetResourceMetadata(PSInv, "version", 0), "1.0.6") then
print("^6Bridge^7: ^4"..PSInv.."^2 Version above ^31.0.6^7, ^2forcing ^1QBInvNew ^2to ^1true^7") print("^6Bridge^7: ^4"..PSInv.."^2 Version above ^31.0.6^7, ^2forcing ^1QBInvNew ^2to ^1true^7")
QBInvNew = true QBInvNew = true
else else
@@ -233,4 +233,4 @@ if isStarted(PSInv) then
QBInvNew = false QBInvNew = false
end end
end end
end end

View File

@@ -1,17 +1,8 @@
2.0.18 2.0.19
- Fix ox_inv client item caching (images) - SetMetadata function now handles full metadata, not just durability
- Add better checks if errors in ox_inv items and add more prominent error messages all around - Completely rework framework caching, it was lagging behind and breaking
- Tidy up several debugPrints - Add new hasFreeInvnentorySlots() function
- Move AuthEvents to new file - Add inv config file grabber, checks inventory configs for inventory weight/slots
- Fix clearStash error with PSInv and JPRInv
- Add extra trigger event for PSInv when opening stash (hopefully fix missing items for some)
- Fix onPlayerUnloaded() not triggering
- Add support for esx_society in openBossMenu()
- Added support for okokbilling to billPlayer()
- Rework progressBar and ui_modules folder
- Rework isAnimal.lua to be more automated and smarter
- Rework/Fix auto detection of QBInvNew
- Add new functions to get players current Inventory Weight
https://github.com/jimathy/jim_bridge https://github.com/jimathy/jim_bridge