From 8797ceb7816c2b5afed15e5a17a8e91b70fae10e Mon Sep 17 00:00:00 2001 From: Mirow Date: Sat, 6 Sep 2025 20:18:00 +0200 Subject: [PATCH 1/6] chore(esx_addoninventory): update manifest --- .../esx_addoninventory/fxmanifest.lua | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/[esx_addons]/esx_addoninventory/fxmanifest.lua b/[esx_addons]/esx_addoninventory/fxmanifest.lua index 8c5f72b0..d4158411 100644 --- a/[esx_addons]/esx_addoninventory/fxmanifest.lua +++ b/[esx_addons]/esx_addoninventory/fxmanifest.lua @@ -1,23 +1,22 @@ -fx_version 'adamant' - +fx_version 'cerulean' game 'gta5' - -description 'Adds a way for resources to store items for players' lua54 'yes' +use_fxv2_oal 'yes' -version '1.0' +author 'ESX-Framework' +description 'Adds a way for resources to store items for players.' +version '1.1' legacyversion '1.13.4' server_scripts { '@es_extended/imports.lua', '@oxmysql/lib/MySQL.lua', 'server/classes/addoninventory.lua', + 'server/database.lua', 'server/main.lua' } -server_exports { - 'GetSharedInventory', - 'AddSharedInventory' -} - -dependency 'es_extended' +-- server_exports { +-- 'GetSharedInventory', +-- 'AddSharedInventory' +-- } From e4ba7e8254e5a968ba4965d302b8aa20f7dc1d96 Mon Sep 17 00:00:00 2001 From: Mirow Date: Sat, 6 Sep 2025 20:26:21 +0200 Subject: [PATCH 2/6] feat(esx_addoninventory/addoninventory): add type annotations Added type annotations and updated functions. Prepared for saving items on restart. --- .../server/classes/addoninventory.lua | 98 +++++++------------ .../esx_addoninventory/server/main.lua | 46 +++++---- 2 files changed, 58 insertions(+), 86 deletions(-) diff --git a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua index 4725788f..8843dda0 100644 --- a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua +++ b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua @@ -1,85 +1,59 @@ +---@alias AddonInventoryItem { name: string, count: number, label: string } + +---@class AddonInventory +---@field name string +---@field owner? string +---@field items table +---@field addItem fun(itemName: string, count: number) +---@field removeItem fun(itemName: string, count: number) +---@field setItem fun(itemName: string, count: number) +---@field getItem fun(itemName: string): AddonInventoryItem +---@field saveItem function + +---@param name string +---@param owner? string +---@param items table +---@return AddonInventory function CreateAddonInventory(name, owner, items) - local self = {} + ---@diagnostic disable-next-line: missing-fields + local self = {} --[[@type AddonInventory]] self.name = name self.owner = owner self.items = items - function self.addItem(name, count) - local item = self.getItem(name) - item.count = item.count + count - - self.saveItem(name, item.count) + function self.addItem(itemName, count) + local item = self.getItem(itemName) + item.count += count end - function self.removeItem(name, count) - if count > 0 then - local item = self.getItem(name) - item.count = item.count - count - - self.saveItem(name, item.count) - end + function self.removeItem(itemName, count) + if count <= 0 then return end + + local item = self.getItem(itemName) + item.count = math.max(0, item.count - count) end - function self.setItem(name, count) - local item = self.getItem(name) + function self.setItem(itemName, count) + local item = self.getItem(itemName) item.count = count - - self.saveItem(name, item.count) end - function self.getItem(name) - for i=1, #self.items, 1 do - if self.items[i].name == name then - return self.items[i] - end - end + function self.getItem(itemName) + local existingItem = self.items[itemName] + if existingItem then return existingItem end - item = { - name = name, + self.items[itemName] = { + name = itemName, count = 0, - label = Items[name] + label = Items[itemName] } - table.insert(self.items, item) - - if self.owner == nil then - MySQL.update('INSERT INTO addon_inventory_items (inventory_name, name, count) VALUES (@inventory_name, @item_name, @count)', - { - ['@inventory_name'] = self.name, - ['@item_name'] = name, - ['@count'] = 0 - }) - else - MySQL.update('INSERT INTO addon_inventory_items (inventory_name, name, count, owner) VALUES (@inventory_name, @item_name, @count, @owner)', - { - ['@inventory_name'] = self.name, - ['@item_name'] = name, - ['@count'] = 0, - ['@owner'] = self.owner - }) - end - - return item + return self.items[itemName] end - function self.saveItem(name, count) - if self.owner == nil then - MySQL.update('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name', { - ['@inventory_name'] = self.name, - ['@item_name'] = name, - ['@count'] = count - }) - else - MySQL.update('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name AND owner = @owner', { - ['@inventory_name'] = self.name, - ['@item_name'] = name, - ['@count'] = count, - ['@owner'] = self.owner - }) - end + function self.saveItem(itemName, count) end return self end - diff --git a/[esx_addons]/esx_addoninventory/server/main.lua b/[esx_addons]/esx_addoninventory/server/main.lua index b1818c56..f818fcc2 100644 --- a/[esx_addons]/esx_addoninventory/server/main.lua +++ b/[esx_addons]/esx_addoninventory/server/main.lua @@ -3,7 +3,7 @@ if ESX.GetConfig().OxInventory then if resourceName == 'ox_inventory' or resourceName == GetCurrentResourceName() then local stashes = MySQL.query.await('SELECT * FROM addon_inventory') - for i=1, #stashes do + for i = 1, #stashes do local stash = stashes[i] local jobStash = stash.name:find('society') and string.sub(stash.name, 9) exports.ox_inventory:RegisterStash(stash.name, stash.label, 100, 200000, stash.shared == 0 and true or false, jobStash) @@ -20,29 +20,28 @@ local InventoriesIndex, Inventories, SharedInventories = {}, {}, {} MySQL.ready(function() local items = MySQL.query.await('SELECT * FROM items') - for i=1, #items, 1 do + for i = 1, #items, 1 do Items[items[i].name] = items[i].label end local result = MySQL.query.await('SELECT * FROM addon_inventory') - for i=1, #result, 1 do - local name = result[i].name - local label = result[i].label - local shared = result[i].shared + for i = 1, #result, 1 do + local name = result[i].name + local label = result[i].label + local shared = result[i].shared local result2 = MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = @inventory_name', { ['@inventory_name'] = name }) if shared == 0 then - table.insert(InventoriesIndex, name) Inventories[name] = {} local items = {} - for j=1, #result2, 1 do + for j = 1, #result2, 1 do local itemName = result2[j].name local itemCount = result2[j].count local itemOwner = result2[j].owner @@ -58,15 +57,14 @@ MySQL.ready(function() }) end - for k,v in pairs(items) do + for k, v in pairs(items) do local addonInventory = CreateAddonInventory(name, k, v) table.insert(Inventories[name], addonInventory) end - else local items = {} - for j=1, #result2, 1 do + for j = 1, #result2, 1 do table.insert(items, { name = result2[j].name, count = result2[j].count, @@ -74,15 +72,15 @@ MySQL.ready(function() }) end - local addonInventory = CreateAddonInventory(name, nil, items) - SharedInventories[name] = addonInventory + local addonInventory = CreateAddonInventory(name, nil, items) + SharedInventories[name] = addonInventory GlobalState.SharedInventories = SharedInventories end end end) function GetInventory(name, owner) - for i=1, #Inventories[name], 1 do + for i = 1, #Inventories[name], 1 do if Inventories[name][i].owner == owner then return Inventories[name][i] end @@ -94,17 +92,17 @@ function GetSharedInventory(name) end function AddSharedInventory(society) - if type(society) ~= 'table' or not society?.name or not society?.label then return end - -- society (array) containing name (string) and label (string) + if type(society) ~= 'table' or not society?.name or not society?.label then return end + -- society (array) containing name (string) and label (string) - -- addon inventory: - MySQL.Async.execute('INSERT INTO addon_inventory (name, label, shared) VALUES (@name, @label, @shared)', { - ['name'] = society.name, - ['label'] = society.label, - ['shared'] = 1 - }) + -- addon inventory: + MySQL.Async.execute('INSERT INTO addon_inventory (name, label, shared) VALUES (@name, @label, @shared)', { + ['name'] = society.name, + ['label'] = society.label, + ['shared'] = 1 + }) - SharedInventories[society.name] = CreateAddonInventory(society.name, nil, {}) + SharedInventories[society.name] = CreateAddonInventory(society.name, nil, {}) end AddEventHandler('esx_addoninventory:getInventory', function(name, owner, cb) @@ -118,7 +116,7 @@ end) AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) local addonInventories = {} - for i=1, #InventoriesIndex, 1 do + for i = 1, #InventoriesIndex, 1 do local name = InventoriesIndex[i] local inventory = GetInventory(name, xPlayer.identifier) From 0a8399d85035d3ab942854cd3a4caab6731e2ae1 Mon Sep 17 00:00:00 2001 From: Mirow Date: Sat, 6 Sep 2025 20:27:09 +0200 Subject: [PATCH 3/6] refactor(esx_addominventory/addoninventory): use `ESX.GetItemLabel` --- .../esx_addoninventory/server/classes/addoninventory.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua index 8843dda0..344d810a 100644 --- a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua +++ b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua @@ -46,7 +46,7 @@ function CreateAddonInventory(name, owner, items) self.items[itemName] = { name = itemName, count = 0, - label = Items[itemName] + label = ESX.GetItemLabel(itemName) } return self.items[itemName] From 62964a0c5cda495040572e8554770b6a91075348 Mon Sep 17 00:00:00 2001 From: Mirow Date: Sun, 7 Sep 2025 19:18:07 +0200 Subject: [PATCH 4/6] feat(esx_addoninventory/database): add database module --- .../esx_addoninventory/server/database.lua | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 [esx_addons]/esx_addoninventory/server/database.lua diff --git a/[esx_addons]/esx_addoninventory/server/database.lua b/[esx_addons]/esx_addoninventory/server/database.lua new file mode 100644 index 00000000..b6eb6277 --- /dev/null +++ b/[esx_addons]/esx_addoninventory/server/database.lua @@ -0,0 +1,35 @@ +Database = {} + +---@class DatabaseInventoryRow +---@field name string +---@field label string +---@field shared number + +---@class DatabaseInventoryItemRow +---@field id number +---@field inventory_name string +---@field name string +---@field count number +---@field owner? string + +---@param name string +---@param shared number +---@return DatabaseInventoryRow? +function Database.fetchInventory(name, shared) + return MySQL.single.await('SELECT * FROM addon_inventory WHERE name = ? AND shared = ?', { name, shared }) +end + +---@param name string +---@param owner? string +---@return DatabaseInventoryItemRow[] +function Database.fetchInventoryItems(name, owner) + if owner then + return MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = ? AND owner = ?', { name, owner }) + end + + return MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = ?', { name }) +end + +function Database.saveInventories() + warn('Not implemented yet.') +end From e48e952b683cac9018f51fb042099b866909c9d7 Mon Sep 17 00:00:00 2001 From: Mirow Date: Sun, 7 Sep 2025 19:21:16 +0200 Subject: [PATCH 5/6] refactor(esx_addoninventory): format items --- .../server/classes/addoninventory.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua index 344d810a..a0425fe3 100644 --- a/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua +++ b/[esx_addons]/esx_addoninventory/server/classes/addoninventory.lua @@ -20,7 +20,16 @@ function CreateAddonInventory(name, owner, items) self.name = name self.owner = owner - self.items = items + self.items = {} + + for _, item in ipairs(items) do + local itemName = item.name + self.items[itemName] = { + name = itemName, + count = item.count, + label = ESX.GetItemLabel(itemName), + } + end function self.addItem(itemName, count) local item = self.getItem(itemName) From 8fbd8b313e92cc42db9379eb9c69bcd7e0c8eba8 Mon Sep 17 00:00:00 2001 From: Mirow Date: Sun, 7 Sep 2025 19:27:21 +0200 Subject: [PATCH 6/6] refactor(esx_addoninventory): rewrite getAccount and getSharedAccount fn --- .../esx_addoninventory/server/database.lua | 4 +- .../esx_addoninventory/server/main.lua | 286 ++++++++++-------- 2 files changed, 165 insertions(+), 125 deletions(-) diff --git a/[esx_addons]/esx_addoninventory/server/database.lua b/[esx_addons]/esx_addoninventory/server/database.lua index b6eb6277..be88891d 100644 --- a/[esx_addons]/esx_addoninventory/server/database.lua +++ b/[esx_addons]/esx_addoninventory/server/database.lua @@ -13,10 +13,10 @@ Database = {} ---@field owner? string ---@param name string ----@param shared number +---@param shared? boolean ---@return DatabaseInventoryRow? function Database.fetchInventory(name, shared) - return MySQL.single.await('SELECT * FROM addon_inventory WHERE name = ? AND shared = ?', { name, shared }) + return MySQL.single.await('SELECT * FROM addon_inventory WHERE name = ? AND shared = ?', { name, shared and 1 or 0 }) end ---@param name string diff --git a/[esx_addons]/esx_addoninventory/server/main.lua b/[esx_addons]/esx_addoninventory/server/main.lua index f818fcc2..0f6f09e8 100644 --- a/[esx_addons]/esx_addoninventory/server/main.lua +++ b/[esx_addons]/esx_addoninventory/server/main.lua @@ -1,132 +1,172 @@ -if ESX.GetConfig().OxInventory then - AddEventHandler('onServerResourceStart', function(resourceName) - if resourceName == 'ox_inventory' or resourceName == GetCurrentResourceName() then - local stashes = MySQL.query.await('SELECT * FROM addon_inventory') - - for i = 1, #stashes do - local stash = stashes[i] - local jobStash = stash.name:find('society') and string.sub(stash.name, 9) - exports.ox_inventory:RegisterStash(stash.name, stash.label, 100, 200000, stash.shared == 0 and true or false, jobStash) - end - end - end) - - return -end - -Items = {} -local InventoriesIndex, Inventories, SharedInventories = {}, {}, {} - -MySQL.ready(function() - local items = MySQL.query.await('SELECT * FROM items') - - for i = 1, #items, 1 do - Items[items[i].name] = items[i].label +Inventories, SharedInventories = {}, {} + +---@param name string +---@param owner string +---@return AddonInventory? +local function getInventory(name, owner) + -- for i = 1, #Inventories[name], 1 do + -- if Inventories[name][i].owner == owner then + -- return Inventories[name][i] + -- end + -- end + local existingInventory = Inventories[name]?[owner] + if existingInventory then return existingInventory end + + local dbInventory, dbItems = Database.fetchInventory(name, false), Database.fetchInventoryItems(name, owner) + if not dbInventory or not dbItems then return end + + if not Inventories[name] then + Inventories[name] = {} end - local result = MySQL.query.await('SELECT * FROM addon_inventory') - - for i = 1, #result, 1 do - local name = result[i].name - local label = result[i].label - local shared = result[i].shared - - local result2 = MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = @inventory_name', { - ['@inventory_name'] = name - }) - - if shared == 0 then - table.insert(InventoriesIndex, name) - - Inventories[name] = {} - local items = {} - - for j = 1, #result2, 1 do - local itemName = result2[j].name - local itemCount = result2[j].count - local itemOwner = result2[j].owner - - if items[itemOwner] == nil then - items[itemOwner] = {} - end - - table.insert(items[itemOwner], { - name = itemName, - count = itemCount, - label = Items[itemName] - }) - end - - for k, v in pairs(items) do - local addonInventory = CreateAddonInventory(name, k, v) - table.insert(Inventories[name], addonInventory) - end - else - local items = {} - - for j = 1, #result2, 1 do - table.insert(items, { - name = result2[j].name, - count = result2[j].count, - label = Items[result2[j].name] - }) - end - - local addonInventory = CreateAddonInventory(name, nil, items) - SharedInventories[name] = addonInventory - GlobalState.SharedInventories = SharedInventories - end - end -end) + Inventories[name][owner] = CreateAddonInventory(name, owner, dbItems) -function GetInventory(name, owner) - for i = 1, #Inventories[name], 1 do - if Inventories[name][i].owner == owner then - return Inventories[name][i] - end - end + return Inventories[name][owner] end -function GetSharedInventory(name) - return SharedInventories[name] -end +---@param name string +---@return AddonInventory? +local function getSharedInventory(name) + local existingInventory = SharedInventories[name] + if existingInventory then return existingInventory end -function AddSharedInventory(society) - if type(society) ~= 'table' or not society?.name or not society?.label then return end - -- society (array) containing name (string) and label (string) + local dbAccount, dbItems = Database.fetchInventory(name, true), Database.fetchInventoryItems(name) + if not dbAccount or not dbItems then return end - -- addon inventory: - MySQL.Async.execute('INSERT INTO addon_inventory (name, label, shared) VALUES (@name, @label, @shared)', { - ['name'] = society.name, - ['label'] = society.label, - ['shared'] = 1 - }) + SharedInventories[name] = CreateAddonInventory(name, nil, dbItems) - SharedInventories[society.name] = CreateAddonInventory(society.name, nil, {}) + GlobalState.SharedInventories = SharedInventories + return SharedInventories[name] end - -AddEventHandler('esx_addoninventory:getInventory', function(name, owner, cb) - cb(GetInventory(name, owner)) -end) - -AddEventHandler('esx_addoninventory:getSharedInventory', function(name, cb) - cb(GetSharedInventory(name)) -end) - -AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) - local addonInventories = {} - - for i = 1, #InventoriesIndex, 1 do - local name = InventoriesIndex[i] - local inventory = GetInventory(name, xPlayer.identifier) - - if inventory == nil then - inventory = CreateAddonInventory(name, xPlayer.identifier, {}) - table.insert(Inventories[name], inventory) - end - - table.insert(addonInventories, inventory) - end - - xPlayer.set('addonInventories', addonInventories) -end) +-- if ESX.GetConfig().OxInventory then +-- AddEventHandler('onServerResourceStart', function(resourceName) +-- if resourceName == 'ox_inventory' or resourceName == GetCurrentResourceName() then +-- local stashes = MySQL.query.await('SELECT * FROM addon_inventory') + +-- for i = 1, #stashes do +-- local stash = stashes[i] +-- local jobStash = stash.name:find('society') and string.sub(stash.name, 9) +-- exports.ox_inventory:RegisterStash(stash.name, stash.label, 100, 200000, stash.shared == 0 and true or false, jobStash) +-- end +-- end +-- end) + +-- return +-- end + +-- Items = {} +-- local InventoriesIndex, Inventories, SharedInventories = {}, {}, {} + +-- MySQL.ready(function() +-- local items = MySQL.query.await('SELECT * FROM items') + +-- for i = 1, #items, 1 do +-- Items[items[i].name] = items[i].label +-- end + +-- local result = MySQL.query.await('SELECT * FROM addon_inventory') + +-- for i = 1, #result, 1 do +-- local name = result[i].name +-- local label = result[i].label +-- local shared = result[i].shared + +-- local result2 = MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = @inventory_name', { +-- ['@inventory_name'] = name +-- }) + +-- if shared == 0 then +-- table.insert(InventoriesIndex, name) + +-- Inventories[name] = {} +-- local items = {} + +-- for j = 1, #result2, 1 do +-- local itemName = result2[j].name +-- local itemCount = result2[j].count +-- local itemOwner = result2[j].owner + +-- if items[itemOwner] == nil then +-- items[itemOwner] = {} +-- end + +-- table.insert(items[itemOwner], { +-- name = itemName, +-- count = itemCount, +-- label = Items[itemName] +-- }) +-- end + +-- for k, v in pairs(items) do +-- local addonInventory = CreateAddonInventory(name, k, v) +-- table.insert(Inventories[name], addonInventory) +-- end +-- else +-- local items = {} + +-- for j = 1, #result2, 1 do +-- table.insert(items, { +-- name = result2[j].name, +-- count = result2[j].count, +-- label = Items[result2[j].name] +-- }) +-- end + +-- local addonInventory = CreateAddonInventory(name, nil, items) +-- SharedInventories[name] = addonInventory +-- GlobalState.SharedInventories = SharedInventories +-- end +-- end +-- end) + +-- function GetInventory(name, owner) +-- for i = 1, #Inventories[name], 1 do +-- if Inventories[name][i].owner == owner then +-- return Inventories[name][i] +-- end +-- end +-- end + +-- function GetSharedInventory(name) +-- return SharedInventories[name] +-- end + +-- function AddSharedInventory(society) +-- if type(society) ~= 'table' or not society?.name or not society?.label then return end +-- -- society (array) containing name (string) and label (string) + +-- -- addon inventory: +-- MySQL.Async.execute('INSERT INTO addon_inventory (name, label, shared) VALUES (@name, @label, @shared)', { +-- ['name'] = society.name, +-- ['label'] = society.label, +-- ['shared'] = 1 +-- }) + +-- SharedInventories[society.name] = CreateAddonInventory(society.name, nil, {}) +-- end + +-- AddEventHandler('esx_addoninventory:getInventory', function(name, owner, cb) +-- cb(GetInventory(name, owner)) +-- end) + +-- AddEventHandler('esx_addoninventory:getSharedInventory', function(name, cb) +-- cb(GetSharedInventory(name)) +-- end) + +-- AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) +-- local addonInventories = {} + +-- for i = 1, #InventoriesIndex, 1 do +-- local name = InventoriesIndex[i] +-- local inventory = GetInventory(name, xPlayer.identifier) + +-- if inventory == nil then +-- inventory = CreateAddonInventory(name, xPlayer.identifier, {}) +-- table.insert(Inventories[name], inventory) +-- end + +-- table.insert(addonInventories, inventory) +-- end + +-- xPlayer.set('addonInventories', addonInventories) +-- end)