mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 05:56:02 +01:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b07c089946 | ||
|
|
d603067c77 | ||
|
|
5a97b8f83a | ||
|
|
d0a09871c7 | ||
|
|
e87aa8d74d | ||
|
|
e204e6153d | ||
|
|
5c69f8dd28 | ||
|
|
f3fa309dbf | ||
|
|
9d74e7cb46 | ||
|
|
fea72bba2e | ||
|
|
b364f14260 | ||
|
|
8077ec4f46 | ||
|
|
e5277574df | ||
|
|
e0563dd4ce | ||
|
|
1987cf3df3 | ||
|
|
9f5145c3b2 | ||
|
|
5921cf8adc | ||
|
|
f154b45076 | ||
|
|
2c41272c36 | ||
|
|
57d4beafed | ||
|
|
bdb526bd1e | ||
|
|
bb9dbe248f | ||
|
|
caf2e7fbed | ||
|
|
d0b8fb16d6 | ||
|
|
2e57e66095 | ||
|
|
05fc856e86 | ||
|
|
74e6a679e2 | ||
|
|
17db5aa31a | ||
|
|
387c97de71 | ||
|
|
de45491c8c | ||
|
|
4da4cbfc15 | ||
|
|
9fbf2f2211 | ||
|
|
41b2965743 | ||
|
|
a02c11d3cc | ||
|
|
c888aa5ecf | ||
|
|
30de9a2d9c | ||
|
|
804327f3aa | ||
|
|
ba59418b6d | ||
|
|
f39678002f | ||
|
|
3e402cae3d | ||
|
|
b4aa1b3eda | ||
|
|
0f9bab00ef | ||
|
|
9d8706e8c6 | ||
|
|
6ba1e14888 | ||
|
|
0454f8c0a9 | ||
|
|
e17d8f2b93 | ||
|
|
b44dfa91e5 | ||
|
|
51d4c88694 | ||
|
|
f5f502875e | ||
|
|
9c769f4e8e | ||
|
|
87cdd5502e | ||
|
|
68d713a8dc | ||
|
|
dd34f8aab1 | ||
|
|
4a3fb8db4d | ||
|
|
59733de08e | ||
|
|
2b7a1c2e6d | ||
|
|
5db9e88bb9 | ||
|
|
a5b1f750dd |
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)
|
||||
@@ -1,10 +1,11 @@
|
||||
--[[
|
||||
Cached Resource Initialization Module
|
||||
--------------------------------------
|
||||
This module initializes shared data (Items, Vehicles, Jobs, Gangs)
|
||||
only once and stores it in _G.__jimBridgeDataCache for reuse across scripts.
|
||||
Initializes shared resource data (Items, Vehicles, Jobs, Gangs) once and caches it
|
||||
to improve performance and reduce redundant data fetching across scripts.
|
||||
]]
|
||||
|
||||
-- Define resource names used in different frameworks
|
||||
local Exports = {
|
||||
QBExport = "qb-core",
|
||||
QBXExport = "qbx_core",
|
||||
@@ -19,6 +20,7 @@ local Exports = {
|
||||
CodeMInv = "codem-inventory",
|
||||
OrigenInv = "origen_inventory",
|
||||
TgiannInv = "tgiann-inventory",
|
||||
JPRInv = "jpr-inventory",
|
||||
|
||||
OXLibExport = "ox_lib",
|
||||
|
||||
@@ -32,17 +34,23 @@ local Exports = {
|
||||
RSGInv = "rsg-inventory"
|
||||
}
|
||||
|
||||
-- Ensure cache only runs once
|
||||
if _G.__jimBridgeDataCache then return end
|
||||
_G.__jimBridgeDataCache = {}
|
||||
local cache = _G.__jimBridgeDataCache
|
||||
-- Prevent reloading if cache is already initialized
|
||||
local cache = {
|
||||
Items = {},
|
||||
Vehicles = {},
|
||||
Jobs = {},
|
||||
Gangs = {},
|
||||
}
|
||||
|
||||
function checkExists(resourceName)
|
||||
-- Helper function to check if resource exists in server (instead of if it is already started)
|
||||
local function checkExists(resourceName)
|
||||
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
|
||||
end
|
||||
|
||||
-- Ensure oxmysql resource is loaded
|
||||
local fileLoader = assert(load(LoadResourceFile("oxmysql", ('lib/MySQL.lua')), ('@@oxmysql/lib/MySQL.lua')))
|
||||
fileLoader()
|
||||
|
||||
if checkExists(Exports.OXCoreExport) then
|
||||
-- Detected OX_Core in server, wait for it to be started if needed
|
||||
while GetResourceState(Exports.OXCoreExport) ~= "started" do Wait(100) end
|
||||
@@ -56,9 +64,8 @@ if checkExists(Exports.ESXExport) then
|
||||
fileLoader()
|
||||
end
|
||||
|
||||
-- Init variables
|
||||
local Items, Vehicles, Jobs, Gangs, Core = nil, nil, nil, nil, nil
|
||||
local itemResource, jobResource, vehResource = "", "", ""
|
||||
-- Initialize variables for caching
|
||||
local itemResource, jobResource, vehResource = "N/A", "N/A", "N/A"
|
||||
|
||||
-- Print just to announce it knows the exports/scripts exist in the server
|
||||
for _, v in pairs(Exports) do
|
||||
@@ -70,51 +77,75 @@ end
|
||||
---------------------
|
||||
---- Load Items -----
|
||||
---------------------
|
||||
-- Items initialization based on detected inventory system
|
||||
if checkExists(Exports.OXInv) then
|
||||
-- Wait for OX Inventory to start if it's not already started
|
||||
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
||||
itemResource = Exports.OXInv
|
||||
Items = exports[Exports.OXInv]:Items()
|
||||
-- IF QBX-Core is running, merge items from there
|
||||
if checkExists(Exports.QBExport)then
|
||||
local tempWeapons = exports[Exports.QBExport]:GetCoreObject().Shared.Weapons
|
||||
for k, v in pairs(tempWeapons) do
|
||||
local info = exports[Exports.OXInv]:Items(v.name)
|
||||
local weight = info and info.weight or 0
|
||||
if not Items[v.name] then
|
||||
Items[v.name] = {
|
||||
name = v.name,
|
||||
label = v.label,
|
||||
type = "weapon",
|
||||
ammotype = v.ammotype or "AMMO_PISTOL",
|
||||
weight = weight,
|
||||
image = v.image or (v.name..".png"),
|
||||
description = v.label or "",
|
||||
}
|
||||
|
||||
local success, result = pcall(function()
|
||||
return exports[Exports.OXInv]:Items()
|
||||
end)
|
||||
if success and result then
|
||||
cache.Items = result
|
||||
end
|
||||
|
||||
-- Get Weapon info and duplicate them if they are uppercase
|
||||
-- (duplicate incase anything checks for the uppercase version)
|
||||
for k, v in pairs(cache.Items) do
|
||||
if type(k) == "string" then
|
||||
if k:find("WEAPON") then
|
||||
cache.Items[k:lower()] = cache.Items[k]
|
||||
end
|
||||
else
|
||||
print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?")
|
||||
print("^1Possible Issue found^7:")
|
||||
print(json.encode(Items[k], {indent = true}))
|
||||
end
|
||||
end
|
||||
-- tidy info into something jim_bridge can use
|
||||
for k, v in pairs(Items) do
|
||||
Items[k].image = (v.client and v.client.image) and v.client.image:gsub("nui://"..Exports.OXInv.."/web/images/", "") or k..".png"
|
||||
Items[k].hunger = v.client and v.client.hunger
|
||||
Items[k].thirst = v.client and v.client.thirst
|
||||
|
||||
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
|
||||
while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end
|
||||
itemResource = Exports.QBExport
|
||||
Core = exports[Exports.QBExport]:GetCoreObject()
|
||||
Items = Core.Shared.Items
|
||||
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
|
||||
|
||||
elseif checkExists(Exports.ESXExport) then
|
||||
itemResource = Exports.ESXExport
|
||||
if GetResourceState(Exports.QSInv):find("start") then
|
||||
Items = exports[Exports.QSInv]:GetItemList()
|
||||
cache.Items = exports[Exports.QSInv]:GetItemList()
|
||||
else
|
||||
Items = ESX.GetItems()
|
||||
while not next(Items) do
|
||||
Items = ESX.GetItems()
|
||||
cache.Items = ESX.GetItems()
|
||||
while not next(cache.Items) do
|
||||
cache.Items = ESX.GetItems()
|
||||
Wait(1000)
|
||||
end
|
||||
end
|
||||
@@ -122,23 +153,26 @@ elseif checkExists(Exports.ESXExport) then
|
||||
elseif checkExists(Exports.RSGExport) then
|
||||
while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end
|
||||
itemResource = Exports.RSGExport
|
||||
Core = exports[Exports.RSGExport]:GetCoreObject()
|
||||
Items = Core.Shared.Items
|
||||
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
|
||||
end
|
||||
|
||||
---------------------
|
||||
--- Load Vehicles ---
|
||||
---------------------
|
||||
if checkExists(Exports.QBXExport) or checkExists(Exports.QBExport) then
|
||||
-- Vehicle loading depending on framework
|
||||
if checkExists(Exports.QBXExport) then
|
||||
vehResource = Exports.QBXExport
|
||||
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||
|
||||
elseif checkExists(Exports.QBExport)then
|
||||
vehResource = Exports.QBExport
|
||||
Core = Core or exports[Exports.QBExport]:GetCoreObject()
|
||||
Vehicles = Core.Shared.Vehicles
|
||||
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||
|
||||
elseif checkExists(Exports.OXCoreExport) then
|
||||
vehResource = Exports.OXCoreExport
|
||||
Vehicles = {}
|
||||
cache.Vehicles = {}
|
||||
for k, v in pairs(Ox.GetVehicleData()) do
|
||||
Vehicles[k] = {
|
||||
cache.Vehicles[k] = {
|
||||
model = k, hash = GetHashKey(k),
|
||||
price = v.price,
|
||||
name = v.name,
|
||||
@@ -149,9 +183,8 @@ elseif checkExists(Exports.OXCoreExport) then
|
||||
elseif checkExists(Exports.ESXExport) then
|
||||
vehResource = Exports.ESXExport
|
||||
while not MySQL do Wait(1000) end
|
||||
Vehicles = {}
|
||||
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
|
||||
Vehicles[v.model] = {
|
||||
cache.Vehicles[v.model] = {
|
||||
model = v.model,
|
||||
hash = GetHashKey(v.model),
|
||||
price = v.price,
|
||||
@@ -161,21 +194,24 @@ elseif checkExists(Exports.ESXExport) then
|
||||
|
||||
elseif checkExists(Exports.RSGExport) then
|
||||
vehResource = Exports.RSGExport
|
||||
Core = Core or exports[Exports.RSGExport]:GetCoreObject()
|
||||
Vehicles = Core.Shared.Vehicles
|
||||
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
|
||||
|
||||
end
|
||||
|
||||
---------------------
|
||||
----- Load Jobs -----
|
||||
---------------------
|
||||
-- Jobs loading based on framework
|
||||
if checkExists(Exports.QBXExport) then
|
||||
jobResource = Exports.QBXExport
|
||||
Core = Core or exports[Exports.QBXExport]:GetCoreObject()
|
||||
Jobs, Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
||||
cache.Jobs, cache.Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
||||
|
||||
elseif checkExists(Exports.QBExport) then
|
||||
jobResource = Exports.QBExport
|
||||
cache.Jobs, cache.Gangs = exports[Exports.QBExport]:GetCoreObject().Shared.Jobs, exports[Exports.QBExport]:GetCoreObject().Shared.Gangs
|
||||
|
||||
elseif checkExists(Exports.OXCoreExport) then
|
||||
jobResource = Exports.OXCoreExport
|
||||
Jobs = {}
|
||||
while not MySQL do Wait(1000) end
|
||||
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
|
||||
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
|
||||
@@ -185,34 +221,29 @@ elseif checkExists(Exports.OXCoreExport) then
|
||||
gradeMap[grade.group][grade.grade] = { name = grade.label }
|
||||
end
|
||||
for _, job in pairs(tempJobs) do
|
||||
Jobs[job.name] = {
|
||||
cache.Jobs[job.name] = {
|
||||
label = job.label,
|
||||
grades = gradeMap[job.name] or {}
|
||||
}
|
||||
end
|
||||
Gangs = Jobs
|
||||
|
||||
elseif checkExists(Exports.QBExport) then
|
||||
jobResource = Exports.QBExport
|
||||
Core = Core or exports[Exports.QBExport]:GetCoreObject()
|
||||
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
||||
cache.Gangs = cache.Jobs
|
||||
|
||||
elseif checkExists(Exports.ESXExport) then
|
||||
jobResource = Exports.ESXExport
|
||||
ESX = exports[Exports.ESXExport]:getSharedObject()
|
||||
Jobs = ESX.GetJobs()
|
||||
while not next(Jobs) do
|
||||
cache.Jobs = ESX.GetJobs()
|
||||
while not next(cache.Jobs) do
|
||||
Wait(100)
|
||||
Jobs = ESX.GetJobs()
|
||||
cache.Jobs = ESX.GetJobs()
|
||||
end
|
||||
for Role, Grades in pairs(Jobs) do
|
||||
for Role, Grades in pairs(cache.Jobs) do
|
||||
-- Check for if user has added grades
|
||||
if Grades.grades == nil or not next(Grades.grades) then
|
||||
goto continue
|
||||
end
|
||||
for grade, info in pairs(Grades.grades) do
|
||||
if info.label and info.label:find("[Bb]oss") then
|
||||
Jobs[Role].grades[grade].isBoss = true
|
||||
cache.Jobs[Role].grades[grade].isBoss = true
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
@@ -225,61 +256,159 @@ elseif checkExists(Exports.ESXExport) then
|
||||
end
|
||||
|
||||
if highestGrade then
|
||||
print("found boss for", Role)
|
||||
Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
||||
cache.Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
Gangs = Jobs
|
||||
cache.Gangs = cache.Jobs
|
||||
|
||||
elseif checkExists(Exports.RSGExport) then
|
||||
jobResource = Exports.RSGExport
|
||||
Core = Core or exports[Exports.RSGExport]:GetCoreObject()
|
||||
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
||||
cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
|
||||
|
||||
end
|
||||
|
||||
-- Fallback if nil or empty
|
||||
if cache.Items == nil or not next(cache.Items) then
|
||||
print("^1--------------------------------------------^7")
|
||||
print("^1ERROR^7: ^1Can NOT find "..itemResource.." ^7Items ^1list^7, ^1possible error in that file or is it empty^7?")
|
||||
print("^1--------------------------------------------^7")
|
||||
cache.Items = {} -- Fallback to an empty table
|
||||
end
|
||||
if cache.Vehicles == nil or not next(cache.Vehicles) then
|
||||
print("^1--------------------------------------------^7")
|
||||
print("^1ERROR^7: ^1Can NOT find "..vehResource.." ^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
|
||||
|
||||
-- Save to global cache
|
||||
cache.Items = Items
|
||||
cache.Vehicles = Vehicles
|
||||
cache.Jobs = Jobs
|
||||
cache.Gangs = Gangs
|
||||
|
||||
CreateThread(function()
|
||||
local counts = {
|
||||
Items = 0,
|
||||
Vehicles = 0,
|
||||
Jobs = 0,
|
||||
Gangs = 0,
|
||||
Items = 0, Vehicles = 0, Jobs = 0, Gangs = 0,
|
||||
}
|
||||
for k, v in pairs(cache) do
|
||||
for count in pairs(v) do
|
||||
counts[k] += 1
|
||||
end
|
||||
if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end
|
||||
end
|
||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Items).."^2 Items from ^7"..itemResource)
|
||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Vehicles).."^2 Vehicles from ^7"..vehResource)
|
||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Jobs).."^2 Jobs from ^7"..jobResource)
|
||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Gangs).."^2 Gangs from ^7"..jobResource)
|
||||
if cache.InventoryWeight then
|
||||
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventoryWeight^7: ^3"..cache.InventoryWeight.."^7 (^3"..(cache.InventoryWeight / 1000).."kg^7)")
|
||||
end
|
||||
if cache.InventorySlots then
|
||||
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventorySlots^7: ^3"..cache.InventorySlots.."^7")
|
||||
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)
|
||||
|
||||
--print(json.encode(cache.Items , { indent = true}))
|
||||
|
||||
RegisterNetEvent("jim_bridge:requestCache", function()
|
||||
local src = source
|
||||
TriggerClientEvent("jim_bridge:receiveCache", src, _G.__jimBridgeDataCache)
|
||||
TriggerClientEvent("jim_bridge:receiveCache", src, cache)
|
||||
end)
|
||||
|
||||
|
||||
exports("GetSharedData", function()
|
||||
-- Wait for data to be ready before returning it
|
||||
local timeout = GetGameTimer() + 5000
|
||||
while (
|
||||
not _G.__jimBridgeDataCache or
|
||||
(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)
|
||||
not cache or
|
||||
(not cache.Items or next(cache.Items) == nil) or
|
||||
(not cache.Vehicles or next(cache.Vehicles) == nil) or
|
||||
(not cache.Jobs or next(cache.Jobs) == nil)
|
||||
) and GetGameTimer() < timeout do
|
||||
Wait(50)
|
||||
end
|
||||
return _G.__jimBridgeDataCache
|
||||
return cache
|
||||
end)
|
||||
@@ -1,6 +1,6 @@
|
||||
name "Jim_Bridge"
|
||||
author "Jimathy"
|
||||
version "2.0.15"
|
||||
version "2.0.19"
|
||||
description "Framework Bridge By Jimathy"
|
||||
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.'
|
||||
@@ -18,9 +18,10 @@ files {
|
||||
-- Version checker
|
||||
server_scripts {
|
||||
'frameworkCache.lua',
|
||||
'_versioncheck.lua'
|
||||
'_versioncheck.lua',
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
'ui_modules/*.lua'
|
||||
'clientFrameworkCache.lua',
|
||||
'ui_modules/*.lua',
|
||||
}
|
||||
118
shared/_authToken.lua
Normal file
118
shared/_authToken.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
-------------------------------------------------------------
|
||||
-- Exploit Auth System
|
||||
-------------------------------------------------------------
|
||||
AuthEvent = nil
|
||||
currentToken = nil
|
||||
if isServer() then
|
||||
|
||||
local AuthEvent = getScript()..":"..keyGen()..keyGen()..keyGen()..keyGen()..":"..keyGen()..keyGen()..keyGen()..keyGen()
|
||||
validTokens = validTokens or {}
|
||||
|
||||
createCallback(AuthEvent, function(source)
|
||||
local src = source
|
||||
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
||||
--debugPrint(GetInvokingResource())
|
||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||
return ""
|
||||
end
|
||||
debugPrint("^1Auth^7:^2 Player Source^7: "..src.." ^2requested new token^7:", token)
|
||||
validTokens[src] = token
|
||||
timeOutAuth(validTokens[src], src) -- Give script 10 seconds, then clear token
|
||||
return token
|
||||
end)
|
||||
|
||||
function timeOutAuth(token, src)
|
||||
local token = token
|
||||
SetTimeout(10000, function()
|
||||
if token == validTokens[src] then
|
||||
print("^1--------------------------------------------^7")
|
||||
print("^7Clearing token for player ^1"..src.."^7", token)
|
||||
print("^7This shouldn't happen unless a token has been called by a player or script and it hasn't been used")
|
||||
print("^1--------------------------------------------^7")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
RegisterNetEvent(getScript()..":clearAuthToken", function()
|
||||
local src = source
|
||||
debugPrint("^1Auth^7: ^2Manually removing token for Player Source^7:", src, validTokens[src])
|
||||
validTokens[src] = nil
|
||||
end)
|
||||
|
||||
receivedEvent = {}
|
||||
authCooldown = {}
|
||||
|
||||
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
||||
local src = source
|
||||
|
||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
||||
return ""
|
||||
end
|
||||
|
||||
if authCooldown[src] then
|
||||
debugPrint("^1Auth^7: ^3Cooldown active^7 for Player ^1"..src.."^7, ignoring additional auth request")
|
||||
return ""
|
||||
end
|
||||
|
||||
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
|
||||
|
||||
authCooldown[src] = true
|
||||
SetTimeout(5000, function() -- 5 second cooldown
|
||||
authCooldown[src] = nil
|
||||
end)
|
||||
|
||||
if not receivedEvent[src] then
|
||||
receivedEvent[src] = true
|
||||
return AuthEvent
|
||||
else
|
||||
print("^1Auth^7: ^1Player ^7"..src.." ^1tried to request auth token more than once^7")
|
||||
return ""
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent(getScript()..":clearAuthEventRequest", function()
|
||||
local src = source
|
||||
debugPrint("^1Auth^7: ^2Manually clearing Auth Event for Player Source^7:", src, AuthEvent)
|
||||
receivedEvent[src] = nil
|
||||
end)
|
||||
|
||||
-- Multiuse function to check if the generated client token is valid
|
||||
function checkToken(src, token, genType, name)
|
||||
if token == nil then
|
||||
debugPrint("^1Auth^7: ^1No token recieved^7")
|
||||
if genType == "stash" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1without an auth token^7")
|
||||
elseif genType == "item" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1without an auth token^7")
|
||||
end
|
||||
return false
|
||||
else
|
||||
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
|
||||
if token ~= validTokens[src] then
|
||||
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", token, validTokens[src])
|
||||
if genType == "stash" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1with an incorrect auth token^7")
|
||||
elseif genType == "item" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1with an incorrect auth token^7")
|
||||
end
|
||||
return false
|
||||
else
|
||||
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", token, validTokens[src])
|
||||
validTokens[src] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
onPlayerLoaded(function()
|
||||
debugPrint("^1Auth^7: ^2Requesting ^3Auth Event^7")
|
||||
AuthEvent = triggerCallback(getScript()..":callback:GetAuthEvent")
|
||||
end, true)
|
||||
|
||||
onPlayerUnload(function()
|
||||
debugPrint("^1Auth^7: ^2Clearing Auth Event^7")
|
||||
TriggerServerEvent(getScript()..":clearAuthEventRequest")
|
||||
end, true)
|
||||
end
|
||||
@@ -47,22 +47,21 @@ function onPlayerLoaded(func, onStart)
|
||||
|
||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||
onPlayerFramework = QBExport
|
||||
AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc)
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', tempFunc)
|
||||
elseif isStarted(ESXExport) then
|
||||
onPlayerFramework = ESXExport
|
||||
AddEventHandler('esx:playerLoaded', function()
|
||||
RegisterNetEvent('esx:playerLoaded', function()
|
||||
if waitForSharedLoad() then
|
||||
if isStarted(ESXExport) then Wait(1000) end
|
||||
tempFunc()
|
||||
end
|
||||
end
|
||||
)
|
||||
elseif isStarted(OXCoreExport) then
|
||||
onPlayerFramework = OXCoreExport
|
||||
AddEventHandler('ox:playerLoaded', tempFunc)
|
||||
RegisterNetEvent('ox:playerLoaded', tempFunc)
|
||||
elseif isStarted(RSGExport) then
|
||||
onPlayerFramework = RSGExport
|
||||
AddEventHandler('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
||||
RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
||||
end
|
||||
|
||||
if onPlayerFramework ~= "" then
|
||||
@@ -83,12 +82,15 @@ end
|
||||
--- end)
|
||||
--- ```
|
||||
function onPlayerUnload(func)
|
||||
AddEventHandler('QBCore:Client:OnPlayerUnload', function() func() end)
|
||||
AddEventHandler('ox:playerLogout', function() func() end)
|
||||
AddEventHandler('RSGCore:Client:OnPlayerUnload', function() func() end)
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3onPlayerUnload^7()")
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() func() end)
|
||||
|
||||
RegisterNetEvent('ox:playerLogout', function() func() end)
|
||||
|
||||
RegisterNetEvent('RSGCore:Client:OnPlayerUnload', function() func() end)
|
||||
|
||||
RegisterNetEvent('esx:onPlayerLogout', function() func() end)
|
||||
|
||||
--AddEventHandler('esx:playerLogout', function() func() end)
|
||||
-- ^ Only server side for now, need a way to send it to client if not already available
|
||||
end
|
||||
|
||||
-------------------------------------------------------------
|
||||
@@ -195,22 +197,23 @@ function waitForSharedLoad()
|
||||
local timeout = 100000 -- 10 seconds in milliseconds
|
||||
local startTime = GetGameTimer()
|
||||
local loaded = true
|
||||
--Wait(1000)
|
||||
local count = {}
|
||||
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
|
||||
if not messageShown then
|
||||
while ((not Jobs or not next(Jobs)) or
|
||||
(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
|
||||
debugPrint("^4Debug^7: ^2Waiting for Jobs to be loaded")
|
||||
print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded")
|
||||
end
|
||||
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
|
||||
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
|
||||
messageShown = true
|
||||
end
|
||||
messageShown = true
|
||||
--print((GetGameTimer() - startTime) < timeout)
|
||||
Wait(1000)
|
||||
if Jobs and Items and Vehicles then
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
--- ```
|
||||
function createCallback(callbackName, funct)
|
||||
if isServer() then
|
||||
debugPrint("^6Bridge^7: ^3Registering callback^7:", callbackName)
|
||||
if isStarted(OXLibExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^4"..OXLibExport.." ^3Callback^7:", callbackName)
|
||||
lib.callback.register(callbackName, funct)
|
||||
else
|
||||
local adaptedFunction = function(source, cb, ...)
|
||||
@@ -34,12 +34,14 @@ function createCallback(callbackName, funct)
|
||||
end
|
||||
|
||||
if isStarted(QBExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^4"..QBExport.." ^3Callback^7:", callbackName)
|
||||
Core = Core or exports[QBExport]:GetCoreObject()
|
||||
Core.Functions.CreateCallback(callbackName, adaptedFunction)
|
||||
elseif isStarted(ESXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^4"..ESXExport.." ^3Callback^7:", callbackName)
|
||||
ESX.RegisterServerCallback(callbackName, adaptedFunction)
|
||||
else
|
||||
print("^6Bridge^7: ^1ERROR^7: ^3Can't find any script to register callback with", callbackName)
|
||||
print("^1ERROR^7: ^1Can't find any supported framework to register callback with^7: "..callbackName)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -64,7 +66,7 @@ end
|
||||
--- ```
|
||||
function triggerCallback(callbackName, ...)
|
||||
local result = nil
|
||||
debugPrint("^6Bridge^7: ^3Triggering callback^7:", callbackName)
|
||||
debugPrint("^6Bridge^7: ^2Triggering ^3Callback^7:", callbackName)
|
||||
if isStarted(OXLibExport) then
|
||||
result = lib.callback.await(callbackName, false, ...)
|
||||
elseif isStarted(QBExport) then
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Items, Vehicles, Jobs, Gangs, Core = {}, nil, nil, nil, nil
|
||||
Items, Vehicles, Jobs, Gangs = nil, nil, nil, nil
|
||||
|
||||
-- Shared Exports Initialization
|
||||
Exports.PSInv = isStarted("lj-inventory") and "lj-inventory" or Exports.PSInv
|
||||
@@ -10,7 +10,7 @@ OXLibExport, QBXExport, QBExport, ESXExport, OXCoreExport =
|
||||
Exports.ESXExport or "",
|
||||
Exports.OXCoreExport or ""
|
||||
|
||||
OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv =
|
||||
OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, JPRInv =
|
||||
Exports.OXInv or "",
|
||||
Exports.QBInv or "",
|
||||
Exports.PSInv or "",
|
||||
@@ -18,7 +18,8 @@ OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv =
|
||||
Exports.CoreInv or "",
|
||||
Exports.CodeMInv or "",
|
||||
Exports.OrigenInv or "",
|
||||
Exports.TgiannInv or ""
|
||||
Exports.TgiannInv or "",
|
||||
Exports.JPRInv or ""
|
||||
|
||||
RSGExport, RSGInv = Exports.RSGExport or "", Exports.RSGInv or ""
|
||||
QBMenuExport = Exports.QBMenuExport or ""
|
||||
@@ -32,71 +33,72 @@ end
|
||||
|
||||
|
||||
if IsDuplicityVersion() then
|
||||
CreateThread(function()
|
||||
local cache = nil
|
||||
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
|
||||
local cache = nil
|
||||
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
|
||||
|
||||
-- Wait until jim_bridge is started and export is available
|
||||
while not cache and GetGameTimer() < timeout do
|
||||
if GetResourceState("jim_bridge"):find("start") then
|
||||
local success, result = pcall(function()
|
||||
return exports["jim_bridge"]:GetSharedData()
|
||||
end)
|
||||
if success and result then
|
||||
cache = result
|
||||
--print(json.encode(cache, {indent = true}))
|
||||
end
|
||||
-- Wait until jim_bridge is started and export is available
|
||||
while not cache and GetGameTimer() < timeout do
|
||||
if GetResourceState("jim_bridge"):find("start") then
|
||||
local success, result = pcall(function()
|
||||
return exports["jim_bridge"]:GetSharedData()
|
||||
end)
|
||||
if success and result then
|
||||
cache = result
|
||||
end
|
||||
Wait(100)
|
||||
end
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
if not cache then
|
||||
print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.")
|
||||
return
|
||||
end
|
||||
if not cache then
|
||||
print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.")
|
||||
return
|
||||
end
|
||||
|
||||
Items = cache.Items
|
||||
Vehicles = cache.Vehicles
|
||||
Jobs = cache.Jobs
|
||||
Gangs = cache.Gangs
|
||||
Items = cache.Items
|
||||
Vehicles = cache.Vehicles
|
||||
Jobs = cache.Jobs
|
||||
Gangs = cache.Gangs
|
||||
InventoryWeight = cache.InventoryWeight or InventoryWeight
|
||||
InventorySlots = cache.InventorySlots or 40
|
||||
|
||||
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
|
||||
end)
|
||||
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
|
||||
--print(countTable(Items), countTable(Vehicles), countTable(Jobs))
|
||||
else
|
||||
local hasCache = false
|
||||
-- 🔹 Client Side: Request from server
|
||||
_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
|
||||
-- Shared bridge cache - prevent duplicate listeners
|
||||
exports["jim_bridge"]:GetBridgeCache(function(cache)
|
||||
Items = cache.Items or {}
|
||||
Vehicles = cache.Vehicles or {}
|
||||
Jobs = cache.Jobs or {}
|
||||
Gangs = cache.Gangs or {}
|
||||
InventoryWeight = cache.InventoryWeight or InventoryWeight
|
||||
InventorySlots = cache.InventorySlots or 50
|
||||
|
||||
if isStarted(ESXExport) then
|
||||
for _, v in pairs(Vehicles) do
|
||||
Vehicles[v.model] = {
|
||||
model = v.model,
|
||||
hash = v.hash,
|
||||
price = v.price,
|
||||
name = v.name,
|
||||
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
|
||||
}
|
||||
CreateThread(function()
|
||||
if isStarted(ESXExport) then
|
||||
for _, v in pairs(Vehicles) do
|
||||
Vehicles[v.model] = {
|
||||
model = v.model,
|
||||
hash = v.hash,
|
||||
price = v.price,
|
||||
name = v.name,
|
||||
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
|
||||
}
|
||||
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
|
||||
|
||||
@@ -88,6 +88,7 @@ function craftingMenu(data)
|
||||
|
||||
-- Check if the player can carry the required items (server callback).
|
||||
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
|
||||
|
||||
-- Process each recipe to create menu entries.
|
||||
for i = 1, #Recipes do
|
||||
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
|
||||
@@ -106,10 +107,13 @@ function craftingMenu(data)
|
||||
local metaTable = {}
|
||||
-- Build ingredient details.
|
||||
for l, b in pairs(Recipes[i][tostring(k)]) do
|
||||
settext = settext..(settext ~= "" and br or "")..(Items[l] and Items[l].label or "error - "..l)..(b > 1 and " x"..b or "")
|
||||
local label = Items[l] and Items[l].label or "error - "..l
|
||||
local hasItem = checkStashItem(data.stashName, { [l] = b })
|
||||
local missingMark = not hasItem and " ❌" or " "
|
||||
settext = settext..(settext ~= "" and br or "").."[ x"..b.." ] - "..label..missingMark
|
||||
|
||||
metaTable[Items[l] and Items[l].label or "error - "..l] = b
|
||||
itemTable[l] = b
|
||||
--Wait(0)
|
||||
end
|
||||
|
||||
while not canCarryTable do Wait(0) end
|
||||
@@ -117,17 +121,17 @@ function craftingMenu(data)
|
||||
setheader = ((metadata and metadata.label) or (Items[tostring(k)] and Items[tostring(k)].label) or "error - "..tostring(k))
|
||||
..(Recipes[i]["amount"] > 1 and " x"..Recipes[i]["amount"] or "")
|
||||
|
||||
local statusEmoji = disable and " ❌" or not canCarryTable[k] and " 📦" or " ✔️"
|
||||
local statusEmoji = disable and " " or not canCarryTable[k] and " 📦" or " ✔️"
|
||||
local isNew = (Recipes[i]["hasCrafted"] ~= nil and craftedItems[k] == nil) and "✨ " or ""
|
||||
setheader = isNew .. setheader .. statusEmoji
|
||||
|
||||
Menu[#Menu + 1] = {
|
||||
arrow = not disable and canCarryTable[k],
|
||||
arrow = isOx() and (not disable and canCarryTable[k]),
|
||||
isMenuHeader = disable or not canCarryTable[k],
|
||||
icon = invImg((metadata and metadata.image) or tostring(k)),
|
||||
image = invImg((metadata and metadata.image) or tostring(k)),
|
||||
header = setheader,
|
||||
txt = (isStarted(QBMenuExport) or disable) and settext or nil,
|
||||
txt = settext or nil,
|
||||
metadata = metaTable,
|
||||
onSelect = (not disable and canCarryTable[k]) and function()
|
||||
local transdata = {
|
||||
@@ -396,7 +400,7 @@ function makeItem(data)
|
||||
currentToken = nil -- clear client cached token
|
||||
CreateThread(function()
|
||||
if data.craft["hasCrafted"] ~= nil then
|
||||
debugPrint("hasCrafted Found, marking '"..data.item.."' as crafted for player")
|
||||
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
|
||||
@@ -404,7 +408,7 @@ function makeItem(data)
|
||||
if data.craft["exp"] ~= nil then
|
||||
craftingLevel += data.craft["exp"].give
|
||||
jsonPrint(data.craft["exp"])
|
||||
debugPrint("exp found, giving exp for '"..data.item.."'")
|
||||
debugPrint("^6Bridge^7: ^3EXP ^2found^7, ^2giving exp for ^7'^4"..data.item.."^7'")
|
||||
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
|
||||
end
|
||||
end)
|
||||
@@ -453,9 +457,9 @@ end
|
||||
--- @usage
|
||||
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata, token)
|
||||
local src = source
|
||||
debugPrint(GetInvokingResource())
|
||||
-- debugPrint("^6Bridge^7: ^4"..getScript().."^7:^4Crafting^7:^4GetItem ^2invoked by^7: "..GetInvokingResource())
|
||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||
debugPrint("^1ERROR^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -56,6 +56,25 @@ function isServer()
|
||||
return IsDuplicityVersion()
|
||||
end
|
||||
|
||||
function checkExportExists(resource, export)
|
||||
if not resource or not export then return false end
|
||||
|
||||
local exists = false
|
||||
local ok, result = pcall(function()
|
||||
exists = type(exports[resource][export]) == "function"
|
||||
end)
|
||||
|
||||
if ok and exists then
|
||||
debugPrint(("^3Export Check^7: ^5exports^7['^5%s^7']:^5%s^7() ^2Exists^7"):format(resource, export))
|
||||
return true
|
||||
else
|
||||
debugPrint(("^3Export Check^7: ^5exports^7['^5%s^7']:^5%s^7() ^1Doesn^7'^1t Exist^7"):format(resource, export))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------
|
||||
-- Debugging and JSON Utilities
|
||||
-------------------------------------------------------------
|
||||
@@ -474,7 +493,7 @@ end
|
||||
--- end
|
||||
--- ```
|
||||
function ensureNetToVeh(vehNetID)
|
||||
debugPrint("^6Bridge^7: ^3ensureNetToVeh^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..vehNetID.."^7)")
|
||||
--debugPrint("^6Bridge^7: ^3ensureNetToVeh^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..vehNetID.."^7)")
|
||||
local timeout = 100
|
||||
while not NetworkDoesNetworkIdExist(vehNetID) and timeout > 0 do
|
||||
timeout -= 1
|
||||
@@ -502,7 +521,7 @@ end
|
||||
--- end
|
||||
--- ```
|
||||
function ensureNetToEnt(entNetID)
|
||||
debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)")
|
||||
--debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)")
|
||||
local timeout = 100
|
||||
while not NetworkDoesNetworkIdExist(entNetID) and timeout > 0 do
|
||||
timeout -= 1
|
||||
|
||||
@@ -50,7 +50,7 @@ function hasItem(items, amount, src)
|
||||
|
||||
local count = 0
|
||||
for _, itemData in pairs(grabInv) do
|
||||
jsonPrint(itemData)
|
||||
--jsonPrint(itemData)
|
||||
if itemData and itemData.name == item then
|
||||
count += (itemData.amount or itemData.count or 1)
|
||||
end
|
||||
@@ -138,6 +138,14 @@ function getPlayerInv(src)
|
||||
grabInv = exports[TgiannInv]:GetPlayerItems()
|
||||
end
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
foundInv = JPRInv
|
||||
if src then
|
||||
grabInv = Core.Functions.GetPlayer(src).PlayerData.items
|
||||
else
|
||||
grabInv = Core.Functions.GetPlayerData().items
|
||||
end
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
foundInv = QBInv
|
||||
if src then
|
||||
@@ -148,8 +156,11 @@ function getPlayerInv(src)
|
||||
|
||||
elseif isStarted(PSInv) then
|
||||
foundInv = PSInv
|
||||
if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
|
||||
else grabInv = Core.Functions.GetPlayerData().items end
|
||||
if src then
|
||||
grabInv = Core.Functions.GetPlayer(src).PlayerData.items
|
||||
else
|
||||
grabInv = Core.Functions.GetPlayerData().items
|
||||
end
|
||||
|
||||
elseif ESX and isStarted(ESXExport) then
|
||||
foundInv = ESX
|
||||
@@ -179,41 +190,6 @@ function isInventoryOpen()
|
||||
|
||||
return IsNuiFocused()
|
||||
|
||||
--if isStarted(OXInv) then
|
||||
-- return LocalPlayer.state.invBusy
|
||||
|
||||
--elseif isStarted(QSInv) then
|
||||
-- return exports[QSInv]:inInventory()
|
||||
|
||||
--elseif isStarted(OrigenInv) then
|
||||
-- return exports[OrigenInv]:IsInventoryOpen()
|
||||
|
||||
--elseif isStarted(CoreInv) then
|
||||
-- return exports[CoreInv]:isInventoryOpen()
|
||||
|
||||
--elseif isStarted(CodeMInv) then
|
||||
-- return false
|
||||
-- -- CodeM doesn't have a function to check if the inventory is open
|
||||
-- -- No idea what it uses, so it just skips the check
|
||||
|
||||
--elseif isStarted(TgiannInv) then
|
||||
-- return IsNuiFocused()
|
||||
|
||||
--elseif isStarted(QBInv) then
|
||||
-- return LocalPlayer.state.inv_busy
|
||||
|
||||
--elseif isStarted(PSInv) then
|
||||
-- return LocalPlayer.state.inv_busy
|
||||
|
||||
--elseif ESX and isStarted(ESXExport) then
|
||||
-- return false
|
||||
|
||||
--elseif isStarted(RSGInv) then
|
||||
-- return LocalPlayer.state.inv_busy
|
||||
|
||||
--end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------
|
||||
@@ -248,12 +224,14 @@ function invImg(item)
|
||||
elseif isStarted(OrigenInv) then
|
||||
imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "")
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
imgLink = "nui://"..JPRInv.."/html/images/"..(Items[item].image or "")
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "")
|
||||
|
||||
elseif isStarted(PSInv) then
|
||||
imgLink = "nui://"..PSInv.."/html/images/"..(Items[item].image or "")
|
||||
|
||||
elseif isStarted(TgiannInv) then
|
||||
imgLink = "nui://inventory_images/images/"..(Items[item].image or "")
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
or other type (e.g., coyote). It uses predefined model hashes stored in the AnimalPeds table.
|
||||
|
||||
Global Flags:
|
||||
- isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal: Booleans to track the player's
|
||||
- isCat, isPedDog, isBigDog, isSmallDog, isCoyote, isAnimal: Booleans to track the player's
|
||||
current animal classification.
|
||||
|
||||
When running client-side (not on the server), the module checks the player's Ped after they load.
|
||||
@@ -26,13 +26,13 @@
|
||||
]]
|
||||
|
||||
-- Global animal classification flags.
|
||||
isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
|
||||
isPedCat, isPedDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
|
||||
|
||||
if not isServer() then
|
||||
onPlayerLoaded(function()
|
||||
Wait(2000)
|
||||
Wait(1000)
|
||||
-- Reset classification flags
|
||||
isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
|
||||
isPedCat, isPedDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
|
||||
-- Check if the player's Ped is an animal.
|
||||
isPedAnimal()
|
||||
if isAnimal then
|
||||
@@ -41,18 +41,18 @@ if not isServer() then
|
||||
|
||||
-- Determine if the Ped is a cat:
|
||||
-- Also treat 'ft-raccoon' as a cat unless it is 'ft-sphynx'
|
||||
isCat = (isCat(ped) or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
|
||||
isPedCat = (isCat(ped) == true or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
|
||||
|
||||
-- Determine if the Ped is a dog and whether it's big:
|
||||
isDog, isBigDog = isDog(ped)
|
||||
isPedDog, isBigDog = isDog(ped)
|
||||
isSmallDog = not isBigDog
|
||||
if isDog and pedModel == `a_c_coyote` then isDog = false end
|
||||
if isPedDog and pedModel == `a_c_coyote` then isPedDog = false end
|
||||
|
||||
-- Determine if the Ped is a coyote (special case):
|
||||
isCoyote = (pedModel == `ft-sphynx` or pedModel == `a_c_coyote`)
|
||||
|
||||
-- Special override: if model is 'ft-capmonkey2', treat as a dog.
|
||||
if pedModel == `ft-capmonkey2` then isDog = true end
|
||||
if pedModel == `ft-capmonkey2` then isPedDog = true end
|
||||
end
|
||||
end, true)
|
||||
|
||||
@@ -79,7 +79,7 @@ if not isServer() then
|
||||
for animalModelHash, _ in pairs(animalCategory) do
|
||||
if PedModel == animalModelHash then
|
||||
isAnimal = true
|
||||
debugPrint("^6Bridge^7: ^2Ped is Animal")
|
||||
debugPrint("^4isAnimal^7: ^2Ped is ^4Animal")
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -109,6 +109,7 @@ if not isServer() then
|
||||
local PedModel = GetEntityModel(ped or PlayerPedId())
|
||||
for modelHash, _ in pairs(AnimalPeds.CatPeds) do
|
||||
if PedModel == modelHash then
|
||||
debugPrint("^4isAnimal^7: ^2Ped is ^4Cat")
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -126,8 +127,8 @@ if not isServer() then
|
||||
---
|
||||
---@usage
|
||||
--- ```lua
|
||||
--- local isDog, isBigDog = isDog()
|
||||
--- if isDog then
|
||||
--- local isPedDog, isBigDog = isDog()
|
||||
--- if isPedDog then
|
||||
--- if isBigDog then
|
||||
--- print("Player is a big dog!")
|
||||
--- else
|
||||
@@ -151,11 +152,13 @@ if not isServer() then
|
||||
local PedModel = GetEntityModel(ped or PlayerPedId())
|
||||
for modelHash, _ in pairs(AnimalPeds.BigDogs) do
|
||||
if PedModel == modelHash then
|
||||
debugPrint("^4isAnimal^7: ^2Ped is ^4Dog")
|
||||
return true, true
|
||||
end
|
||||
end
|
||||
for modelHash, _ in pairs(AnimalPeds.SmallDogs) do
|
||||
if PedModel == modelHash then
|
||||
debugPrint("^4isAnimal^7: ^2Ped is ^4Small Dog")
|
||||
return true, false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,25 +30,34 @@ validTokens = {}
|
||||
--- end)
|
||||
--- ```
|
||||
function createUseableItem(item, funct)
|
||||
if isStarted(ESXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7es_extended^7", item)
|
||||
while not ESX do Wait(0) end
|
||||
ESX.RegisterUsableItem(item, funct)
|
||||
if doesItemExist(item) then
|
||||
local itemResource = ""
|
||||
if isStarted(ESXExport) then
|
||||
itemResource = ESXExport
|
||||
while not ESX do Wait(0) end
|
||||
ESX.RegisterUsableItem(item, funct)
|
||||
|
||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qb-core^7", item)
|
||||
Core.Functions.CreateUseableItem(item, funct)
|
||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||
itemResource = QBExport
|
||||
Core.Functions.CreateUseableItem(item, funct)
|
||||
|
||||
elseif isStarted(QBXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core^7", item)
|
||||
exports[QBXExport]:CreateUseableItem(item, funct)
|
||||
elseif isStarted(QBXExport) then
|
||||
itemResource = QBXExport
|
||||
exports[QBXExport]:CreateUseableItem(item, funct)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^1rsg-core^7", item)
|
||||
Core.Functions.CreateUseableItem(item, funct)
|
||||
elseif isStarted(RSGExport) then
|
||||
itemResource = RSGExport
|
||||
Core.Functions.CreateUseableItem(item, funct)
|
||||
|
||||
end
|
||||
|
||||
if itemResource ~= "" then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..itemResource.."^7:", item)
|
||||
else
|
||||
debugPrint("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
||||
end
|
||||
else
|
||||
print("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
||||
print("^1ERROR^7: ^1Tried to make item usable but it didn't exist^7: "..item)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -186,6 +195,20 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
invName = TgiannInv
|
||||
exports[TgiannInv]:RemoveItem(src, item, remamount)
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
invName = JPRInv
|
||||
while remamount > 0 do
|
||||
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then
|
||||
remamount -= 1
|
||||
else
|
||||
print("^1Error removing "..item.." Amount left: "..remamount)
|
||||
break
|
||||
end
|
||||
end
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
|
||||
end
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
invName = QBInv
|
||||
while remamount > 0 do
|
||||
@@ -196,7 +219,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
break
|
||||
end
|
||||
end
|
||||
if Config.Crafting.showItemBox then
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
|
||||
end
|
||||
|
||||
@@ -210,7 +233,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
break
|
||||
end
|
||||
end
|
||||
if Config.Crafting.showItemBox then
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
|
||||
end
|
||||
|
||||
@@ -223,7 +246,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
break
|
||||
end
|
||||
end
|
||||
if Config.Crafting.showItemBox then
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent("rsg-inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
|
||||
end
|
||||
|
||||
@@ -270,6 +293,13 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
elseif isStarted(TgiannInv) then invName = TgiannInv
|
||||
exports[TgiannInv]:AddItem(src, item, amountToAdd, slot, info)
|
||||
|
||||
elseif isStarted(JPRInv) then invName = JPRInv
|
||||
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd)
|
||||
end
|
||||
end
|
||||
|
||||
elseif isStarted(QBInv) then invName = QBInv
|
||||
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
|
||||
TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "add", amountToAdd)
|
||||
@@ -277,7 +307,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
||||
|
||||
elseif isStarted(PSInv) then invName = PSInv
|
||||
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
|
||||
if Config.Crafting.showItemBox then
|
||||
if Config.Crafting ~= nil and Config.Crafting.showItemBox then
|
||||
TriggerClientEvent("ps-inventory:client:ItemBox", src, Items[item], "add", amountToAdd)
|
||||
end
|
||||
end
|
||||
@@ -379,7 +409,7 @@ end
|
||||
function getDurability(item)
|
||||
local lowestSlot = 100
|
||||
local durability = nil
|
||||
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) then
|
||||
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
|
||||
local itemcheck = Core.Functions.GetPlayerData().items
|
||||
for k, v in pairs(itemcheck) do
|
||||
if v.name == item then
|
||||
@@ -486,17 +516,19 @@ end
|
||||
--- ```lua
|
||||
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
|
||||
--- ```
|
||||
RegisterNetEvent(getScript()..":server:setMetaData", function(data)
|
||||
local src = source
|
||||
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) then
|
||||
debugPrint(src, data.item, 1, data.slot)
|
||||
RegisterNetEvent(getScript()..":server:setMetaData", function(data, src)
|
||||
local src = src or source
|
||||
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
|
||||
--debugPrint(src, data.item, 1, data.slot)
|
||||
local Player = Core.Functions.GetPlayer(src)
|
||||
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)
|
||||
|
||||
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
|
||||
exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
|
||||
@@ -626,6 +658,22 @@ function canCarry(itemTable, src)
|
||||
resultTable[k] = exports[TgiannInv]:CanCarryItem(source, k, v)
|
||||
end
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
local items = getPlayerInv(src)
|
||||
local totalWeight = 0
|
||||
if not items then return false end
|
||||
for _, item in pairs(items) do
|
||||
totalWeight += (item.weight * item.amount)
|
||||
end
|
||||
for k, v in pairs(itemTable) do
|
||||
local itemInfo = Items[k]
|
||||
if not itemInfo then
|
||||
resultTable[k] = true
|
||||
else
|
||||
resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight
|
||||
end
|
||||
end
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
if QBInvNew then
|
||||
for k, v in pairs(itemTable) do
|
||||
@@ -671,8 +719,6 @@ end
|
||||
-------------------------------------------------------------
|
||||
-- Server Callback Registration
|
||||
-------------------------------------------------------------
|
||||
AuthEvent = nil
|
||||
currentToken = nil
|
||||
if isServer() then
|
||||
createCallback(getScript()..":server:canCarry", function(source, itemTable)
|
||||
local result = canCarry(itemTable, source)
|
||||
@@ -698,103 +744,138 @@ if isServer() then
|
||||
end
|
||||
return maxCanCarry
|
||||
end)
|
||||
end
|
||||
|
||||
local AuthEvent = getScript()..":"..keyGen()..keyGen()..keyGen()..keyGen()..":"..keyGen()..keyGen()..keyGen()..keyGen()
|
||||
validTokens = validTokens or {}
|
||||
|
||||
createCallback(AuthEvent, function(source)
|
||||
local src = source
|
||||
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
||||
--debugPrint(GetInvokingResource())
|
||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||
return ""
|
||||
function getMaxInvWeight()
|
||||
local weight = 0
|
||||
if isStarted(QBInv) then
|
||||
if checkExportExists(QBInv, "GetMaxWeight") then
|
||||
return exports[QBInv]:GetMaxWeight()
|
||||
else
|
||||
return InventoryWeight
|
||||
end
|
||||
end
|
||||
if isStarted(PSInv) then
|
||||
if checkExportExists(PSInv, "GetMaxWeight") then
|
||||
return exports[PSInv]:GetMaxWeight()
|
||||
else
|
||||
return InventoryWeight
|
||||
end
|
||||
end
|
||||
if isStarted(RSGInv) then
|
||||
if checkExportExists(RSGInv, "GetMaxWeight") then
|
||||
return exports[RSGInv]:GetMaxWeight()
|
||||
else
|
||||
return InventoryWeight
|
||||
end
|
||||
end
|
||||
if isStarted(JPRInv) then
|
||||
if checkExportExists(JPRInv, "GetMaxWeight") then
|
||||
return exports[JPRInv]:GetMaxWeight()
|
||||
else
|
||||
return InventoryWeight
|
||||
end
|
||||
debugPrint("^1Auth^7:^2 Player Source^7: "..src.." ^2requested new token^7:", token)
|
||||
validTokens[src] = token
|
||||
timeOutAuth(validTokens[src], src) -- Give script 10 seconds, then clear token
|
||||
return token
|
||||
end)
|
||||
|
||||
function timeOutAuth(token, src)
|
||||
local token = token
|
||||
SetTimeout(10000, function()
|
||||
if token == validTokens[src] then
|
||||
print("^1--------------------------------------------^7")
|
||||
print("^7Clearing token for player ^1"..src.."^7", token)
|
||||
print("^7This shouldn't happen unless a token has been called by a player or script and it hasn't been used")
|
||||
print("^1--------------------------------------------^7")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
RegisterNetEvent(getScript()..":clearAuthToken", function()
|
||||
local src = source
|
||||
debugPrint("^1Auth^7: ^2Manually removing token for Player Source^7:", src, validTokens[src])
|
||||
validTokens[src] = nil
|
||||
end)
|
||||
if isStarted(OXInv) then
|
||||
return exports[OXInv]:GetPlayerMaxWeight()
|
||||
end
|
||||
|
||||
receivedEvent = {}
|
||||
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
||||
local src = source
|
||||
--debugPrint(GetInvokingResource())
|
||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
||||
return ""
|
||||
if isStarted(QSInv) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
if isStarted(CoreInv) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
if isStarted(CodeMInv) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
if isStarted(OrigenInv) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
if isStarted(TgiannInv) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
-- For ESX default inventory (es_extended)
|
||||
if ESX and isStarted(ESXExport) then
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
return InventoryWeight
|
||||
end
|
||||
|
||||
function getCurrentInvWeight()
|
||||
local weight = 0
|
||||
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
|
||||
local itemcheck = Core.Functions.GetPlayerData().items
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
|
||||
if not receivedEvent[src] then
|
||||
receivedEvent[src] = true
|
||||
return AuthEvent
|
||||
else
|
||||
print("^1Auth^7: ^1Player ^7"..src.." ^1tried to request auth token more than once^7")
|
||||
return ""
|
||||
end
|
||||
|
||||
if isStarted(OXInv) then
|
||||
weight = exports[OXInv]:GetPlayerWeight()
|
||||
end
|
||||
|
||||
if isStarted(QSInv) then
|
||||
local itemcheck = exports[QSInv]:getUserInventory()
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
RegisterNetEvent(getScript()..":clearAuthEventRequest", function()
|
||||
local src = source
|
||||
debugPrint("^1Auth^7: ^2Manually clearing Auth Event for Player Source^7:", src, AuthEvent)
|
||||
receivedEvent[src] = nil
|
||||
end)
|
||||
if isStarted(CoreInv) then
|
||||
local itemcheck = exports[CoreInv]:getInventory()
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Multiuse function to check if the generated client token is valid
|
||||
function checkToken(src, token, genType, name)
|
||||
if token == nil then
|
||||
debugPrint("^1Auth^7: ^1No token recieved^7")
|
||||
if genType == "stash" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1without an auth token^7")
|
||||
elseif genType == "item" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1without an auth token^7")
|
||||
end
|
||||
return false
|
||||
else
|
||||
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
|
||||
if token ~= validTokens[src] then
|
||||
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", token, validTokens[src])
|
||||
if genType == "stash" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1with an incorrect auth token^7")
|
||||
elseif genType == "item" then
|
||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1with an incorrect auth token^7")
|
||||
end
|
||||
return false
|
||||
else
|
||||
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", token, validTokens[src])
|
||||
validTokens[src] = nil
|
||||
return true
|
||||
if isStarted(CodeMInv) then
|
||||
local itemcheck = exports[CodeMInv]:GetClientPlayerInventory()
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
if isStarted(OrigenInv) then
|
||||
local itemcheck = exports[OrigenInv]:getPlayerInventory()
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
if isStarted(TgiannInv) then
|
||||
local itemcheck = exports[TgiannInv]:GetPlayerItems()
|
||||
for _, v in pairs(itemcheck) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- For ESX default inventory (es_extended)
|
||||
if ESX and isStarted(ESXExport) then
|
||||
local xPlayer = ESX.GetPlayerData() or {}
|
||||
if xPlayer.inventory then
|
||||
for _, v in pairs(xPlayer.inventory) do
|
||||
weight += ((v.weight * v.amount) or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
onPlayerLoaded(function()
|
||||
debugPrint("^1Auth^7: ^2Requesting ^3Auth Event^7")
|
||||
AuthEvent = triggerCallback(getScript()..":callback:GetAuthEvent")
|
||||
end, true)
|
||||
|
||||
onPlayerUnload(function()
|
||||
debugPrint("^1Auth^7: ^2Clearing Auth Event^7")
|
||||
TriggerServerEvent(getScript()..":clearAuthEventRequest")
|
||||
end, true)
|
||||
return weight
|
||||
end
|
||||
|
||||
function hasFreeInventorySlots(slots, src)
|
||||
local inv = getPlayerInv(src)
|
||||
if (countTable(inv) + slots) <= InventorySlots then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------
|
||||
|
||||
@@ -231,3 +231,19 @@ function useDoor(data)
|
||||
DoScreenFadeIn(1000)
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
|
||||
function openBossMenu(isGang, group)
|
||||
if isStarted("qb-management") then
|
||||
if isGang then
|
||||
TriggerEvent("qb-gangmenu:client:OpenMenu")
|
||||
else
|
||||
TriggerEvent("qb-bossmenu:client:OpenMenu")
|
||||
end
|
||||
elseif isStarted("qbx_management") then
|
||||
exports["qbx_management"]:OpenBossMenu(isGang and "gang" or "job")
|
||||
|
||||
elseif isStarted("esx_society") then
|
||||
TriggerEvent('esx_society:openBossMenu', group, function() end, { wash = false })
|
||||
end
|
||||
end
|
||||
@@ -187,7 +187,7 @@ end
|
||||
--- ```
|
||||
function playAnim(animDict, animName, duration, flag, ped, speed)
|
||||
loadAnimDict(animDict)
|
||||
debugPrint("Attempting to make player play anim", animDict, animName)
|
||||
debugPrint("^6Bridge^7: ^3playAnim^7() ^2Triggered^7: ", animDict, animName)
|
||||
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
|
||||
|
||||
@@ -204,7 +204,7 @@ end
|
||||
--- stopAnim('amb@world_human_hang_out_street@male_c@base', 'base', PlayerPedId())
|
||||
--- ```
|
||||
function stopAnim(animDict, animName, ped)
|
||||
debugPrint("Stopping anim for "..(ped or PlayerPedId()))
|
||||
debugPrint("^6Bridge^7: ^3stopAnim^7() ^2Triggered^7: ", animDict, animName)
|
||||
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
|
||||
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
|
||||
unloadAnimDict(animDict)
|
||||
|
||||
@@ -54,14 +54,14 @@ function makeDistVehicle(data, radius, onEnter, onExit)
|
||||
onEnter = function()
|
||||
vehicle = makeVeh(data.model, data.coords)
|
||||
if onEnter then
|
||||
debugPrint("makeDistVehicle onEnter running")
|
||||
debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onEnter^7() ^2running^7")
|
||||
onEnter(vehicle)
|
||||
end
|
||||
end,
|
||||
onExit = function()
|
||||
deleteVehicle(vehicle)
|
||||
if onExit then
|
||||
debugPrint("makeDistVehicle onExit running")
|
||||
debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onExit^7() ^2running^7")
|
||||
onExit(vehicle)
|
||||
end
|
||||
end,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
local inProgress = false
|
||||
local storedPID = nil
|
||||
|
||||
--- Displays a progress bar using the configured progress bar system.
|
||||
---
|
||||
@@ -43,11 +44,12 @@ function progressBar(data)
|
||||
local result = nil
|
||||
if data.cam then startTempCam(data.cam) end
|
||||
if Config.System.ProgressBar == "ox" then
|
||||
if exports[OXLibExport]:progressBar({
|
||||
local options = {
|
||||
duration = debugMode and 1000 or data.time,
|
||||
label = data.label,
|
||||
position = data.position or "bottom",
|
||||
useWhileDead = data.dead or false,
|
||||
canCancel = data.cancel and data.cancel or true,
|
||||
canCancel = data.cancel or true,
|
||||
anim = {
|
||||
dict = data.dict,
|
||||
clip = data.anim,
|
||||
@@ -55,12 +57,41 @@ function progressBar(data)
|
||||
scenario = data.task
|
||||
},
|
||||
disable = {
|
||||
combat = true
|
||||
combat = data.combat or true,
|
||||
move = data.disableMovement or false,
|
||||
car = data.disableMovement or false,
|
||||
mouse = data.mouse or false
|
||||
},
|
||||
}) then
|
||||
result = true
|
||||
else
|
||||
result = false
|
||||
}
|
||||
if data.prop and data.prop.model then
|
||||
options.prop = {
|
||||
model = data.prop.model,
|
||||
pos = data.prop.pos or vec3(0, 0, 0),
|
||||
rot = data.prop.rot or vec3(0, 0, 0),
|
||||
bone = data.prop.bone or 0
|
||||
}
|
||||
end
|
||||
if data.propTwo and data.propTwo.model then
|
||||
options.propTwo = {
|
||||
model = data.propTwo.model,
|
||||
pos = data.propTwo.pos or vec3(0, 0, 0),
|
||||
rot = data.propTwo.rot or vec3(0, 0, 0),
|
||||
bone = data.propTwo.bone or 0
|
||||
}
|
||||
end
|
||||
if data.progressType == "circle" then
|
||||
if exports[OXLibExport]:progressCircle(options) then
|
||||
result = true
|
||||
else
|
||||
result = false
|
||||
end
|
||||
end
|
||||
if not data.progressType or data.progressType == "bar" then
|
||||
if exports[OXLibExport]:progressBar(options) then
|
||||
result = true
|
||||
else
|
||||
result = false
|
||||
end
|
||||
end
|
||||
|
||||
elseif Config.System.ProgressBar == "qb" then
|
||||
@@ -69,8 +100,19 @@ function progressBar(data)
|
||||
debugMode and 1000 or data.time,
|
||||
data.dead or false,
|
||||
data.cancel or true,
|
||||
{ disableMovement = true, disableCarMovement = true, disableMouse = false, disableCombat = true },
|
||||
{ animDict = data.dict, anim = data.anim, flags = data.flag or 32, task = data.task }, {}, {},
|
||||
{
|
||||
disableMovement = data.disableMovement or false,
|
||||
disableCarMovement = data.disableMovement or false,
|
||||
disableMouse = data.disableMouse or false,
|
||||
disableCombat = data.disableCombat or true,
|
||||
},
|
||||
{
|
||||
animDict = data.dict,
|
||||
anim = data.anim,
|
||||
flags = data.flag or 32,
|
||||
task = data.task
|
||||
},
|
||||
{}, {},
|
||||
function()
|
||||
result = true
|
||||
end, function()
|
||||
@@ -95,14 +137,15 @@ function progressBar(data)
|
||||
|
||||
elseif Config.System.ProgressBar == "red" then
|
||||
-- Currently only uses jim-redui if you choose this option
|
||||
if exports["jim-redui"]:progressBar({
|
||||
if exports["jim_bridge"]:redProgressBar({
|
||||
label = data.label,
|
||||
time = debugMode and 1000 or data.time,
|
||||
dict = data.dict,
|
||||
anim = data.anim,
|
||||
flag = data.flag or 32,
|
||||
task = data.task,
|
||||
cancel = true,
|
||||
disableMovement = data.disableMovement or false,
|
||||
cancel = data.cancel or true,
|
||||
}) then
|
||||
result = true
|
||||
else
|
||||
@@ -110,76 +153,26 @@ function progressBar(data)
|
||||
end
|
||||
|
||||
elseif Config.System.ProgressBar == "gta" then
|
||||
loadTextureDict("timerbars")
|
||||
if inProgress then return false end
|
||||
inProgress = true
|
||||
local wait = debugMode and 1000 or data.time
|
||||
local endTime = GetGameTimer() + wait
|
||||
local ped = PlayerPedId()
|
||||
|
||||
-- Setup Animation/Task if specified
|
||||
if data.dict then
|
||||
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
||||
elseif data.task then
|
||||
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
||||
if exports["jim_bridge"]:gtaProgressBar({
|
||||
label = data.label,
|
||||
time = debugMode and 1000 or data.time,
|
||||
dict = data.dict,
|
||||
anim = data.anim,
|
||||
flag = data.flag or 32,
|
||||
task = data.task,
|
||||
disableMovement = data.disableMovement or false,
|
||||
cancel = data.cancel or true,
|
||||
}) then
|
||||
result = true
|
||||
else
|
||||
result = false
|
||||
end
|
||||
|
||||
-- Progress bar rendering loop
|
||||
CreateThread(function()
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(0)
|
||||
local elapsed = GetGameTimer()
|
||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||
|
||||
-- Convert to segmented progress (assuming 5 segments here)
|
||||
local segments = 5 -- Number of segments in the bar
|
||||
local segmentProgress = {}
|
||||
local progressPerSegment = 100 / segments
|
||||
|
||||
for i = 1, segments do
|
||||
local segmentStart = (i - 1) * progressPerSegment
|
||||
local segmentEnd = i * progressPerSegment
|
||||
if percentage >= segmentEnd then
|
||||
segmentProgress[i] = 100
|
||||
elseif percentage <= segmentStart then
|
||||
segmentProgress[i] = 0
|
||||
else
|
||||
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
||||
end
|
||||
end
|
||||
|
||||
percentage = percentage >= 100 and 100 or percentage
|
||||
-- Draw your segmented progress bar
|
||||
ShowGTAProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
||||
|
||||
-- Controls to disable during progress
|
||||
DisablePlayerFiring(ped, true)
|
||||
DisableControlAction(0, 25, true) -- Disable aim
|
||||
DisableControlAction(0, 21, true) -- Disable sprint
|
||||
DisableControlAction(0, 30, true) -- Disable move left/right
|
||||
DisableControlAction(0, 31, true) -- Disable move forward/back
|
||||
DisableControlAction(0, 36, true) -- Disable stealth
|
||||
|
||||
if data.cancel and (IsControlJustReleased(0, 202) or IsControlJustReleased(0, 177) or IsControlJustReleased(0, 73)) then
|
||||
inProgress = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Wait for completion or cancel
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
-- Cleanup animations/tasks
|
||||
if data.dict then stopAnim(data.dict, data.anim, ped) end
|
||||
ClearPedTasks(ped)
|
||||
|
||||
result = inProgress
|
||||
inProgress = false
|
||||
end
|
||||
|
||||
while result == nil do Wait(10) end
|
||||
while result == nil do
|
||||
Wait(10)
|
||||
end
|
||||
|
||||
-- Cleanup
|
||||
FreezeEntityPosition(ped, false)
|
||||
@@ -203,51 +196,6 @@ function progressBar(data)
|
||||
return result
|
||||
end
|
||||
|
||||
function ShowGTAProgressBar(currentProg, title, level)
|
||||
local loc = vec2(0.37, 0.90)
|
||||
local size = vec2(0.3, 0.03)
|
||||
|
||||
-- Draw background box
|
||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.028, loc.y-0.01, 0.15, 0.07, 0.0, 255, 255, 255, 255)
|
||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.170, loc.y-0.01, 0.15, 0.07, 180.0, 255, 255, 255, 255)
|
||||
|
||||
SetTextFont(0)
|
||||
SetTextProportional(1)
|
||||
SetTextScale(0.35, 0.35)
|
||||
SetTextColour(255, 255, 255, 255)
|
||||
SetTextDropshadow(0, 0, 0, 0, 255)
|
||||
SetTextDropShadow()
|
||||
SetTextOutline()
|
||||
SetTextEntry("STRING")
|
||||
AddTextComponentString(title)
|
||||
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034) -- Adjust text position
|
||||
|
||||
SetTextFont(0)
|
||||
SetTextProportional(1)
|
||||
SetTextScale(0.35, 0.25)
|
||||
SetTextColour(255, 255, 255, 255)
|
||||
SetTextEntry("STRING")
|
||||
AddTextComponentString(level)
|
||||
DrawText(loc.x - size.x / 4 + 0.246, loc.y - 0.030) -- Right-aligned additional text
|
||||
|
||||
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
||||
|
||||
for i = 1, #currentProg do
|
||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
||||
local fillPercentage = currentProg[i]
|
||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
||||
|
||||
-- Semi-transparent background for each segment
|
||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
||||
|
||||
-- Filling progress for each segment
|
||||
if progressBarWidth > 0 then
|
||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 93, 182, 229, 255) -- Blue progress
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Stops the current progress bar.
|
||||
---
|
||||
--- This function cancels the progress bar based on the configured progress bar system, handling any necessary cleanup.
|
||||
@@ -256,15 +204,14 @@ function stopProgressBar()
|
||||
exports[OXLibExport]:cancelProgress()
|
||||
elseif Config.System.ProgressBar == "qb" then
|
||||
TriggerEvent("progressbar:client:cancel")
|
||||
elseif Config.System.ProgressBar == "gta" then
|
||||
inProgress = false
|
||||
elseif Config.System.ProgressBar == "gta" or Config.System.ProgressBar == "red" then
|
||||
exports["jim_bridge"]:stopProgressBar()
|
||||
end
|
||||
end
|
||||
|
||||
-- System to handle sending/sharing progress bars between players --
|
||||
-- For example, healing someone --
|
||||
|
||||
local storedPID = nil
|
||||
|
||||
--- Server event handler for starting a shared progress bar.
|
||||
--- This event is triggered when a player wants to start a progress bar on another player.
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
|
||||
--- ```
|
||||
function triggerNotify(title, message, type, src)
|
||||
if not Config.System or not Config.System.Notify then debugPrint("Notify triggered but not set up") return end
|
||||
if Config.System.Notify == "okok" then
|
||||
if not src then
|
||||
TriggerEvent('okokNotify:Alert', title, message, 6000, type)
|
||||
|
||||
@@ -73,6 +73,11 @@ function sendPhoneMail(data)
|
||||
TriggerServerEvent('qb-phone:server:sendNewMail', mailData)
|
||||
end,
|
||||
},
|
||||
{ name = "npwd_qbx_mail",
|
||||
send = function(mailData)
|
||||
TriggerServerEvent('qb-phone:server:sendNewMail', mailData)
|
||||
end,
|
||||
},
|
||||
{ name = "jpr-phonesystem",
|
||||
send = function(mailData)
|
||||
TriggerServerEvent(getScript()..":jpr:SendMail", mailData)
|
||||
@@ -85,23 +90,125 @@ function sendPhoneMail(data)
|
||||
},
|
||||
}
|
||||
|
||||
local activePhone = nil
|
||||
-- Check each phone system in order and use the first active one.
|
||||
for _, phone in ipairs(phoneSystems) do
|
||||
if isStarted(phone.name) then
|
||||
activePhone = phone.name
|
||||
debugPrint("^6Bridge^7[^3"..phone.name.."^7]: ^2Sending mail to player")
|
||||
phone.send(data)
|
||||
break
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if activePhone then
|
||||
debugPrint("^6Bridge^7[^3"..activePhone.."^7]: ^2Sending mail to player")
|
||||
else
|
||||
print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7- ^2No supported phone found")
|
||||
end
|
||||
print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7- ^2No supported phone found")
|
||||
return false
|
||||
end
|
||||
|
||||
function sendPhoneInvoice(data)
|
||||
if not data.src then return end
|
||||
-- Define each supported phone system and its corresponding mail-sending function.
|
||||
local phoneSystems = {
|
||||
{
|
||||
name = "qb-phone",
|
||||
send = function(mailData)
|
||||
-- Defensive check for required fields
|
||||
local required = { "billedCitizenid", "amount", "job", "name", "billerCitizenid", "src" }
|
||||
for _, field in ipairs(required) do
|
||||
if mailData[field] == nil then
|
||||
print("^1[jim-payments] ERROR:^0 Missing field '" .. field .. "' in mailData")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Safe insert with all parameters present
|
||||
MySQL.Async.insert(
|
||||
'INSERT INTO phone_invoices (citizenid, amount, society, sender, sendercitizenid) VALUES (?, ?, ?, ?, ?)',
|
||||
{
|
||||
mailData.billedCitizenid,
|
||||
mailData.amount,
|
||||
mailData.job,
|
||||
mailData.name,
|
||||
mailData.billerCitizenid
|
||||
},
|
||||
function(id)
|
||||
if id then
|
||||
TriggerClientEvent('qb-phone:client:AcceptorDenyInvoice', mailData.src, id, mailData.name, mailData.job, mailData.billerCitizenid, mailData.amount, GetInvokingResource())
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
TriggerClientEvent('qb-phone:RefreshPhone', mailData.src)
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "codem-phone",
|
||||
send = function(mailData)
|
||||
-- Defensive check for required fields
|
||||
local required = { "billedCitizenid", "amount", "job", "name", "billerCitizenid", "src" }
|
||||
for _, field in ipairs(required) do
|
||||
if mailData[field] == nil then
|
||||
print("^1[jim-payments] ERROR:^0 Missing field '" .. field .. "' in mailData")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Safe insert with all parameters present
|
||||
MySQL.Async.insert(
|
||||
'INSERT INTO phone_invoices (citizenid, amount, society, sender, sendercitizenid) VALUES (?, ?, ?, ?, ?)',
|
||||
{
|
||||
mailData.billedCitizenid,
|
||||
mailData.amount,
|
||||
mailData.job,
|
||||
mailData.name,
|
||||
mailData.billerCitizenid
|
||||
},
|
||||
function(id)
|
||||
-- if id then
|
||||
-- TriggerClientEvent('qb-phone:client:AcceptorDenyInvoice', mailData.src, id, mailData.name, mailData.job, mailData.billerCitizenid, mailData.amount, GetInvokingResource())
|
||||
-- end
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "gks-phone",
|
||||
send = function(mailData)
|
||||
-- Defensive check for required fields
|
||||
local required = { "billedCitizenid", "amount", "job", "name", "billerCitizenid", "label" }
|
||||
for _, field in ipairs(required) do
|
||||
if mailData[field] == nil then
|
||||
print("^1[jim-payments] ERROR:^0 Missing field '" .. field .. "' in mailData")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
MySQL.Async.execute(
|
||||
'INSERT INTO gksphone_invoices (citizenid, amount, society, sender, sendercitizenid, label) VALUES (@citizenid, @amount, @society, @sender, @sendercitizenid, @label)',
|
||||
{
|
||||
['@citizenid'] = mailData.billedCitizenid,
|
||||
['@amount'] = mailData.amount,
|
||||
['@society'] = mailData.job,
|
||||
['@sender'] = mailData.name,
|
||||
['@sendercitizenid'] = mailData.billerCitizenid,
|
||||
['@label'] = mailData.label
|
||||
}
|
||||
)
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- Check each phone system in order and use the first active one.
|
||||
for _, phone in ipairs(phoneSystems) do
|
||||
if isStarted(phone.name) then
|
||||
debugPrint("^6Bridge^7[^3"..phone.name.."^7]: ^2Sending mail to player^7", data.src)
|
||||
phone.send(data)
|
||||
return true
|
||||
end
|
||||
end
|
||||
print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7- ^2No supported phone found")
|
||||
return false
|
||||
end
|
||||
-------------------------------------------------------------
|
||||
-- Phone System Event Handlers
|
||||
-------------------------------------------------------------
|
||||
|
||||
@@ -109,19 +109,18 @@ end)
|
||||
--- setThirst(playerId, 80)
|
||||
--- ```
|
||||
function setThirst(src, thirst)
|
||||
|
||||
debugPrint("^4Debug^7: ^2Adding thirst^7: ^3"..thirst.." ^2to player^7: ^3"..src.."^7")
|
||||
if isStarted(ESXExport) then
|
||||
TriggerClientEvent('esx_status:add', src, 'thirst', thirst)
|
||||
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) or isStarted(RSGExport) then
|
||||
local Player = Core.Functions.GetPlayer(src)
|
||||
Player.Functions.SetMetaData('thirst', thirst)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, thirst, Player.PlayerData.metadata.thirst)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, Player.PlayerData.metadata.hunger, thirst)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
local Player = Core.Functions.GetPlayer(src)
|
||||
Player.Functions.SetMetaData('thirst', thirst)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, thirst, Player.PlayerData.metadata.thirst)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Sets the player's hunger level.
|
||||
@@ -134,18 +133,16 @@ end
|
||||
--- setHunger(playerId, 60)
|
||||
--- ```
|
||||
function setHunger(src, hunger)
|
||||
|
||||
debugPrint("^4Debug^7: ^2Adding hunger^7: ^3"..hunger.." ^2to player^7: ^3"..src.."^7")
|
||||
if isStarted(ESXExport) then
|
||||
TriggerClientEvent('esx_status:add', src, 'hunger', hunger)
|
||||
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) or isStarted(RSGExport) then
|
||||
local Player = Core.Functions.GetPlayer(src)
|
||||
Player.Functions.SetMetaData('hunger', hunger)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, hunger, Player.PlayerData.metadata.hunger)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, hunger, Player.PlayerData.metadata.thirst)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
local Player = Core.Functions.GetPlayer(src)
|
||||
Player.Functions.SetMetaData('hunger', hunger)
|
||||
TriggerClientEvent("hud:client:UpdateNeeds", src, hunger, Player.PlayerData.metadata.hunger)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -153,6 +150,21 @@ end
|
||||
-- Economy Event Handlers
|
||||
-------------------------------------------------------------
|
||||
|
||||
--- BillPlayer
|
||||
function billPlayer(data)
|
||||
if not Config.System.Billing or Config.System.Billing == "jim" then
|
||||
TriggerEvent("jim-payments:client:Charge", {
|
||||
job = data.job,
|
||||
gang = data.gang,
|
||||
coords = data.coords.xyz,
|
||||
img = data.img
|
||||
})
|
||||
elseif Config.System.Billing == "okok" then
|
||||
-- TriggerServerEvent("okokBilling:CreateCustomInvoice", target, price, reason, invoiceSource, society, societyName)
|
||||
TriggerEvent("okokBilling:ToggleCreateInvoice")
|
||||
end
|
||||
end
|
||||
|
||||
--- Charges a player by removing money from their account.
|
||||
---
|
||||
--- @param cost number The amount to charge.
|
||||
@@ -164,6 +176,7 @@ end
|
||||
--- chargePlayer(100, "cash", playerId)
|
||||
--- ```
|
||||
function chargePlayer(cost, moneyType, newsrc)
|
||||
local is_success = false
|
||||
local src = newsrc or source
|
||||
local fundResource = ""
|
||||
if cost < 0 then
|
||||
@@ -172,33 +185,35 @@ function chargePlayer(cost, moneyType, newsrc)
|
||||
end
|
||||
if moneyType == "cash" then
|
||||
if isStarted(OXInv) then fundResource = OXInv
|
||||
exports[OXInv]:RemoveItem(src, "money", cost)
|
||||
is_success = exports[OXInv]:RemoveItem(src, "money", cost)
|
||||
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||
fundResource = QBExport
|
||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
fundResource = RSGExport
|
||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||
|
||||
elseif isStarted(ESXExport) then
|
||||
fundResource = ESXExport
|
||||
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
||||
is_success = true
|
||||
|
||||
end
|
||||
elseif moneyType == "bank" then
|
||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||
fundResource = QBExport
|
||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
fundResource = RSGExport
|
||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||
|
||||
elseif isStarted(ESXExport) then
|
||||
fundResource = ESXExport
|
||||
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
||||
is_success = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -207,6 +222,7 @@ function chargePlayer(cost, moneyType, newsrc)
|
||||
else
|
||||
debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", moneyType, fundResource)
|
||||
end
|
||||
return is_success
|
||||
end
|
||||
RegisterNetEvent(getScript()..":server:ChargePlayer", function(cost, moneyType, newsrc)
|
||||
debugPrint(GetInvokingResource())
|
||||
@@ -228,39 +244,42 @@ end)
|
||||
--- fundPlayer(150, "cash", playerId)
|
||||
--- ```
|
||||
function fundPlayer(fund, moneyType, newsrc)
|
||||
local is_success = false
|
||||
local src = newsrc or source
|
||||
local fundResource = ""
|
||||
|
||||
if moneyType == "cash" then
|
||||
if isStarted(OXInv) then
|
||||
fundResource = OXInv
|
||||
exports[OXInv]:AddItem(src, "money", fund)
|
||||
is_success = exports[OXInv]:AddItem(src, "money", fund)
|
||||
|
||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||
fundResource = QBExport
|
||||
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
fundResource = RSGExport
|
||||
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||
|
||||
elseif isStarted(ESXExport) then
|
||||
fundResource = ESXExport
|
||||
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
||||
is_success = true
|
||||
|
||||
end
|
||||
elseif moneyType == "bank" then
|
||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||
fundResource = QBExport
|
||||
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
fundResource = RSGExport
|
||||
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||
|
||||
elseif isStarted(ESXExport) then
|
||||
fundResource = ESXExport
|
||||
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
||||
is_success = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -269,6 +288,7 @@ function fundPlayer(fund, moneyType, newsrc)
|
||||
else
|
||||
debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
|
||||
end
|
||||
return is_success
|
||||
end
|
||||
|
||||
-------------------------------------------------------------
|
||||
|
||||
@@ -164,25 +164,23 @@ function openShop(data)
|
||||
elseif isStarted(OXInv) then
|
||||
exports[OXInv]:openInventory('shop', { type = data.shop })
|
||||
|
||||
elseif isStarted(QSInv) or isStarted(CodeMInv) then
|
||||
elseif isStarted(QSInv) then
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||
|
||||
elseif isStarted(TgiannInv) then
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
|
||||
elseif isStarted(CodeMInv) then
|
||||
--print(data.shop)
|
||||
TriggerEvent("codem-inventory:openshop", data.shop)
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
if QBInvNew then
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
else
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||
end
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||
|
||||
elseif isStarted(PSInv) then
|
||||
if QBInvNew then
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
else
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||
end
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||
|
||||
elseif isStarted(RSGInv) then
|
||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||
@@ -191,10 +189,10 @@ function openShop(data)
|
||||
end
|
||||
|
||||
RegisterNetEvent(getScript()..':server:openServerShop', function(data)
|
||||
if isStarted(QBInv) then
|
||||
if isStarted(QBInv) and checkExportExists(QBInv, "OpenShop") then
|
||||
exports[QBInv]:OpenShop(source, data)
|
||||
end
|
||||
if isStarted(PSInv) then
|
||||
if isStarted(PSInv) and checkExportExists(PSInv, "OpenShop") then
|
||||
exports[PSInv]:OpenShop(source, data)
|
||||
end
|
||||
if isStarted(TgiannInv) then
|
||||
@@ -217,8 +215,9 @@ end)
|
||||
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
|
||||
--- ```
|
||||
function registerShop(name, label, items, society)
|
||||
local shopResource = ""
|
||||
if isStarted(OXInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
|
||||
shopResource = OXInv
|
||||
exports[OXInv]:RegisterShop(name, {
|
||||
name = label,
|
||||
inventory = items,
|
||||
@@ -226,11 +225,11 @@ function registerShop(name, label, items, society)
|
||||
})
|
||||
|
||||
elseif isStarted(TgiannInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Tgiann ^2Store^7:", name, label)
|
||||
shopResource = TgiannInv
|
||||
exports[TgiannInv]:RegisterShop(name, items)
|
||||
|
||||
elseif isStarted(QBInv) and QBInvNew then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
|
||||
elseif isStarted(QBInv) and checkExportExists(QBInv, "CreateShop") then
|
||||
shopResource = QBInv
|
||||
exports[QBInv]:CreateShop({
|
||||
name = name,
|
||||
label = label,
|
||||
@@ -239,8 +238,8 @@ function registerShop(name, label, items, society)
|
||||
society = society,
|
||||
})
|
||||
|
||||
elseif isStarted(PSInv) and QBInvNew then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3PS ^2Store^7:", name, label)
|
||||
elseif isStarted(PSInv) and checkExportExists(PSInv, "CreateShop") then
|
||||
shopResource = PSInv
|
||||
exports[PSInv]:CreateShop({
|
||||
name = name,
|
||||
label = label,
|
||||
@@ -250,7 +249,7 @@ function registerShop(name, label, items, society)
|
||||
})
|
||||
|
||||
elseif isStarted(RSGInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3RSG ^2Store^7:", name, label)
|
||||
shopResource = RSGInv
|
||||
exports[RSGInv]:CreateShop({
|
||||
name = name,
|
||||
label = label,
|
||||
@@ -259,4 +258,9 @@ function registerShop(name, label, items, society)
|
||||
society = society,
|
||||
})
|
||||
end
|
||||
if shopResource ~= "" then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label)
|
||||
else
|
||||
debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name)
|
||||
end
|
||||
end
|
||||
@@ -151,6 +151,22 @@ function openStash(data)
|
||||
slots = data.slots or 40
|
||||
})
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
if QBInvNew then
|
||||
TriggerServerEvent(getScript()..':server:openServerStash', {
|
||||
stashName = data.stash,
|
||||
label = data.label,
|
||||
maxweight = data.maxWeight or 600000,
|
||||
slots = data.slots or 40
|
||||
})
|
||||
else
|
||||
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, {
|
||||
slots = data.slots or 50,
|
||||
maxWeight = data.maxWeight or 600000
|
||||
})
|
||||
end
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
if QBInvNew then
|
||||
TriggerServerEvent(getScript()..':server:openServerStash', {
|
||||
@@ -176,6 +192,7 @@ function openStash(data)
|
||||
slots = data.slots or 40
|
||||
})
|
||||
else
|
||||
TriggerEvent("ps-inventory:client:SetCurrentStash", data.stash)
|
||||
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
|
||||
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, {
|
||||
slots = data.slots or 50,
|
||||
@@ -211,6 +228,9 @@ RegisterNetEvent(getScript()..":server:openServerStash", function(data)
|
||||
if isStarted(TgiannInv) then
|
||||
exports[TgiannInv]:OpenInventory(source, 'stash', data.stashName, data)
|
||||
end
|
||||
if isStarted(JPRInv) then
|
||||
exports[JPRInv]:OpenInventory(source, data.stashName, data)
|
||||
end
|
||||
if isStarted(QBInv) then
|
||||
exports[QBInv]:OpenInventory(source, data.stashName, data)
|
||||
end
|
||||
@@ -223,8 +243,15 @@ RegisterNetEvent(getScript()..":server:openServerStash", function(data)
|
||||
end)
|
||||
|
||||
function clearStash(stashId)
|
||||
if isStarted(QBInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..QBInv.."^2 Stash^7", stashId)
|
||||
if isStarted(JPRInv) then
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..JPRInv.."^2 Stash^7", stashId)
|
||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||
['stash'] = stashId,
|
||||
['items'] = json.encode({})
|
||||
})
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..QBInv.."^2 Stash^7", stashId)
|
||||
if QBInvNew then
|
||||
exports[QBInv]:ClearStash(stashId)
|
||||
else
|
||||
@@ -235,42 +262,38 @@ function clearStash(stashId)
|
||||
end
|
||||
|
||||
elseif isStarted(OXInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..OXInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..OXInv.."^2 Stash^7", stashId)
|
||||
exports[OXInv]:ClearInventory(stashId)
|
||||
|
||||
elseif isStarted(PSInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..PSInv.."^2 Stash^7", stashId)
|
||||
if QBInvNew then
|
||||
exports[QBInv]:ClearStash(stashId)
|
||||
else
|
||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||
['stash'] = stashId,
|
||||
['items'] = json.encode({})
|
||||
})
|
||||
end
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..PSInv.."^2 Stash^7", stashId)
|
||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||
['stash'] = stashId,
|
||||
['items'] = json.encode({})
|
||||
})
|
||||
|
||||
elseif isStarted(QSInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..QSInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..QSInv.."^2 Stash^7", stashId)
|
||||
exports[QSInv]:ClearOtherInventory('stash', stashId)
|
||||
|
||||
elseif isStarted(CoreInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..CoreInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..CoreInv.."^2 Stash^7", stashId)
|
||||
exports[CoreInv]:clearInventory("stash-"..stashId)
|
||||
|
||||
elseif isStarted(CodeMInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..CodeMInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..CodeMInv.."^2 Stash^7", stashId)
|
||||
exports[CodeMInv]:ClearInventory(stashId)
|
||||
|
||||
elseif isStarted(OrigenInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..OrigenInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..OrigenInv.."^2 Stash^7", stashId)
|
||||
exports[OrigenInv]:ClearInventory(stashId)
|
||||
|
||||
elseif isStarted(TgiannInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..TgiannInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..TgiannInv.."^2 Stash^7", stashId)
|
||||
exports["tgiann-inventory"]:DeleteInventory("stash", stashId)
|
||||
|
||||
elseif isStarted(RSGInv) then
|
||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..RSGInv.."^2 Stash^7", stashId)
|
||||
debugPrint("^5Bridge^7: ^2Clearing ^3"..RSGInv.."^2 Stash^7", stashId)
|
||||
exports[RSGInv]:ClearStash(stashId)
|
||||
|
||||
end
|
||||
@@ -331,6 +354,11 @@ function getStash(stashName)
|
||||
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
|
||||
if result then stashItems = json.decode(result) end
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
stashResource = JPRInv
|
||||
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
|
||||
if result then stashItems = json.decode(result) end
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
stashResource = QBInv
|
||||
if QBInvNew then
|
||||
@@ -449,6 +477,29 @@ function stashRemoveItem(stashItems, stashName, items)
|
||||
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..TgiannInv, k, v)
|
||||
end
|
||||
|
||||
elseif isStarted(JPRInv) then
|
||||
if not stashItems or not next(stashItems) then
|
||||
stashItems = getStash(stashName[1])
|
||||
end
|
||||
for k, v in pairs(items) do
|
||||
for l in pairs(stashItems) do
|
||||
if stashItems[l].name == k then
|
||||
if (stashItems[l].amount - v) <= 0 then
|
||||
debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
|
||||
stashItems[l] = nil
|
||||
else
|
||||
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with "..JPRInv, k, v)
|
||||
stashItems[l].amount -= v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3JPR^2 stash '^6"..stashName[1].."^7'")
|
||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||
['stash'] = stashName[1],
|
||||
['items'] = json.encode(stashItems)
|
||||
})
|
||||
|
||||
elseif isStarted(QBInv) then
|
||||
if QBInvNew then
|
||||
for k, v in pairs(items) do
|
||||
@@ -531,7 +582,7 @@ RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem)
|
||||
--- local hasAll, details = stashhasItem(currentStashItems, { iron = 2, wood = 5 })
|
||||
--- ```
|
||||
function stashhasItem(stashItems, items, amount)
|
||||
local invs = { OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, QBInv, PSInv, RSGInv }
|
||||
local invs = { OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, JPRInv, QBInv, PSInv, RSGInv }
|
||||
local foundInv = ""
|
||||
for _, inv in ipairs(invs) do
|
||||
if isStarted(inv) then
|
||||
@@ -585,27 +636,25 @@ end
|
||||
--- )
|
||||
--- ```
|
||||
function registerStash(name, label, slots, weight, owner, coords)
|
||||
local stashResource = ""
|
||||
if isStarted(OXInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Stash^7:", name, label, owner or nil)
|
||||
stashResource = OXInv
|
||||
exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
|
||||
|
||||
--elseif isStarted(QSInv) then
|
||||
-- debugPrint("^6Bridge^7: ^2Registering ^3QS ^2Stash^7:", name, label)
|
||||
-- exports[QSInv]:RegisterStash(name, label, slots or 50, weight or 4000000)
|
||||
|
||||
--elseif isStarted(CoreInv) then
|
||||
-- debugPrint("^6Bridge^7: ^2Registering ^3CoreInv ^2Stash^7:", name, label)
|
||||
-- exports[CoreInv]:openHolder(nil, name, 'stash', nil, nil, false, nil)
|
||||
|
||||
elseif isStarted(OrigenInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3OrigenInv ^2Stash^7:", name, label)
|
||||
stashResource = OrigenInv
|
||||
exports["origen_inventory"]:registerStash(name, label, slots or 50, weight or 4000000)
|
||||
|
||||
elseif isStarted(TgiannInv) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3TgiannInv ^2Stash^7:", name, label)
|
||||
stashResource = TgiannInv
|
||||
exports[TgiannInv]:RegisterStash(name, label, slots or 50, weight or 4000000)
|
||||
|
||||
end
|
||||
if stashResource ~= "" then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^4"..stashResource.." ^3Stash^7:", name, "^4Label^7: "..label)
|
||||
else
|
||||
debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register stash^7:", name)
|
||||
end
|
||||
end
|
||||
|
||||
if isServer() then
|
||||
|
||||
@@ -21,24 +21,29 @@
|
||||
--- })
|
||||
--- ```
|
||||
function registerCommand(command, options)
|
||||
|
||||
local commandResource = ""
|
||||
if isStarted(OXLibExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..OXLibExport, command)
|
||||
commandResource = OXLibExport
|
||||
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
|
||||
|
||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..QBExport, command)
|
||||
commandResource = QBExport
|
||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
||||
|
||||
elseif isStarted(RSGExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..RSGExport, command)
|
||||
commandResource = RSGExport
|
||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
||||
|
||||
elseif isStarted(ESXExport) then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7ESX Legacy", command)
|
||||
commandResource = ESXExport
|
||||
ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
|
||||
options[4](xPlayer.source, args, showError)
|
||||
end, false, { help = options[1] })
|
||||
|
||||
end
|
||||
if commandResource ~= "" then
|
||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^4"..commandResource.."^7:", "^7/"..command)
|
||||
else
|
||||
print("^1ERROR^7: ^1Couldn't find supported framework to register command^7:", "/"..command)
|
||||
end
|
||||
end
|
||||
47
starter.lua
47
starter.lua
@@ -14,6 +14,7 @@ Exports = {
|
||||
CodeMInv = "codem-inventory",
|
||||
OrigenInv = "origen_inventory",
|
||||
TgiannInv = "tgiann-inventory",
|
||||
JPRInv = "jpr-inventory",
|
||||
|
||||
OXLibExport = "ox_lib",
|
||||
|
||||
@@ -176,7 +177,7 @@ for _, v in pairs({ -- This is a specific load order
|
||||
'effects.lua',
|
||||
|
||||
--'warmenu.lua',
|
||||
|
||||
'_authToken.lua',
|
||||
-- Do version check last
|
||||
'_scriptversioncheck.lua'
|
||||
}) do
|
||||
@@ -188,4 +189,48 @@ for _, v in pairs({ -- This is a specific load order
|
||||
if debugMode then
|
||||
print("^5CoreLoader^7: ^2loaded file^7: ^3"..(v):gsub("/", "^7/^3"):gsub("%.lua", "^7.lua").."^7")
|
||||
end
|
||||
end
|
||||
|
||||
-- Auto Detection of Old/New QBInv/PSInv versions --
|
||||
|
||||
if isStarted(QBInv) then
|
||||
if isServer() then
|
||||
if not checkExportExists(QBInv, "CreateShop") then
|
||||
print("^6Bridge^7: ^2Can^7'^2t ^2find new ^4"..QBInv.."^2 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||
QBInvNew = false
|
||||
end
|
||||
else
|
||||
if checkExportExists(QBInv, "GetTrunkSize") then
|
||||
print("^6Bridge^7: ^2Found old ^4"..QBInv.."^3 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||
QBInvNew = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isStarted(PSInv) then
|
||||
if isServer() then
|
||||
if not checkExportExists(PSInv, "CreateShop") then
|
||||
print("^6Bridge^7: ^2Can^7'^2t ^2find new ^4"..PSInv.."^2 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||
QBInvNew = false
|
||||
end
|
||||
else
|
||||
local function versionCompare(v1, v2)
|
||||
local split = function(v) local t = {} for s in v:gmatch("[^.]+") do t[#t+1] = tonumber(s) end return t end
|
||||
local a, b = split(v1), split(v2)
|
||||
for i = 1, math.max(#a, #b) do
|
||||
local ai, bi = a[i] or 0, b[i] or 0
|
||||
if ai > bi then return true
|
||||
elseif ai < bi then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
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")
|
||||
QBInvNew = true
|
||||
else
|
||||
print("^6Bridge^7: ^4"..PSInv.."^2 Version ^1below ^31.0.6^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||
QBInvNew = false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,13 +2,15 @@ local notifications = {}
|
||||
local spacing = 60 -- vertical spacing between notifications
|
||||
local activeDrawing = false -- flag for drawing loop state
|
||||
|
||||
if not HasStreamedTextureDictLoaded("timerbars") then
|
||||
while not HasStreamedTextureDictLoaded("timerbars") do
|
||||
RequestStreamedTextureDict("timerbars")
|
||||
Wait(5)
|
||||
end
|
||||
local function loadTextureDict(dict)
|
||||
if not HasStreamedTextureDictLoaded(dict) then
|
||||
while not HasStreamedTextureDictLoaded(dict) do RequestStreamedTextureDict(dict) Wait(5) end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- GTA Section ---
|
||||
|
||||
local notifTypes = {
|
||||
success = "✔️",
|
||||
error = "❌",
|
||||
@@ -35,12 +37,13 @@ function gtaNotify(title, message, emoji, src)
|
||||
-- This allows it to be silent until the first notifcation is called, then the loop starts
|
||||
if not activeDrawing then
|
||||
activeDrawing = true
|
||||
StartDrawingLoop()
|
||||
StartGTADrawingLoop()
|
||||
end
|
||||
end
|
||||
|
||||
-- Drawing loop as a separate controlled thread
|
||||
function StartDrawingLoop()
|
||||
function StartGTADrawingLoop()
|
||||
--loadTextureDict("timerbars")
|
||||
CreateThread(function()
|
||||
while #notifications > 0 do
|
||||
local currentTime = GetGameTimer()
|
||||
@@ -87,14 +90,14 @@ function StartDrawingLoop()
|
||||
if not notif.title or notif.title == "" then
|
||||
moveMessage = true
|
||||
else
|
||||
drawNotiText(8, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
||||
drawGTANotiText(8, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
||||
end
|
||||
|
||||
-- Message text
|
||||
drawNotiText(4, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.015 or 0.03)))
|
||||
drawGTANotiText(4, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.015 or 0.03)))
|
||||
|
||||
-- Emoji
|
||||
drawNotiText(0, 0.3, vec2(0.75, 0.995), notif.emoji, vec2(posX, posY + 0.015))
|
||||
drawGTANotiText(0, 0.3, vec2(0.75, 0.995), notif.emoji, vec2(posX, posY + 0.015))
|
||||
|
||||
::continue::
|
||||
end
|
||||
@@ -104,7 +107,7 @@ function StartDrawingLoop()
|
||||
end)
|
||||
end
|
||||
|
||||
function drawNotiText(font, scale, wrap, string, pos)
|
||||
function drawGTANotiText(font, scale, wrap, string, pos)
|
||||
SetTextFont(font)
|
||||
SetTextScale(scale, scale)
|
||||
SetTextWrap(wrap.x, wrap.y)
|
||||
@@ -116,5 +119,148 @@ function drawNotiText(font, scale, wrap, string, pos)
|
||||
DrawText(pos.x, pos.y)
|
||||
end
|
||||
|
||||
RegisterNetEvent("jim-bridge:Notify", gtaNotify)
|
||||
exports("Notify", gtaNotify)
|
||||
|
||||
------------------
|
||||
-- REDM SECTION --
|
||||
------------------
|
||||
---
|
||||
---
|
||||
local REDMnotifTypes = {
|
||||
success = vec3(0, 150, 0),
|
||||
error = vec3(150, 0, 0),
|
||||
warning = vec3(0, 150, 150),
|
||||
police = vec3(0, 0, 150),
|
||||
ambulance = vec3(0, 0, 150),
|
||||
}
|
||||
|
||||
function redNotify(title, message, style)
|
||||
local notif = {
|
||||
title = title and CreateVarString(10, "LITERAL_STRING", title) or nil,
|
||||
message = message and CreateVarString(10, "LITERAL_STRING", message) or nil,
|
||||
state = "enter",
|
||||
startTime = GetGameTimer(),
|
||||
style = REDMnotifTypes[style] or vec3(0, 0, 0),
|
||||
progress = 0,
|
||||
holdTime = 8000,
|
||||
slideDuration = 100,
|
||||
currentOffset = 0,
|
||||
}
|
||||
table.insert(notifications, notif)
|
||||
|
||||
-- Activate drawing loop if not already running
|
||||
if not activeDrawing then
|
||||
activeDrawing = true
|
||||
StartREDMDrawingLoop()
|
||||
end
|
||||
end
|
||||
|
||||
-- Drawing loop as a separate controlled thread
|
||||
function StartREDMDrawingLoop()
|
||||
CreateThread(function()
|
||||
loadTextureDict("generic_textures")
|
||||
while #notifications > 0 do
|
||||
local currentTime = GetGameTimer()
|
||||
|
||||
-- Update notifications vertical offset
|
||||
for i, notif in ipairs(notifications) do
|
||||
local target = (#notifications - i) * spacing
|
||||
notif.currentOffset += (target - notif.currentOffset) * 0.1
|
||||
end
|
||||
|
||||
for i = #notifications, 1, -1 do
|
||||
local notif = notifications[i]
|
||||
|
||||
if notif.state == "enter" then
|
||||
local elapsed = currentTime - notif.startTime
|
||||
notif.progress = math.min(elapsed / notif.slideDuration, 1.0)
|
||||
if notif.progress >= 1.0 then
|
||||
notif.state = "hold"
|
||||
notif.holdStart = currentTime
|
||||
end
|
||||
elseif notif.state == "hold" then
|
||||
if currentTime - notif.holdStart >= notif.holdTime then
|
||||
notif.state = "exit"
|
||||
notif.exitStart = currentTime
|
||||
end
|
||||
elseif notif.state == "exit" then
|
||||
local elapsed = currentTime - notif.exitStart
|
||||
notif.progress = 1.0 - math.min(elapsed / notif.slideDuration, 1.0)
|
||||
if notif.progress <= 0 then
|
||||
table.remove(notifications, i)
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
|
||||
local startX, targetX = 1.0, 0.8
|
||||
local posX = startX - (startX - targetX) * notif.progress
|
||||
local posY = 0.05 + (notif.currentOffset / 1080)
|
||||
|
||||
-- Background sprite
|
||||
DrawSprite("generic_textures", "inkroller_1a", posX + 0.09, posY + 0.025, 0.2, 0.053, 180.0, notif.style.x, notif.style.y, notif.style.z, 150)
|
||||
|
||||
-- Title text
|
||||
local moveMessage = false
|
||||
if not notif.title or notif.title == "" then
|
||||
moveMessage = true
|
||||
else
|
||||
drawREDMNotiText(1, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
||||
end
|
||||
|
||||
-- Message text
|
||||
drawREDMNotiText(6, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.0145 or 0.025)))
|
||||
|
||||
::continue::
|
||||
end
|
||||
Wait(0)
|
||||
end
|
||||
activeDrawing = false -- No notifications left, pause drawing
|
||||
end)
|
||||
end
|
||||
|
||||
function drawREDMNotiText(font, scale, wrap, string, pos)
|
||||
SetTextFontForCurrentCommand(font)
|
||||
SetTextScale(scale, scale)
|
||||
SetTextColor(255, 255, 255, 255)
|
||||
SetTextWrap(wrap.x, wrap.y)
|
||||
SetTextJustification(2)
|
||||
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||
DisplayText(string, pos.x, pos.y)
|
||||
end
|
||||
|
||||
-- Exports --
|
||||
-- Example:
|
||||
-- TriggerEvent("jim_bridge:Notify", nil, "Short test message", "success")
|
||||
--
|
||||
-- exports["jim_bridge"]:Notify(nil, "Short test message", "success")
|
||||
--
|
||||
|
||||
RegisterNetEvent("jim_bridge:RedNotify", redNotify)
|
||||
RegisterNetEvent("jim_bridge:GTANotify", gtaNotify)
|
||||
|
||||
RegisterNetEvent("jim_bridge:Notify", function(...)
|
||||
if GetCurrentGameName() == "rdr3" then
|
||||
redNotify(...)
|
||||
else
|
||||
gtaNotify(...)
|
||||
end
|
||||
end)
|
||||
|
||||
function Notify(...)
|
||||
if GetCurrentGameName() == "rdr3" then
|
||||
redNotify(...)
|
||||
else
|
||||
gtaNotify(...)
|
||||
end
|
||||
end
|
||||
|
||||
exports("Notify", Notify)
|
||||
|
||||
-- Example testing notifications (can remove after testing)
|
||||
--CreateThread(function()
|
||||
-- TriggerEvent("jim_bridge:Notify", nil, "Short test message", "success")
|
||||
-- Wait(3000)
|
||||
-- TriggerEvent("jim_bridge:Notify", "Error!", "This is a longer example message to show stacking.", "error")
|
||||
-- Wait(3000)
|
||||
-- TriggerEvent("jim_bridge:Notify", "Test!", "This notification has to 'type' set.", nil )
|
||||
-- Wait(4000)
|
||||
--end)
|
||||
344
ui_modules/progressbar.lua
Normal file
344
ui_modules/progressbar.lua
Normal file
@@ -0,0 +1,344 @@
|
||||
local inProgress = false
|
||||
|
||||
local function loadTextureDict(dict)
|
||||
if not HasStreamedTextureDictLoaded(dict) then
|
||||
while not HasStreamedTextureDictLoaded(dict) do RequestStreamedTextureDict(dict) Wait(5) end
|
||||
end
|
||||
end
|
||||
|
||||
function redProgressBar(data)
|
||||
local ped = PlayerPedId()
|
||||
|
||||
local result = nil
|
||||
|
||||
loadTextureDict("generic_textures")
|
||||
if inProgress then return false end
|
||||
inProgress = true
|
||||
local wait = debugMode and 1000 or data.time
|
||||
local endTime = GetGameTimer() + wait
|
||||
|
||||
LocalPlayer.state:set("inv_busy", true, true)
|
||||
TriggerEvent('inventory:client:busy:status', true)
|
||||
TriggerEvent('canUseInventoryAndHotbar:toggle', not true)
|
||||
|
||||
-- Setup Animation/Task if specified
|
||||
if data.dict then
|
||||
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
||||
elseif data.task then
|
||||
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
||||
end
|
||||
|
||||
-- Progress bar rendering loop
|
||||
CreateThread(function()
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(0)
|
||||
local elapsed = GetGameTimer()
|
||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||
|
||||
-- Convert to segmented progress (assuming 5 segments here)
|
||||
local segments = 1 -- Number of segments in the bar
|
||||
local segmentProgress = {}
|
||||
local progressPerSegment = 100 / segments
|
||||
|
||||
for i = 1, segments do
|
||||
local segmentStart = (i - 1) * progressPerSegment
|
||||
local segmentEnd = i * progressPerSegment
|
||||
if percentage >= segmentEnd then
|
||||
segmentProgress[i] = 100
|
||||
elseif percentage <= segmentStart then
|
||||
segmentProgress[i] = 0
|
||||
else
|
||||
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
||||
end
|
||||
end
|
||||
|
||||
percentage = percentage >= 100 and 100 or percentage
|
||||
-- Draw your segmented progress bar
|
||||
ShowRedProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
||||
|
||||
-- Controls to disable during progress
|
||||
if data.disableMouse then
|
||||
DisableControlAction(0, `INPUT_LOOK_LR`, true)
|
||||
DisableControlAction(0, `INPUT_LOOK_UD`, true)
|
||||
DisableControlAction(0, `INPUT_VEH_MOUSE_CONTROL_OVERRIDE`, true)
|
||||
end
|
||||
|
||||
if data.disableMovement then
|
||||
DisableControlAction(0, `INPUT_MOVE_LR`, true)
|
||||
DisableControlAction(0, `INPUT_MOVE_UD`, true)
|
||||
DisableControlAction(0, `INPUT_DUCK`, true)
|
||||
DisableControlAction(0, `INPUT_SPRINT`, true)
|
||||
end
|
||||
|
||||
if data.disableCarMovement then
|
||||
DisableControlAction(0, `INPUT_VEH_MOVE_LEFT_ONLY`, true)
|
||||
DisableControlAction(0, `INPUT_VEH_MOVE_RIGHT_ONLY`, true)
|
||||
DisableControlAction(0, `INPUT_VEH_ACCELERATE`, true)
|
||||
DisableControlAction(0, `INPUT_VEH_BRAKE`, true)
|
||||
DisableControlAction(0, `INPUT_VEH_EXIT`, true)
|
||||
end
|
||||
|
||||
if data.disableCombat then
|
||||
DisablePlayerFiring(PlayerId(), true)
|
||||
DisableControlAction(0, `INPUT_ATTACK`, true)
|
||||
DisableControlAction(0, `INPUT_AIM`, true)
|
||||
DisableControlAction(0, `INPUT_OPEN_WHEEL_MENU`, true)
|
||||
DisableControlAction(0, `INPUT_DETONATE`, true)
|
||||
DisableControlAction(0, `INPUT_THROW_GRENADE`, true)
|
||||
DisableControlAction(0, `INPUT_MELEE_ATTACK`, true)
|
||||
DisableControlAction(0, `INPUT_MELEE_GRAPPLE_ATTACK`, true)
|
||||
end
|
||||
|
||||
if data.cancel and (
|
||||
IsControlJustReleased(0, `INPUT_FRONTEND_RS`) or
|
||||
IsControlJustReleased(0, `INPUT_SWITCH_SHOULDER`) or
|
||||
IsControlJustReleased(0, `INPUT_GAME_MENU_TAB_RIGHT_SECONDARY`) or
|
||||
IsControlJustReleased(0, `INPUT_FRONTEND_CANCEL`) or
|
||||
IsControlJustReleased(0, `INPUT_GAME_MENU_CANCEL`) or
|
||||
IsControlJustReleased(0, `INPUT_FRONTEND_RRIGHT`) or
|
||||
IsControlJustReleased(0, `INPUT_QUIT`) or
|
||||
IsControlJustReleased(0, `INPUT_MINIGAME_QUIT`)
|
||||
) then
|
||||
inProgress = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Wait for completion or cancel
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
-- Cleanup animations/tasks
|
||||
if data.dict then
|
||||
stopAnim(data.dict, data.anim, ped)
|
||||
end
|
||||
if data.task then
|
||||
ClearPedTasks(ped)
|
||||
end
|
||||
|
||||
result = inProgress
|
||||
inProgress = false
|
||||
|
||||
while result == nil do Wait(10) end
|
||||
|
||||
-- Cleanup
|
||||
FreezeEntityPosition(ped, false)
|
||||
|
||||
LocalPlayer.state:set("inv_busy", false, true)
|
||||
TriggerEvent('inventory:client:busy:status', false)
|
||||
TriggerEvent('canUseInventoryAndHotbar:toggle', not false)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function ShowRedProgressBar(currentProg, title, level)
|
||||
local loc = vec2(0.40, 0.90)
|
||||
local size = vec2(0.3, 0.03)
|
||||
|
||||
-- Draw background box
|
||||
DrawSprite("generic_textures", "inkroller_1a", loc.x+0.1, loc.y-0.01, 0.25, 0.07, 180.0, 0, 0, 0, 200)
|
||||
|
||||
SetTextFontForCurrentCommand(6)
|
||||
SetTextScale(0.35, 0.35)
|
||||
SetTextColor(255, 255, 255, 255)
|
||||
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||
BgDisplayText(title, loc.x - size.x / 4 + 0.074, loc.y - 0.034)
|
||||
|
||||
SetTextFontForCurrentCommand(1)
|
||||
SetTextScale(0.35, 0.35)
|
||||
SetTextColor(255, 255, 255, 255)
|
||||
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||
BgDisplayText(level, loc.x - size.x / 4 + 0.246, loc.y - 0.030)
|
||||
|
||||
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
||||
|
||||
for i = 1, #currentProg do
|
||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
||||
local fillPercentage = currentProg[i]
|
||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
||||
|
||||
-- Semi-transparent background for each segment
|
||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
||||
|
||||
-- Filling progress for each segment
|
||||
if progressBarWidth > 0 then
|
||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 255, 0, 0, 200) -- Blue progress
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function gtaProgressBar(data)
|
||||
local ped = PlayerPedId()
|
||||
|
||||
local result = nil
|
||||
|
||||
loadTextureDict("timerbars")
|
||||
if inProgress then return false end
|
||||
inProgress = true
|
||||
local wait = debugMode and 1000 or data.time
|
||||
local endTime = GetGameTimer() + wait
|
||||
|
||||
-- Setup Animation/Task if specified
|
||||
if data.dict then
|
||||
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
||||
elseif data.task then
|
||||
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
||||
end
|
||||
|
||||
-- Progress bar rendering loop
|
||||
CreateThread(function()
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(0)
|
||||
local elapsed = GetGameTimer()
|
||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||
|
||||
-- Convert to segmented progress (assuming 5 segments here)
|
||||
local segments = 1 -- Number of segments in the bar
|
||||
local segmentProgress = {}
|
||||
local progressPerSegment = 100 / segments
|
||||
|
||||
for i = 1, segments do
|
||||
local segmentStart = (i - 1) * progressPerSegment
|
||||
local segmentEnd = i * progressPerSegment
|
||||
if percentage >= segmentEnd then
|
||||
segmentProgress[i] = 100
|
||||
elseif percentage <= segmentStart then
|
||||
segmentProgress[i] = 0
|
||||
else
|
||||
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
||||
end
|
||||
end
|
||||
|
||||
percentage = percentage >= 100 and 100 or percentage
|
||||
-- Draw your segmented progress bar
|
||||
ShowGTAProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
||||
|
||||
-- Controls to disable during progress
|
||||
DisablePlayerFiring(ped, true)
|
||||
DisableControlAction(0, 25, true) -- Disable aim
|
||||
DisableControlAction(0, 21, true) -- Disable sprint
|
||||
DisableControlAction(0, 30, true) -- Disable move left/right
|
||||
DisableControlAction(0, 31, true) -- Disable move forward/back
|
||||
DisableControlAction(0, 36, true) -- Disable stealth
|
||||
|
||||
if data.cancel and (IsControlJustReleased(0, 202) or IsControlJustReleased(0, 177) or IsControlJustReleased(0, 73)) then
|
||||
inProgress = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Wait for completion or cancel
|
||||
while GetGameTimer() < endTime and inProgress do
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
-- Cleanup animations/tasks
|
||||
if data.dict then
|
||||
stopAnim(data.dict, data.anim, ped)
|
||||
end
|
||||
if data.task then
|
||||
ClearPedTasks(ped)
|
||||
end
|
||||
|
||||
result = inProgress
|
||||
inProgress = false
|
||||
|
||||
while result == nil do Wait(10) end
|
||||
|
||||
-- Cleanup
|
||||
FreezeEntityPosition(ped, false)
|
||||
|
||||
LocalPlayer.state:set("inv_busy", false, true)
|
||||
TriggerEvent('inventory:client:busy:status', false)
|
||||
TriggerEvent('canUseInventoryAndHotbar:toggle', not false)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function ShowGTAProgressBar(currentProg, title, level)
|
||||
local loc = vec2(0.37, 0.90)
|
||||
local size = vec2(0.3, 0.03)
|
||||
|
||||
-- Draw background box
|
||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.028, loc.y-0.01, 0.15, 0.07, 0.0, 255, 255, 255, 255)
|
||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.170, loc.y-0.01, 0.15, 0.07, 180.0, 255, 255, 255, 255)
|
||||
|
||||
SetTextFont(0)
|
||||
SetTextProportional(1)
|
||||
SetTextScale(0.35, 0.35)
|
||||
SetTextColour(255, 255, 255, 255)
|
||||
SetTextDropshadow(0, 0, 0, 0, 255)
|
||||
SetTextDropShadow()
|
||||
SetTextOutline()
|
||||
SetTextEntry("STRING")
|
||||
AddTextComponentString(title)
|
||||
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034) -- Adjust text position
|
||||
|
||||
SetTextFont(0)
|
||||
SetTextProportional(1)
|
||||
SetTextScale(0.35, 0.25)
|
||||
SetTextColour(255, 255, 255, 255)
|
||||
SetTextEntry("STRING")
|
||||
AddTextComponentString(level)
|
||||
DrawText(loc.x - size.x / 4 + 0.246, loc.y - 0.030) -- Right-aligned additional text
|
||||
|
||||
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
||||
|
||||
for i = 1, #currentProg do
|
||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
||||
local fillPercentage = currentProg[i]
|
||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
||||
|
||||
-- Semi-transparent background for each segment
|
||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
||||
|
||||
-- Filling progress for each segment
|
||||
if progressBarWidth > 0 then
|
||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 93, 182, 229, 255) -- Blue progress
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function stopProgressBar() inProgress = false end
|
||||
function isProgressBar() return inProgress end
|
||||
|
||||
function progressBar(...)
|
||||
if GetCurrentGameName() == "rdr3" then
|
||||
return redProgressBar(...)
|
||||
else
|
||||
return gtaProgressBar(...)
|
||||
end
|
||||
end
|
||||
|
||||
exports("progressBar", function(...)
|
||||
return progressBar(...)
|
||||
end)
|
||||
exports("gtaProgressBar", function(...)
|
||||
return gtaProgressBar(...)
|
||||
end)
|
||||
exports("redProgressBar", function(...)
|
||||
return redProgressBar(...)
|
||||
end)
|
||||
|
||||
exports("stopProgressBar", stopProgressBar)
|
||||
exports("isProgressBar", isProgressBar)
|
||||
|
||||
--RegisterCommand("testprog", function()
|
||||
-- print("test")
|
||||
-- if progressBar({
|
||||
-- label = "Processing...",
|
||||
-- time = 10000,
|
||||
-- disableMovement = true,
|
||||
-- --dict = "amb@world_human_hang_out_street@female_hold_arm@base",
|
||||
-- --anim = "base",
|
||||
-- --flag = 49,
|
||||
-- cancel = true,
|
||||
-- }) then
|
||||
-- Notify("Success!", "Short test message", "success")
|
||||
-- else
|
||||
-- Notify("Error!", "This is a longer example message to show stacking.", "error")
|
||||
-- end
|
||||
--end)
|
||||
@@ -79,7 +79,7 @@ function gtaSkillCheck()
|
||||
-- Draw stationary cursor at result position
|
||||
DrawRect(x - width / 2 + cursorPos * width, y, 0.002, height + 0.01, 255, 255, 255, 255)
|
||||
-- Display result text
|
||||
drawSuccessText(x, y, result and "Success" or "Failed", result and 114 or 228, result and 204 or 52, result and 144 or 52)
|
||||
drawGTASuccessText(x, y, result and "Success" or "Failed", result and 114 or 228, result and 204 or 52, result and 144 or 52)
|
||||
end
|
||||
if result then
|
||||
successes += 1
|
||||
@@ -94,7 +94,7 @@ function gtaSkillCheck()
|
||||
return successes == barsRequired
|
||||
end
|
||||
|
||||
function drawSuccessText(x, y, text, r, g, b)
|
||||
function drawGTASuccessText(x, y, text, r, g, b)
|
||||
SetTextFont(8)
|
||||
SetTextScale(0.45, 0.45)
|
||||
SetTextColour(r, g, b, 255)
|
||||
@@ -118,4 +118,10 @@ function createScaleBars(x, y, width, height)
|
||||
DrawRect(x, y, width, height, 100, 100, 100, 255)
|
||||
end
|
||||
|
||||
exports("skillCheck", gtaSkillCheck)
|
||||
exports("skillCheck", function(...)
|
||||
if GetCurrentGameName() == "rdr3" then
|
||||
--redSkillCheck(...)
|
||||
else
|
||||
gtaSkillCheck(...)
|
||||
end
|
||||
end)
|
||||
14
version.txt
14
version.txt
@@ -1,12 +1,8 @@
|
||||
2.0.15
|
||||
2.0.19
|
||||
|
||||
- Fix old PSInv/QBInv stashes being wiped when it should be updating them
|
||||
- Better breakout from opening inventory while crafting
|
||||
- Unify isInventoryOpen() function to simply check for IsNuiFocused()
|
||||
- Add clearStash() and doesItemExist() functions for future use
|
||||
- Add warmenu.lua file for gta scaleform menus (loading is disabled by default)
|
||||
- Move custom GTA native notify, skillcheck, target to separate files, can be used outside the script
|
||||
- Complete refactor of framework shared info loading, now only does it once and then shares to scripts
|
||||
- Added extra checks for ESX loading, should hopefully stop the coreloader line 317 error
|
||||
- SetMetadata function now handles full metadata, not just durability
|
||||
- Completely rework framework caching, it was lagging behind and breaking
|
||||
- Add new hasFreeInvnentorySlots() function
|
||||
- Add inv config file grabber, checks inventory configs for inventory weight/slots
|
||||
|
||||
https://github.com/jimathy/jim_bridge
|
||||
Reference in New Issue
Block a user