mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 22:16:03 +01:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5af0334d5 | ||
|
|
c1ab2b3505 | ||
|
|
1665ab41de | ||
|
|
6d397b59bf | ||
|
|
9dfbcbe878 | ||
|
|
ca0970b650 | ||
|
|
02218a4902 | ||
|
|
a900044306 | ||
|
|
7382a534a0 | ||
|
|
3b76b15bb4 | ||
|
|
f2eda0fef6 | ||
|
|
61c1cce814 | ||
|
|
0f1660e3ed | ||
|
|
e5b55e49b7 | ||
|
|
1ff01526bc | ||
|
|
1ed227080b | ||
|
|
b07c089946 | ||
|
|
d603067c77 | ||
|
|
5a97b8f83a | ||
|
|
d0a09871c7 | ||
|
|
e87aa8d74d | ||
|
|
e204e6153d | ||
|
|
5c69f8dd28 | ||
|
|
f3fa309dbf | ||
|
|
9d74e7cb46 |
10
README.md
10
README.md
@@ -53,11 +53,11 @@ setr jim_DisableDebug true
|
|||||||
setr jim_DisableEventDebug true
|
setr jim_DisableEventDebug true
|
||||||
|
|
||||||
# Force the default setting for what framework scripts should be used
|
# Force the default setting for what framework scripts should be used
|
||||||
setr jim_menuScript qb # qb, ox, gta, jim
|
setr jim_menuScript qb # qb, ox, gta, lation
|
||||||
setr jim_notifyScript gta # qb, ox, gta, esx, okok, red
|
setr jim_notifyScript gta # qb, ox, gta, esx, okok, red, lation
|
||||||
setr jim_drawTextScript qb # qb, ox, gta, esx
|
setr jim_drawTextScript qb # qb, ox, gta, esx, lation
|
||||||
setr jim_progressBarScript qb # qb, ox, gta, esx
|
setr jim_progressBarScript qb # qb, ox, gta, esx, lation
|
||||||
setr jim_skillCheckScript qb # qb, ox, gta
|
setr jim_skillCheckScript qb # qb, ox, gta, lation
|
||||||
setr jim_dontUseTarget false # Set to true to disable target systems and use draw text 3d
|
setr jim_dontUseTarget false # Set to true to disable target systems and use draw text 3d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
32
clientFrameworkCache.lua
Normal file
32
clientFrameworkCache.lua
Normal 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)
|
||||||
@@ -35,10 +35,23 @@ 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 = {},
|
||||||
|
}
|
||||||
|
local cacheReady = false
|
||||||
|
local timers = {}
|
||||||
|
local function startTimer(label)
|
||||||
|
timers[label] = GetGameTimer()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function endTimer(label)
|
||||||
|
timers[label] = GetGameTimer() - (timers[label] or GetGameTimer())
|
||||||
|
timers[label] = timers[label] / 1000
|
||||||
|
end
|
||||||
|
startTimer("Cache")
|
||||||
-- 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)
|
||||||
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
|
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
|
||||||
@@ -62,7 +75,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
|
||||||
@@ -76,6 +88,7 @@ end
|
|||||||
---- Load Items -----
|
---- Load Items -----
|
||||||
---------------------
|
---------------------
|
||||||
-- Items initialization based on detected inventory system
|
-- Items initialization based on detected inventory system
|
||||||
|
startTimer("Items")
|
||||||
if checkExists(Exports.OXInv) then
|
if checkExists(Exports.OXInv) then
|
||||||
-- Wait for OX Inventory to start if it's not already started
|
-- Wait for OX Inventory to start if it's not already started
|
||||||
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
||||||
@@ -85,22 +98,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 +115,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 +164,28 @@ 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
|
||||||
|
endTimer("Items")
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
--- Load Vehicles ---
|
--- Load Vehicles ---
|
||||||
---------------------
|
---------------------
|
||||||
|
startTimer("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 +196,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,35 +207,26 @@ 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
|
||||||
|
endTimer("Vehicles")
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
----- Load Jobs -----
|
----- Load Jobs -----
|
||||||
---------------------
|
---------------------
|
||||||
|
startTimer("Jobs")
|
||||||
-- 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 +236,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 +271,184 @@ 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
|
||||||
|
endTimer("Jobs")
|
||||||
|
|
||||||
-- 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
|
||||||
|
startTimer("InvWeight")
|
||||||
|
startTimer("InvSlots")
|
||||||
|
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] = {
|
||||||
|
fallback = {
|
||||||
|
{ file = "config.lua", path = { "MaxInventoryWeight" }, slotPath = { "MaxInventorySlots" } }, -- old version
|
||||||
|
{ file = "config/config.lua", path = { "MaxWeight" }, slotPath = { "MaxSlots" } }, -- new version
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[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 goto skip end
|
||||||
|
|
||||||
|
local attempts = data.fallback or { data }
|
||||||
|
local lookup, used, err
|
||||||
|
|
||||||
|
for _, option in ipairs(attempts) do
|
||||||
|
local try, e = getInventoryConfig(script, option)
|
||||||
|
if try then
|
||||||
|
lookup = try
|
||||||
|
used = option
|
||||||
|
break
|
||||||
|
end
|
||||||
|
err = e
|
||||||
|
end
|
||||||
|
|
||||||
|
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", used.path or { "MaxWeight" })
|
||||||
|
resolve("Slots", used.slotPath or { "MaxSlots" })
|
||||||
|
|
||||||
|
invResource = script:gsub("-", "^7-^4"):gsub("_", "^7_^4")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
::skip::
|
||||||
|
end
|
||||||
|
endTimer("InvWeight")
|
||||||
|
endTimer("InvSlots")
|
||||||
|
|
||||||
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) ^7("..timers["InvWeight"].."s)")
|
||||||
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("..timers["InvSlots"].."s)")
|
||||||
|
end
|
||||||
|
print("^6FrameworkCache^7: ^4"..itemResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Items).."^2 Items ^7("..timers["Items"].."s)")
|
||||||
|
print("^6FrameworkCache^7: ^4"..vehResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Vehicles).."^2 Vehicles ^7("..timers["Vehicles"].."s)")
|
||||||
|
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Jobs).."^2 Jobs ^7("..timers["Jobs"].."s)")
|
||||||
|
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Gangs).."^2 Gangs ^7("..timers["Jobs"].."s)")
|
||||||
|
endTimer("Cache")
|
||||||
|
print("^6FrameworkCache^7: ^2Cache Ready ^7("..timers["Cache"].."s)")
|
||||||
|
cacheReady = true
|
||||||
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 indefinitely (with periodic checks) until cacheReady is true
|
||||||
local timeout = GetGameTimer() + 5000
|
local timeout = GetGameTimer() + 20000 -- 20 second failsafe timeout
|
||||||
while (
|
while not cacheReady and GetGameTimer() < timeout do
|
||||||
not _G.__jimBridgeDataCache or
|
Wait(100)
|
||||||
(not _G.__jimBridgeDataCache.Items or next(_G.__jimBridgeDataCache.Items) == nil) or
|
|
||||||
(not _G.__jimBridgeDataCache.Vehicles or next(_G.__jimBridgeDataCache.Vehicles) == nil) or
|
|
||||||
(not _G.__jimBridgeDataCache.Jobs or next(_G.__jimBridgeDataCache.Jobs) == nil)
|
|
||||||
) and GetGameTimer() < timeout do
|
|
||||||
Wait(50)
|
|
||||||
end
|
end
|
||||||
return _G.__jimBridgeDataCache
|
|
||||||
|
if not cacheReady then
|
||||||
|
print("^1ERROR^7: jim_bridge cache timed out waiting for data.")
|
||||||
|
return nil -- Signal clearly if cache never became ready
|
||||||
|
end
|
||||||
|
|
||||||
|
return cache
|
||||||
end)
|
end)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
name "Jim_Bridge"
|
name "Jim_Bridge"
|
||||||
author "Jimathy"
|
author "Jimathy"
|
||||||
version "2.0.18"
|
version "2.0.20"
|
||||||
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',
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -47,48 +47,7 @@
|
|||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
function openMenu(Menu, data)
|
function openMenu(Menu, data)
|
||||||
if Config.System.Menu == "jim" then
|
if Config.System.Menu == "ox" then
|
||||||
if data.onBack then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-arrow-left",
|
|
||||||
header = " ",
|
|
||||||
txt = "Return",
|
|
||||||
params = {
|
|
||||||
isAction = true,
|
|
||||||
event = data.onBack,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
elseif data.canClose then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-xmark",
|
|
||||||
header = " ",
|
|
||||||
txt = "Close",
|
|
||||||
params = {
|
|
||||||
isAction = true,
|
|
||||||
event = data.onExit and data.onExit or (function() exports[QBMenuExport]:closeMenu() end),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
if data.header ~= nil then
|
|
||||||
local tempMenu = {}
|
|
||||||
for k, v in pairs(Menu) do tempMenu[k + 1] = v end
|
|
||||||
tempMenu[1] = { header = data.header, txt = data.headertxt or "", isMenuHeader = true }
|
|
||||||
Menu = tempMenu
|
|
||||||
end
|
|
||||||
for k in pairs(Menu) do
|
|
||||||
if not Menu[k].params or not Menu[k].params.event then
|
|
||||||
Menu[k].params = {
|
|
||||||
isAction = true,
|
|
||||||
event = Menu[k].onSelect or function() end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
if not Menu[k].header then Menu[k].header = " " end
|
|
||||||
if Menu[k].arrow then Menu[k].icon = "fas fa-angle-right" end
|
|
||||||
Menu[k].isMenuHeader = Menu[k].isMenuHeader or Menu[k].disable
|
|
||||||
end
|
|
||||||
TriggerEvent("jim_bridge:client:openMenu", Menu)
|
|
||||||
|
|
||||||
elseif Config.System.Menu == "ox" then
|
|
||||||
local index = nil
|
local index = nil
|
||||||
if data.onBack and not data.onSelected then
|
if data.onBack and not data.onSelected then
|
||||||
table.insert(Menu, 1, {
|
table.insert(Menu, 1, {
|
||||||
@@ -286,11 +245,57 @@ function openMenu(Menu, data)
|
|||||||
function(data, menu)
|
function(data, menu)
|
||||||
menu.close()
|
menu.close()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
elseif Config.System.Menu == "lation" then
|
||||||
|
if data.onBack then
|
||||||
|
table.insert(Menu, 1, {
|
||||||
|
icon = "fas fa-circle-arrow-left",
|
||||||
|
onSelect = data.onBack,
|
||||||
|
header = "Return",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
for k in pairs(Menu) do
|
||||||
|
if data.onSelected and Menu[k].arrow then
|
||||||
|
Menu[k].icon = "fas fa-angle-right"
|
||||||
|
end
|
||||||
|
-- If no title, use header or txt as title/label.
|
||||||
|
if not Menu[k].title then
|
||||||
|
if Menu[k].header ~= nil and Menu[k].header ~= "" then
|
||||||
|
Menu[k].title = Menu[k].header
|
||||||
|
Menu[k].label = Menu[k].header
|
||||||
|
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
|
||||||
|
else
|
||||||
|
Menu[k].title = Menu[k].txt
|
||||||
|
Menu[k].label = Menu[k].txt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Copy parameters from 'params' if available.
|
||||||
|
if Menu[k].params then
|
||||||
|
Menu[k].event = Menu[k].params.event
|
||||||
|
Menu[k].args = Menu[k].params.args or {}
|
||||||
|
end
|
||||||
|
if Menu[k].isMenuHeader then
|
||||||
|
Menu[k].readOnly = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exports.lation_ui:registerMenu({
|
||||||
|
id = 'menu',
|
||||||
|
title = data.header,
|
||||||
|
onExit = data.onExit and data.onExit or nil,
|
||||||
|
subtitle = (data.headertxt and data.headertxt or ""),
|
||||||
|
options = Menu,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Show menu
|
||||||
|
exports.lation_ui:showMenu('menu')
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- A line break constant used for menu header formatting.
|
--- A line break constant used for menu header formatting.
|
||||||
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta") and "\n" or "<br>"
|
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta" or Config.System.Menu == "lation") and "\n" or "<br>"
|
||||||
|
|
||||||
--- Checks if the current menu system is 'ox' or 'gta' for formatting purposes.
|
--- Checks if the current menu system is 'ox' or 'gta' for formatting purposes.
|
||||||
--- @return boolean boolean True if using ox or gta menus, otherwise false.
|
--- @return boolean boolean True if using ox or gta menus, otherwise false.
|
||||||
|
|||||||
@@ -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() + 120000 -- 2 minutes max wait (had to up this from 5 seconds because of slow servers)
|
||||||
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
|
||||||
|
|||||||
@@ -89,6 +89,14 @@ function craftingMenu(data)
|
|||||||
-- Check if the player can carry the required items (server callback).
|
-- Check if the player can carry the required items (server callback).
|
||||||
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
|
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
|
||||||
|
|
||||||
|
local usingStash = data.stashName ~= nil
|
||||||
|
|
||||||
|
Menu[#Menu+1] = {
|
||||||
|
icon = usingStash and "fas fa-boxes-stacked" or "fas fa-person",
|
||||||
|
header = "Material Source: "..(usingStash and "Job Stash" or "Player Inventory"),
|
||||||
|
disabled = true,
|
||||||
|
}
|
||||||
|
|
||||||
-- Process each recipe to create menu entries.
|
-- Process each recipe to create menu entries.
|
||||||
for i = 1, #Recipes do
|
for i = 1, #Recipes do
|
||||||
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
|
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
|
||||||
@@ -238,7 +246,7 @@ function multiCraft(data)
|
|||||||
})
|
})
|
||||||
|
|
||||||
local dialog = createInput(data.craftable.Header..(Config.System.Menu == "qb" and ": "..br.."How many to craft? "..br.."Max: "..carryMax or ""), {
|
local dialog = createInput(data.craftable.Header..(Config.System.Menu == "qb" and ": "..br.."How many to craft? "..br.."Max: "..carryMax or ""), {
|
||||||
((Config.System.Menu == "ox") and {
|
((Config.System.Menu == "ox" or Config.System.Menu == "lation") and {
|
||||||
type = "slider",
|
type = "slider",
|
||||||
label = "How many to craft? "..br.."Max: "..carryMax,
|
label = "How many to craft? "..br.."Max: "..carryMax,
|
||||||
required = true,
|
required = true,
|
||||||
@@ -311,6 +319,9 @@ end
|
|||||||
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
|
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
|
||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
|
|
||||||
|
Config.Crafting.SingleProgress = true -- Set false to use individual progress bars per craft
|
||||||
|
|
||||||
function makeItem(data)
|
function makeItem(data)
|
||||||
if CraftLock then return end
|
if CraftLock then return end
|
||||||
CraftLock = true
|
CraftLock = true
|
||||||
@@ -330,117 +341,144 @@ function makeItem(data)
|
|||||||
local crafted, crafting = true, true
|
local crafted, crafting = true, true
|
||||||
local cam = createTempCam(PlayerPedId(), data.coords)
|
local cam = createTempCam(PlayerPedId(), data.coords)
|
||||||
startTempCam(cam)
|
startTempCam(cam)
|
||||||
|
|
||||||
|
-- Calculate total bartime if SingleProgress is enabled
|
||||||
|
local totalBartime = bartime * craftAmount
|
||||||
|
|
||||||
|
-- Run ingredient check and usage separately first
|
||||||
for i = 1, craftAmount do
|
for i = 1, craftAmount do
|
||||||
for k, v in pairs(data.craft) do
|
for k, v in pairs(data.craft) do
|
||||||
if not excludeKeys[k] then
|
if not excludeKeys[k] and type(v) == "table" then
|
||||||
if type(v) == "table" then
|
for l, b in pairs(v) do
|
||||||
for l, b in pairs(v) do
|
|
||||||
if isInventoryOpen() then
|
|
||||||
print("^1Error^7: ^2Inventory is open, you tried to break things")
|
|
||||||
crafted, crafting = false, false
|
|
||||||
stopTempCam()
|
|
||||||
ClearPedTasks(PlayerPedId())
|
|
||||||
if canReturn then craftingMenu(data) end
|
|
||||||
CraftLock = false
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if crafting and progressBar({
|
|
||||||
label = "Using "..b.." "..Items[l].label,
|
|
||||||
time = 1000,
|
|
||||||
cancel = true,
|
|
||||||
dict = 'pickup_object',
|
|
||||||
anim = "putdown_low",
|
|
||||||
flag = 49,
|
|
||||||
icon = l,
|
|
||||||
}) then
|
|
||||||
TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
|
|
||||||
else
|
|
||||||
crafted, crafting = false, false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
Wait(200)
|
|
||||||
end
|
|
||||||
if isInventoryOpen() then
|
if isInventoryOpen() then
|
||||||
print("^1Error^7: ^2Inventory is open, you tried to break things")
|
print("^1Error^7: ^2Inventory is open, you tried to break things")
|
||||||
crafted, crafting = false, false
|
|
||||||
stopTempCam()
|
stopTempCam()
|
||||||
ClearPedTasks(PlayerPedId())
|
ClearPedTasks(PlayerPedId())
|
||||||
if canReturn then craftingMenu(data) end
|
if canReturn then craftingMenu(data) end
|
||||||
CraftLock = false
|
CraftLock = false
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if crafting and progressBar({
|
||||||
if crafted then
|
label = "Using "..b.." "..Items[l].label,
|
||||||
local craftProp = nil
|
time = 1000,
|
||||||
if prop then
|
cancel = true,
|
||||||
craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
|
dict = 'pickup_object',
|
||||||
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), prop.bone), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true, true, false, true, 1, true)
|
anim = "putdown_low",
|
||||||
end
|
flag = 49,
|
||||||
if data.sound then
|
icon = l,
|
||||||
local s = data.sound
|
}) then
|
||||||
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
|
TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
|
||||||
end
|
else
|
||||||
if isInventoryOpen() then
|
crafted, crafting = false, false
|
||||||
--print("^1Error^7: ^2Inventory is open, you tried to break things")
|
break
|
||||||
crafted, crafting = false, false
|
|
||||||
crafted, crafting, CraftLock = false, false, false
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if crafting and progressBar({
|
|
||||||
label = bartext..((metadata and metadata.label) or Items[data.item].label),
|
|
||||||
time = bartime,
|
|
||||||
cancel = true,
|
|
||||||
dict = animDict,
|
|
||||||
anim = anim,
|
|
||||||
flag = 49,
|
|
||||||
icon = data.item,
|
|
||||||
request = true,
|
|
||||||
}) then
|
|
||||||
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
|
|
||||||
currentToken = nil -- clear client cached token
|
|
||||||
CreateThread(function()
|
|
||||||
if data.craft["hasCrafted"] ~= nil then
|
|
||||||
debugPrint("^5Bridge^7: ^3hasCrafted ^2Found^7, ^2marking ^7'^4"..data.item.."^7' ^2as crafted for player^7")
|
|
||||||
data.craftable.craftedItems[data.item] = true
|
|
||||||
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
|
|
||||||
end
|
|
||||||
Wait(100)
|
|
||||||
if data.craft["exp"] ~= nil then
|
|
||||||
craftingLevel += data.craft["exp"].give
|
|
||||||
jsonPrint(data.craft["exp"])
|
|
||||||
debugPrint("^6Bridge^7: ^3EXP ^2found^7, ^2giving exp for ^7'^4"..data.item.."^7'")
|
|
||||||
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
if data.craftable.Recipes[1].oneUse == true then
|
|
||||||
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
|
|
||||||
local breakId = GetSoundId()
|
|
||||||
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
|
|
||||||
canReturn = false
|
|
||||||
end
|
|
||||||
if data.sound then
|
|
||||||
StopSound(data.sound.soundId)
|
|
||||||
end
|
|
||||||
if data.requiredItemfunc then
|
|
||||||
data.requiredItemfunc()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
crafting = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if craftProp then destroyProp(craftProp) end
|
|
||||||
end
|
end
|
||||||
|
Wait(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Wait(500)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not crafted then
|
||||||
|
stopTempCam()
|
||||||
|
ClearPedTasks(PlayerPedId())
|
||||||
|
if canReturn then craftingMenu(data) end
|
||||||
|
CraftLock = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle SingleProgress option
|
||||||
|
if Config.Crafting.SingleProgress then
|
||||||
|
local craftProp = nil
|
||||||
|
if prop then
|
||||||
|
craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
|
||||||
|
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), prop.bone), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true, true, false, true, 1, true)
|
||||||
|
end
|
||||||
|
if data.sound then
|
||||||
|
local s = data.sound
|
||||||
|
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
|
||||||
|
end
|
||||||
|
if crafting and progressBar({
|
||||||
|
label = bartext..((metadata and metadata.label) or Items[data.item].label).." x"..craftAmount,
|
||||||
|
time = totalBartime,
|
||||||
|
cancel = true,
|
||||||
|
dict = animDict,
|
||||||
|
anim = anim,
|
||||||
|
flag = 49,
|
||||||
|
icon = data.item,
|
||||||
|
request = true,
|
||||||
|
}) then
|
||||||
|
data.craft.amount = craftAmount
|
||||||
|
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
|
||||||
|
currentToken = nil -- clear client cached token
|
||||||
|
-- handle metadata and experience in a single go
|
||||||
|
if data.craft["hasCrafted"] ~= nil then
|
||||||
|
data.craftable.craftedItems[data.item] = true
|
||||||
|
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
|
||||||
|
end
|
||||||
|
if data.craft["exp"] ~= nil then
|
||||||
|
craftingLevel += data.craft["exp"].give * craftAmount
|
||||||
|
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
|
||||||
|
end
|
||||||
|
if data.craftable.Recipes[1].oneUse == true then
|
||||||
|
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
|
||||||
|
local breakId = GetSoundId()
|
||||||
|
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
|
||||||
|
canReturn = false
|
||||||
|
end
|
||||||
|
if data.sound then
|
||||||
|
StopSound(data.sound.soundId)
|
||||||
|
end
|
||||||
|
if data.requiredItemfunc then
|
||||||
|
data.requiredItemfunc()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if craftProp then destroyProp(craftProp) end
|
||||||
|
else
|
||||||
|
-- Run the original loop for multiple progress bars
|
||||||
|
for i = 1, craftAmount do
|
||||||
|
if crafting and progressBar({
|
||||||
|
label = bartext..((metadata and metadata.label) or Items[data.item].label),
|
||||||
|
time = bartime,
|
||||||
|
cancel = true,
|
||||||
|
dict = animDict,
|
||||||
|
anim = anim,
|
||||||
|
flag = 49,
|
||||||
|
icon = data.item,
|
||||||
|
request = true,
|
||||||
|
}) then
|
||||||
|
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
|
||||||
|
currentToken = nil
|
||||||
|
if data.craft["hasCrafted"] ~= nil then
|
||||||
|
data.craftable.craftedItems[data.item] = true
|
||||||
|
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
|
||||||
|
end
|
||||||
|
if data.craft["exp"] ~= nil then
|
||||||
|
craftingLevel += data.craft["exp"].give
|
||||||
|
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
|
||||||
|
end
|
||||||
|
if data.craftable.Recipes[1].oneUse == true then
|
||||||
|
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
|
||||||
|
local breakId = GetSoundId()
|
||||||
|
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
|
||||||
|
canReturn = false
|
||||||
|
end
|
||||||
|
if data.requiredItemfunc then
|
||||||
|
data.requiredItemfunc()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Wait(500)
|
||||||
stopTempCam()
|
stopTempCam()
|
||||||
CraftLock = false
|
CraftLock = false
|
||||||
if canReturn then craftingMenu(data) end
|
if canReturn then craftingMenu(data) end
|
||||||
ClearPedTasks(PlayerPedId())
|
ClearPedTasks(PlayerPedId())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
-- Server Event Handler: Crafted Item
|
-- Server Event Handler: Crafted Item
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|||||||
@@ -35,11 +35,26 @@ function drawText(image, input, style, oxStyleTable)
|
|||||||
|
|
||||||
elseif Config.System.drawText == "ox" then
|
elseif Config.System.drawText == "ox" then
|
||||||
-- Append newline spacing to each input line.
|
-- Append newline spacing to each input line.
|
||||||
|
local inputnum = countTable(input)
|
||||||
for k, v in pairs(input) do
|
for k, v in pairs(input) do
|
||||||
input[k] = v.." \n"
|
input[k] = v..(inputnum ~= k and " \n" or "")
|
||||||
end
|
end
|
||||||
lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable })
|
lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable })
|
||||||
|
|
||||||
|
elseif Config.System.drawText == "lation" then
|
||||||
|
local inputnum = countTable(input)
|
||||||
|
for k, v in pairs(input) do
|
||||||
|
input[k] = v..(inputnum ~= k and " \n" or "")
|
||||||
|
end
|
||||||
|
exports.lation_ui:showText({
|
||||||
|
--title = " ",
|
||||||
|
description = table.concat(input),
|
||||||
|
keybind = nil,
|
||||||
|
icon = (image and radarTable[image] or image) or nil,
|
||||||
|
iconColor = '#3B82F6',
|
||||||
|
position = 'center-left'
|
||||||
|
})
|
||||||
|
|
||||||
elseif Config.System.drawText == "gta" then
|
elseif Config.System.drawText == "gta" then
|
||||||
-- Concatenate input lines and apply GTA style formatting.
|
-- Concatenate input lines and apply GTA style formatting.
|
||||||
for i = 1, #input do
|
for i = 1, #input do
|
||||||
@@ -88,6 +103,8 @@ function hideText()
|
|||||||
exports[QBExport]:HideText()
|
exports[QBExport]:HideText()
|
||||||
elseif Config.System.drawText == "ox" then
|
elseif Config.System.drawText == "ox" then
|
||||||
lib.hideTextUI()
|
lib.hideTextUI()
|
||||||
|
elseif Config.System.drawText == "lation" then
|
||||||
|
exports.lation_ui:hideText()
|
||||||
elseif Config.System.drawText == "gta" then
|
elseif Config.System.drawText == "gta" then
|
||||||
ClearAllHelpMessages()
|
ClearAllHelpMessages()
|
||||||
elseif Config.System.drawText == "esx" then
|
elseif Config.System.drawText == "esx" then
|
||||||
|
|||||||
@@ -138,6 +138,98 @@ function createInput(title, opts)
|
|||||||
)
|
)
|
||||||
return dialog
|
return dialog
|
||||||
|
|
||||||
|
elseif Config.System.Menu == "lation" then
|
||||||
|
|
||||||
|
for i in pairs(opts) do
|
||||||
|
currentNum += 1
|
||||||
|
if opts[i] == nil then currentNum -= 1 goto skip end
|
||||||
|
if opts[i].type == "radio" then
|
||||||
|
for k in pairs(opts[i].options) do
|
||||||
|
opts[i].options[k].label = opts[i].options[k].text
|
||||||
|
end
|
||||||
|
options[currentNum] = {
|
||||||
|
type = "select",
|
||||||
|
required = opts[i].isRequired,
|
||||||
|
label = opts[i].label or opts[i].text,
|
||||||
|
name = opts[i].name,
|
||||||
|
default = opts[i].default or opts[i].options[1].value,
|
||||||
|
options = opts[i].options,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if opts[i].type == "number" then
|
||||||
|
options[currentNum] = {
|
||||||
|
type = opts[i].type,
|
||||||
|
label = opts[i].label or opts[i].text,
|
||||||
|
description = opts[i].txt and " - "..opts[i].txt or "",
|
||||||
|
required = opts[i].isRequired,
|
||||||
|
name = opts[i].name,
|
||||||
|
options = opts[i].options,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if opts[i].type == "text" then
|
||||||
|
options[currentNum] = {
|
||||||
|
type = "input",
|
||||||
|
label = opts[i].label or opts[i].text,
|
||||||
|
description = (opts[i].txt and " - "..opts[i].txt or ""),
|
||||||
|
placeholder = opts[i].default,
|
||||||
|
required = opts[i].isRequired,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if opts[i].type == "select" then
|
||||||
|
options[currentNum] = {
|
||||||
|
type = opts[i].type,
|
||||||
|
label = opts[i].label or opts[i].text,
|
||||||
|
description = opts[i].txt and " - "..opts[i].txt or "",
|
||||||
|
required = opts[i].isRequired,
|
||||||
|
name = opts[i].name,
|
||||||
|
options = opts[i].options,
|
||||||
|
min = opts[i].min,
|
||||||
|
max = opts[i].max,
|
||||||
|
default = opts[i].default,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[i].type == "checkbox" then
|
||||||
|
for k in pairs(opts[i].options) do
|
||||||
|
if options[currentNum] then currentNum += 1 end
|
||||||
|
options[currentNum] = {
|
||||||
|
type = opts[i].type,
|
||||||
|
label = opts[i].options[k].text..(opts[i].txt and " - "..opts[i].txt or ""),
|
||||||
|
name = opts[i].options[k].value,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[i].type == "color" then
|
||||||
|
options[currentNum] = {
|
||||||
|
type = opts[i].type,
|
||||||
|
label = opts[i].label,
|
||||||
|
required = opts[i].isRequired,
|
||||||
|
format = opts[i].format,
|
||||||
|
default = opts[i].default,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[i].type == "slider" then
|
||||||
|
options[currentNum] = {
|
||||||
|
type = opts[i].type,
|
||||||
|
label = opts[i].label,
|
||||||
|
required = opts[i].required,
|
||||||
|
min = opts[i].min,
|
||||||
|
max = opts[i].max,
|
||||||
|
default = opts[i].default,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
::skip::
|
||||||
|
end
|
||||||
|
|
||||||
|
local dialog = exports.lation_ui:input({
|
||||||
|
title = title,
|
||||||
|
submitText = "Accept",
|
||||||
|
options = options
|
||||||
|
})
|
||||||
|
return dialog
|
||||||
|
|
||||||
elseif Config.System.Menu == "gta" then
|
elseif Config.System.Menu == "gta" then
|
||||||
WarMenu.CreateMenu(tostring(opts),
|
WarMenu.CreateMenu(tostring(opts),
|
||||||
title,
|
title,
|
||||||
|
|||||||
@@ -275,7 +275,6 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
|||||||
else
|
else
|
||||||
local amountToAdd = amount or 1
|
local amountToAdd = amount or 1
|
||||||
if isStarted(OXInv) then invName = OXInv
|
if isStarted(OXInv) then invName = OXInv
|
||||||
jsonPrint(info)
|
|
||||||
exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
|
exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
|
||||||
|
|
||||||
elseif isStarted(QSInv) then invName = QSInv
|
elseif isStarted(QSInv) then invName = QSInv
|
||||||
@@ -516,17 +515,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)
|
||||||
@@ -653,7 +654,7 @@ function canCarry(itemTable, src)
|
|||||||
|
|
||||||
elseif isStarted(TgiannInv) then
|
elseif isStarted(TgiannInv) then
|
||||||
for k, v in pairs(itemTable) do
|
for k, v in pairs(itemTable) do
|
||||||
resultTable[k] = exports[TgiannInv]:CanCarryItem(source, k, v)
|
resultTable[k] = exports[TgiannInv]:CanCarryItem(src, k, v)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif isStarted(JPRInv) then
|
elseif isStarted(JPRInv) then
|
||||||
@@ -868,6 +869,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)
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@@ -882,9 +891,28 @@ end
|
|||||||
--- if doesItemExist(item) then print("item exists") end
|
--- if doesItemExist(item) then print("item exists") end
|
||||||
--- ```
|
--- ```
|
||||||
function doesItemExist(item)
|
function doesItemExist(item)
|
||||||
if not item or item == "" then return false end
|
if not item or item == "" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not Items or not next(Items) then
|
||||||
|
return item.." (Missing)"
|
||||||
|
end
|
||||||
if Items[item] ~= nil then
|
if Items[item] ~= nil then
|
||||||
return true
|
return Items[item]
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function getItemLabel(item)
|
||||||
|
if not item or item == "" then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
if not Items or not next(Items) then
|
||||||
|
return item.." (Missing)"
|
||||||
|
end
|
||||||
|
if Items[item] ~= nil then
|
||||||
|
return Items[item].label
|
||||||
|
end
|
||||||
|
return item.." (Missing)"
|
||||||
|
end
|
||||||
@@ -135,6 +135,34 @@ function progressBar(data)
|
|||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
elseif Config.System.ProgressBar == "lation" then
|
||||||
|
if exports.lation_ui:progressBar({
|
||||||
|
label = data.label,
|
||||||
|
description = nil,
|
||||||
|
duration = debugMode and 1000 or data.time,
|
||||||
|
icon = data.icon,
|
||||||
|
useWhileDead = data.dead or false,
|
||||||
|
disable = {
|
||||||
|
combat = data.combat or true,
|
||||||
|
move = data.disableMovement or false,
|
||||||
|
car = data.disableMovement or false,
|
||||||
|
},
|
||||||
|
anim = {
|
||||||
|
dict = data.dict,
|
||||||
|
clip = data.anim,
|
||||||
|
},
|
||||||
|
prop = {
|
||||||
|
model = data.prop and data.prop.model,
|
||||||
|
pos = data.prop and (data.prop.pos or vec3(0, 0, 0)),
|
||||||
|
rot = data.prop and (data.prop.rot or vec3(0, 0, 0)),
|
||||||
|
bone = data.prop and (data.prop.bone or 0)
|
||||||
|
}
|
||||||
|
}) then
|
||||||
|
result = true
|
||||||
|
else
|
||||||
|
result = true
|
||||||
|
end
|
||||||
|
|
||||||
elseif Config.System.ProgressBar == "red" then
|
elseif Config.System.ProgressBar == "red" then
|
||||||
-- Currently only uses jim-redui if you choose this option
|
-- Currently only uses jim-redui if you choose this option
|
||||||
if exports["jim_bridge"]:redProgressBar({
|
if exports["jim_bridge"]:redProgressBar({
|
||||||
@@ -204,6 +232,8 @@ function stopProgressBar()
|
|||||||
exports[OXLibExport]:cancelProgress()
|
exports[OXLibExport]:cancelProgress()
|
||||||
elseif Config.System.ProgressBar == "qb" then
|
elseif Config.System.ProgressBar == "qb" then
|
||||||
TriggerEvent("progressbar:client:cancel")
|
TriggerEvent("progressbar:client:cancel")
|
||||||
|
elseif Config.System.ProgressBar == "lation" then
|
||||||
|
exports.lation_ui:cancelProgress()
|
||||||
elseif Config.System.ProgressBar == "gta" or Config.System.ProgressBar == "red" then
|
elseif Config.System.ProgressBar == "gta" or Config.System.ProgressBar == "red" then
|
||||||
exports["jim_bridge"]:stopProgressBar()
|
exports["jim_bridge"]:stopProgressBar()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,6 +62,20 @@ function triggerNotify(title, message, type, src)
|
|||||||
else
|
else
|
||||||
TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
|
TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
|
||||||
end
|
end
|
||||||
|
elseif Config.System.Notify == "lation" then
|
||||||
|
if not src then
|
||||||
|
exports.lation_ui:notify({
|
||||||
|
title = title,
|
||||||
|
message = message,
|
||||||
|
type = type or "success",
|
||||||
|
})
|
||||||
|
else
|
||||||
|
TriggerClientEvent("lation_ui:notify", src, {
|
||||||
|
title = title,
|
||||||
|
message = message,
|
||||||
|
type = type or "success",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
elseif Config.System.Notify == "red" then
|
elseif Config.System.Notify == "red" then
|
||||||
if isStarted("jim-redui") then
|
if isStarted("jim-redui") then
|
||||||
|
|||||||
@@ -32,6 +32,25 @@ function skillCheck(data)
|
|||||||
elseif Config.System.skillCheck == "gta" then
|
elseif Config.System.skillCheck == "gta" then
|
||||||
exports.jim_bridge:skillCheck()
|
exports.jim_bridge:skillCheck()
|
||||||
|
|
||||||
|
elseif Config.System.skillCheck == "lation" then
|
||||||
|
local Skillbar = exports.lation_ui:skillCheck("",
|
||||||
|
{
|
||||||
|
"easy",
|
||||||
|
"easy",
|
||||||
|
"easy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4"
|
||||||
|
})
|
||||||
|
|
||||||
|
if Skillbar then
|
||||||
|
result = true
|
||||||
|
else
|
||||||
|
result = false
|
||||||
|
end
|
||||||
else
|
else
|
||||||
result = true
|
result = true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ function searchCar(vehicle)
|
|||||||
"Military", --20
|
"Military", --20
|
||||||
"Commercial", --21
|
"Commercial", --21
|
||||||
"Trains", --22
|
"Trains", --22
|
||||||
|
"Open Wheel", --23
|
||||||
}
|
}
|
||||||
if Vehicles then
|
if Vehicles then
|
||||||
for k, v in pairs(Vehicles) do
|
for k, v in pairs(Vehicles) do
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -6,6 +6,25 @@ local function loadTextureDict(dict)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function loadAnimDict(animDict)
|
||||||
|
if not DoesAnimDictExist(animDict) then
|
||||||
|
print("^6Bridge^7: ^1ERROR^7: ^2Anim Dictionary^7 - '^6"..animDict.."^7' ^2does not exist in server") return
|
||||||
|
else
|
||||||
|
while not HasAnimDictLoaded(animDict) do RequestAnimDict(animDict) Wait(5) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function playAnim(animDict, animName, duration, flag, ped, speed)
|
||||||
|
loadAnimDict(animDict)
|
||||||
|
TaskPlayAnim(ped and ped or PlayerPedId(), animDict, animName, speed or 8.0, speed or -8.0, duration or 30000, flag or 50, 1, false, false, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function stopAnim(animDict, animName, ped)
|
||||||
|
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
|
||||||
|
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
|
||||||
|
unloadAnimDict(animDict)
|
||||||
|
end
|
||||||
|
|
||||||
function redProgressBar(data)
|
function redProgressBar(data)
|
||||||
local ped = PlayerPedId()
|
local ped = PlayerPedId()
|
||||||
|
|
||||||
|
|||||||
25
version.txt
25
version.txt
@@ -1,17 +1,14 @@
|
|||||||
2.0.18
|
2.0.20
|
||||||
|
|
||||||
- Fix ox_inv client item caching (images)
|
- Add Full support for lation_ui
|
||||||
- Add better checks if errors in ox_inv items and add more prominent error messages all around
|
- Add "Open Wheel" to searchCar()
|
||||||
- Tidy up several debugPrints
|
- Increase timeout for cache timer from 5 seconds to 2 minutes
|
||||||
- Move AuthEvents to new file
|
- Fix GTA progressbar erroring on playAnim()
|
||||||
- Fix clearStash error with PSInv and JPRInv
|
- Add Igredient location indicator when crafting
|
||||||
- Add extra trigger event for PSInv when opening stash (hopefully fix missing items for some)
|
- Fix JPRInv checks breaking early when caching framework info
|
||||||
- Fix onPlayerUnloaded() not triggering
|
- Enhance doesItemExist() and add getItemLabel() function
|
||||||
- Add support for esx_society in openBossMenu()
|
- Add Support for old qb-inventory config loading
|
||||||
- Added support for okokbilling to billPlayer()
|
- Fix typo in cancarry when using Tgiann Inv
|
||||||
- Rework progressBar and ui_modules folder
|
- Add support for single progressBar when crafting
|
||||||
- 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
|
||||||
Reference in New Issue
Block a user