From c9c6a28063c33bee11ed6c308dc5e0affbf197d1 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 13 Jul 2025 10:11:50 +0200 Subject: [PATCH 1/8] feat(es_extended): refresh/add items during runtime --- [core]/es_extended/client/functions.lua | 2 +- [core]/es_extended/client/modules/events.lua | 4 + [core]/es_extended/server/common.lua | 6 +- [core]/es_extended/server/functions.lua | 73 +++++++++++++++++++ .../es_extended/server/modules/commands.lua | 4 + 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 6e2aa519..c8483d02 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -85,7 +85,7 @@ end function ESX.SetPlayerData(key, val) local current = ESX.PlayerData[key] ESX.PlayerData[key] = val - if key ~= "inventory" and key ~= "loadout" then + if key ~= "loadout" then if type(val) == "table" or val ~= current then TriggerEvent("esx:setPlayerData", key, val, current) end diff --git a/[core]/es_extended/client/modules/events.lua b/[core]/es_extended/client/modules/events.lua index db4d0c2d..d68ef3e5 100644 --- a/[core]/es_extended/client/modules/events.lua +++ b/[core]/es_extended/client/modules/events.lua @@ -57,6 +57,10 @@ ESX.SecureNetEvent("esx:setMaxWeight", function(newMaxWeight) ESX.SetPlayerData("maxWeight", newMaxWeight) end) +ESX.SecureNetEvent("esx:setInventory", function(newInventory) + ESX.SetPlayerData("inventory", newInventory) +end) + local function onPlayerSpawn() ESX.SetPlayerData("ped", PlayerPedId()) ESX.SetPlayerData("dead", false) diff --git a/[core]/es_extended/server/common.lua b/[core]/es_extended/server/common.lua index 49071d8b..a0f571df 100644 --- a/[core]/es_extended/server/common.lua +++ b/[core]/es_extended/server/common.lua @@ -36,11 +36,9 @@ end MySQL.ready(function() Core.DatabaseConnected = true + if not Config.CustomInventory then - local items = MySQL.query.await("SELECT * FROM items") - for _, v in ipairs(items) do - ESX.Items[v.name] = { label = v.label, weight = v.weight, rare = v.rare, canRemove = v.can_remove } - end + ESX.RefreshItems() end ESX.RefreshJobs() diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index cb4157b1..f7176d4a 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -635,6 +635,79 @@ if not Config.CustomInventory then TriggerClientEvent("esx:createPickup", -1, pickupId, label, coords, itemType, name, components, tintIndex) Core.PickupId = pickupId end + + local function refreshPlayerInventories() + for i, xPlayer in ipairs(ESX.GetExtendedPlayers()) do + local minimalInv = xPlayer.getInventory(true) + + for itemName, itemCount in pairs(minimalInv) do + if not ESX.Items[itemName] then + xPlayer.setInventoryItem(itemName, 0) + minimalInv[itemName] = nil + end + end + + xPlayer.inventory = {} + for itemName, itemData in pairs(ESX.Items) do + xPlayer.inventory[#xPlayer.inventory+1] = { + name = itemName, + count = minimalInv[itemName] or 0, + label = itemData.label, + weight = itemData.weight, + usable = Core.UsableItemsCallbacks[itemName] ~= nil, + rare = itemData.rare, + canRemove = itemData.canRemove, + } + end + + TriggerClientEvent("esx:setInventory", xPlayer.source, xPlayer.inventory) + end + end + + function ESX.RefreshItems() + ESX.Items = {} + + local items = MySQL.query.await("SELECT * FROM items") + for _, v in ipairs(items) do + ESX.Items[v.name] = { label = v.label, weight = v.weight, rare = v.rare, canRemove = v.can_remove } + end + + refreshPlayerInventories() + end + + ---@param name string + ---@param label string + ---@param weight number + ---@param rare boolean? + ---@param canRemove boolean? + function ESX.AddItem(name, label, weight, rare, canRemove) + assert(type(name) == "string", "Item name must be a string") + assert(type(label) == "string", "Item label must be a string") + assert(type(weight) == "number", "Item weight must be a number") + assert(type(rare) == "boolean" or rare == nil, "Item rare must be a boolean or nil") + assert(type(canRemove) == "boolean" or canRemove == nil, "Item canRemove must be a boolean or nil") + + if ESX.Items[name] then + return + end + + MySQL.insert.await("INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)", { + name, + label, + weight, + rare or false, + canRemove or false, + }) + + ESX.Items[name] = { + label = label, + weight = weight, + rare = rare or false, + canRemove = canRemove or false, + } + + refreshPlayerInventories() + end end ---@param job string diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 628e85ea..27cd5959 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -438,6 +438,10 @@ ESX.RegisterCommand("refreshjobs", "admin", function() ESX.RefreshJobs() end, true, { help = TranslateCap("command_clearall") }) +ESX.RegisterCommand("refreshitems", "admin", function() + ESX.RefreshItems() +end, true, { help = TranslateCap("command_clearall") }) + if not Config.CustomInventory then ESX.RegisterCommand( "clearinventory", From 4ce2864e88c04d3314d26d4f678106cae2268d4e Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 13 Jul 2025 10:23:49 +0200 Subject: [PATCH 2/8] fix(es_extended/server/functions): add correct default values for addItem --- [core]/es_extended/server/functions.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index f7176d4a..62d9cf10 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -677,16 +677,20 @@ if not Config.CustomInventory then ---@param name string ---@param label string - ---@param weight number + ---@param weight number? ---@param rare boolean? ---@param canRemove boolean? function ESX.AddItem(name, label, weight, rare, canRemove) assert(type(name) == "string", "Item name must be a string") assert(type(label) == "string", "Item label must be a string") - assert(type(weight) == "number", "Item weight must be a number") + assert(type(weight) == "number" or weight == nil, "Item weight must be a number or nil") assert(type(rare) == "boolean" or rare == nil, "Item rare must be a boolean or nil") assert(type(canRemove) == "boolean" or canRemove == nil, "Item canRemove must be a boolean or nil") + weight = weight or 1 + rare = rare or false + canRemove = canRemove ~= false + if ESX.Items[name] then return end @@ -695,15 +699,15 @@ if not Config.CustomInventory then name, label, weight, - rare or false, - canRemove or false, + rare, + canRemove, }) ESX.Items[name] = { label = label, weight = weight, - rare = rare or false, - canRemove = canRemove or false, + rare = rare, + canRemove = canRemove, } refreshPlayerInventories() From 2e5c761607009fc14ea3cb456f1b4d3266140bc3 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 13 Jul 2025 10:37:58 +0200 Subject: [PATCH 3/8] feat(es_extended/server/functions): replace AddItem with AddItems --- [core]/es_extended/server/functions.lua | 88 ++++++++++++++++--------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 62d9cf10..c4551f08 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -675,42 +675,70 @@ if not Config.CustomInventory then refreshPlayerInventories() end - ---@param name string - ---@param label string - ---@param weight number? - ---@param rare boolean? - ---@param canRemove boolean? - function ESX.AddItem(name, label, weight, rare, canRemove) - assert(type(name) == "string", "Item name must be a string") - assert(type(label) == "string", "Item label must be a string") - assert(type(weight) == "number" or weight == nil, "Item weight must be a number or nil") - assert(type(rare) == "boolean" or rare == nil, "Item rare must be a boolean or nil") - assert(type(canRemove) == "boolean" or canRemove == nil, "Item canRemove must be a boolean or nil") + ---@param items { name: string, label: string, weight?: number, rare?: boolean, canRemove?: boolean }[] + function ESX.AddItems(items) + local toInsert = {} - weight = weight or 1 - rare = rare or false - canRemove = canRemove ~= false + for _, item in ipairs(items) do + local name = item.name + local label = item.label + local weight = item.weight or 1 + local rare = item.rare or false + local canRemove = item.canRemove ~= false - if ESX.Items[name] then - return + if type(name) ~= "string" then + print(("^1[AddItems]^0 Invalid item name: %s"):format(name)) + goto continue + end + + if type(label) ~= "string" then + print(("^1[AddItems]^0 Invalid label for item '%s'"):format(name)) + goto continue + end + + if type(weight) ~= "number" then + print(("^1[AddItems]^0 Invalid weight for item '%s'"):format(name)) + goto continue + end + + if type(rare) ~= "boolean" then + print(("^1[AddItems]^0 Invalid rare flag for item '%s'"):format(name)) + goto continue + end + + if type(canRemove) ~= "boolean" then + print(("^1[AddItems]^0 Invalid canRemove flag for item '%s'"):format(name)) + goto continue + end + + if not ESX.Items[name] then + toInsert[#toInsert + 1] = { + name, + label, + weight, + rare, + canRemove, + } + end + + ::continue:: end - MySQL.insert.await("INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)", { - name, - label, - weight, - rare, - canRemove, - }) + if #toInsert > 0 then + MySQL.prepare("INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)", toInsert) - ESX.Items[name] = { - label = label, - weight = weight, - rare = rare, - canRemove = canRemove, - } + for _, row in ipairs(toInsert) do + local name, label, weight, rare, canRemove = table.unpack(row) + ESX.Items[name] = { + label = label, + weight = weight, + rare = rare, + canRemove = canRemove, + } + end - refreshPlayerInventories() + refreshPlayerInventories() + end end end From a903a6d7a2455a8b1a969ee200737054ab060798 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:11:33 +0200 Subject: [PATCH 4/8] fix(es_extended/server/modules/commands): cmd only for esx inv --- [core]/es_extended/server/modules/commands.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 27cd5959..9f7c551a 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -438,11 +438,11 @@ ESX.RegisterCommand("refreshjobs", "admin", function() ESX.RefreshJobs() end, true, { help = TranslateCap("command_clearall") }) -ESX.RegisterCommand("refreshitems", "admin", function() - ESX.RefreshItems() -end, true, { help = TranslateCap("command_clearall") }) - if not Config.CustomInventory then + ESX.RegisterCommand("refreshitems", "admin", function() + ESX.RefreshItems() + end, true, { help = TranslateCap("command_clearall") }) + ESX.RegisterCommand( "clearinventory", "admin", From a95427830caedae8e9e8eda2effd2ef5023a6bb7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:43:50 +0200 Subject: [PATCH 5/8] refactor(es_extended/server/functions): implement requested changes --- [core]/es_extended/locales/en.lua | 2 + [core]/es_extended/server/functions.lua | 54 +++++++++++-------- .../es_extended/server/modules/commands.lua | 6 ++- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/[core]/es_extended/locales/en.lua b/[core]/es_extended/locales/en.lua index 194d2912..b10966a9 100644 --- a/[core]/es_extended/locales/en.lua +++ b/[core]/es_extended/locales/en.lua @@ -65,6 +65,8 @@ return { ["command_repair_success_target"] = "An admin repaired your vehicle", ["command_clear"] = "Clear chat Text", ["command_clearall"] = "Clear chat Text for all players", + ["command_refreshitems"] = "Reload all items from the database.", + ["command_refreshitems_success"] = "Successfully reloaded %d items.", ["command_clearinventory"] = "Remove All items from the Players Inventory", ["command_clearloadout"] = "Remove All weapons from the Players Loadout", ["command_freeze"] = "Freeze a player", diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index c4551f08..f8db0d37 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -637,7 +637,9 @@ if not Config.CustomInventory then end local function refreshPlayerInventories() - for i, xPlayer in ipairs(ESX.GetExtendedPlayers()) do + local xPlayers = ESX.GetExtendedPlayers() + for i = 1, #xPlayers do + local xPlayer = xPlayers[i] local minimalInv = xPlayer.getInventory(true) for itemName, itemCount in pairs(minimalInv) do @@ -648,8 +650,9 @@ if not Config.CustomInventory then end xPlayer.inventory = {} + local playerInvIndex = 1 for itemName, itemData in pairs(ESX.Items) do - xPlayer.inventory[#xPlayer.inventory+1] = { + xPlayer.inventory[playerInvIndex] = { name = itemName, count = minimalInv[itemName] or 0, label = itemData.label, @@ -658,6 +661,7 @@ if not Config.CustomInventory then rare = itemData.rare, canRemove = itemData.canRemove, } + playerInvIndex += 1 end TriggerClientEvent("esx:setInventory", xPlayer.source, xPlayer.inventory) @@ -668,16 +672,17 @@ if not Config.CustomInventory then ESX.Items = {} local items = MySQL.query.await("SELECT * FROM items") - for _, v in ipairs(items) do - ESX.Items[v.name] = { label = v.label, weight = v.weight, rare = v.rare, canRemove = v.can_remove } + for i = 1, #items do + local item = items[i] + ESX.Items[item.name] = { label = item.label, weight = item.weight, rare = item.rare, canRemove = item.can_remove } end - - refreshPlayerInventories() + refreshPlayerInventories() end ---@param items { name: string, label: string, weight?: number, rare?: boolean, canRemove?: boolean }[] function ESX.AddItems(items) local toInsert = {} + local toInsertIndex = 1 for _, item in ipairs(items) do local name = item.name @@ -691,6 +696,10 @@ if not Config.CustomInventory then goto continue end + if ESX.Items[name] then + goto continue + end + if type(label) ~= "string" then print(("^1[AddItems]^0 Invalid label for item '%s'"):format(name)) goto continue @@ -711,29 +720,28 @@ if not Config.CustomInventory then goto continue end - if not ESX.Items[name] then - toInsert[#toInsert + 1] = { - name, - label, - weight, - rare, - canRemove, - } - end + toInsert[toInsertIndex] = { + name = name, + label = label, + weight = weight, + rare = rare, + canRemove = canRemove, + } + toInsertIndex += 1 ::continue:: end if #toInsert > 0 then - MySQL.prepare("INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)", toInsert) + MySQL.prepare.await("INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)", toInsert) - for _, row in ipairs(toInsert) do - local name, label, weight, rare, canRemove = table.unpack(row) - ESX.Items[name] = { - label = label, - weight = weight, - rare = rare, - canRemove = canRemove, + for i = 1, #toInsert do + local row = toInsert[i] + ESX.Items[row.name] = { + label = row.label, + weight = row.weight, + rare = row.rare, + canRemove = row.canRemove, } end diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 9f7c551a..673ced1d 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -439,9 +439,11 @@ ESX.RegisterCommand("refreshjobs", "admin", function() end, true, { help = TranslateCap("command_clearall") }) if not Config.CustomInventory then - ESX.RegisterCommand("refreshitems", "admin", function() + ESX.RegisterCommand("refreshitems", "admin", function(xPlayer) ESX.RefreshItems() - end, true, { help = TranslateCap("command_clearall") }) + + xPlayer.showNotification(TranslateCap("command_refreshitems_success", #ESX.Items), true, false, 140) + end, true, { help = TranslateCap("command_refreshitems") }) ESX.RegisterCommand( "clearinventory", From 71143e96d88f58041aa97bbdc129129ccc9d66a7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:47:40 +0200 Subject: [PATCH 6/8] fix(es_extended/server/modules/commands): use translate --- [core]/es_extended/server/modules/commands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 673ced1d..8c96ddaa 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -442,7 +442,7 @@ if not Config.CustomInventory then ESX.RegisterCommand("refreshitems", "admin", function(xPlayer) ESX.RefreshItems() - xPlayer.showNotification(TranslateCap("command_refreshitems_success", #ESX.Items), true, false, 140) + xPlayer.showNotification(Translate("command_refreshitems_success", #ESX.Items), true, false, 140) end, true, { help = TranslateCap("command_refreshitems") }) ESX.RegisterCommand( From 60e1d7e7c810dc88313fc5a09fc8e882dd223362 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:50:11 +0200 Subject: [PATCH 7/8] refactor(es_extended/server/functions): use numeric loop --- [core]/es_extended/server/functions.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index f8db0d37..93fdd19a 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -684,7 +684,8 @@ if not Config.CustomInventory then local toInsert = {} local toInsertIndex = 1 - for _, item in ipairs(items) do + for i = 1, #items do + local item = items[i] local name = item.name local label = item.label local weight = item.weight or 1 From 513003fe08ce44c9a21edc55eea022ccf7dea418 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:58:30 +0200 Subject: [PATCH 8/8] fix(es_extended/server/functions): return new item count on refresh --- [core]/es_extended/server/functions.lua | 6 +++++- [core]/es_extended/server/modules/commands.lua | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 93fdd19a..e1d7a8bd 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -668,15 +668,19 @@ if not Config.CustomInventory then end end + ---@return number newItemCount function ESX.RefreshItems() ESX.Items = {} local items = MySQL.query.await("SELECT * FROM items") - for i = 1, #items do + local itemCount = #items + for i = 1, itemCount do local item = items[i] ESX.Items[item.name] = { label = item.label, weight = item.weight, rare = item.rare, canRemove = item.can_remove } end refreshPlayerInventories() + + return itemCount end ---@param items { name: string, label: string, weight?: number, rare?: boolean, canRemove?: boolean }[] diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 8c96ddaa..a8c7d6a6 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -440,9 +440,9 @@ end, true, { help = TranslateCap("command_clearall") }) if not Config.CustomInventory then ESX.RegisterCommand("refreshitems", "admin", function(xPlayer) - ESX.RefreshItems() + local itemCount = ESX.RefreshItems() - xPlayer.showNotification(Translate("command_refreshitems_success", #ESX.Items), true, false, 140) + xPlayer.showNotification(Translate("command_refreshitems_success", itemCount), true, false, 140) end, true, { help = TranslateCap("command_refreshitems") }) ESX.RegisterCommand(