Installations

Remember that this resource has verified DMCA protection, and its illegal use or distribution could imply a claim for protection of Title 17 of Chapter 512 (c) (3) of the Digital Millennium Copyright Act

STEP 1 - Dependencies

It is important to use the dependencies that are indicated in this step, do not use others, otherwise you will receive critical errors
Quasar Inventory only has two dependencies, which are dpclothing and progressbar, download them by clicking on the following buttons

STEP 2 - Start order

Remember that you must place scripts with usable items below qs-inventory in server.cfg, otherwise they will never work!
It is important to put the resources in the correct order, below es_extened but above the other scripts
In order for the resource to start correctly and not receive any errors, we must start the resources in the following order
1
ensure qs-core
2
ensure qs-inventory
3
ensure qs-shops

STEP 3 - Database

Before executing the full sql, we will use the following UPDATE TABLE in our database
Do not continue without completing this first step, otherwise you will receive errors about player = nil...
Change the value of YOUR_DB_NAME to the name of your database
Inventory column cleaner
1
UPDATE YOUR_DB_NAME.users SET inventory = '[]' WHERE identifier IS NOT NULL
Do not start the resource without first having installed the complete database, if you start the resource in the database, you will get critical errors
Inventory database
1
CREATE TABLE IF NOT EXISTS `qs_glovebox` (
2
`id` int(11) NOT NULL AUTO_INCREMENT,
3
`plate` varchar(255) DEFAULT NULL,
4
`items` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
5
PRIMARY KEY (`id`),
6
KEY `plate` (`plate`)
7
) ENGINE = InnoDB AUTO_INCREMENT = 9 DEFAULT CHARSET = latin1;
8
9
CREATE TABLE IF NOT EXISTS `qs_ammo` (
10
`id` int(11) NOT NULL AUTO_INCREMENT,
11
`identifier` varchar(255) NOT NULL,
12
`ammo` text DEFAULT NULL,
13
PRIMARY KEY (`id`),
14
KEY `citizenid` (`identifier`) USING BTREE
15
) ENGINE = InnoDB AUTO_INCREMENT = 3638 DEFAULT CHARSET = latin1;
16
17
CREATE TABLE IF NOT EXISTS `qs_stash` (
18
`id` int(11) NOT NULL AUTO_INCREMENT,
19
`stash` varchar(255) NOT NULL,
20
`items` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
21
PRIMARY KEY (`id`),
22
KEY `stash` (`stash`)
23
) ENGINE = InnoDB AUTO_INCREMENT = 40 DEFAULT CHARSET = latin1;
24
25
CREATE TABLE IF NOT EXISTS `qs_trunk` (
26
`id` int(11) NOT NULL AUTO_INCREMENT,
27
`plate` varchar(255) NOT NULL,
28
`items` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
29
PRIMARY KEY (`id`),
30
KEY `plate` (`plate`)
31
) ENGINE = InnoDB AUTO_INCREMENT = 1141 DEFAULT CHARSET = latin1;
32
33
CREATE TABLE IF NOT EXISTS `qs_customshops` (
34
`cid` varchar(50) DEFAULT NULL,
35
`shopname` varchar(50) DEFAULT NULL,
36
`shopdata` longtext DEFAULT NULL
37
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
38
39
/*!40000 ALTER TABLE `qs_trunk` DISABLE KEYS */;
40
/*!40000 ALTER TABLE `qs_trunk` ENABLE KEYS */;
41
42
ALTER TABLE `users` ADD `metadata` mediumtext DEFAULT NULL;

STEP 4 - Modifications

The longest and most tedious step within the resource is the modification of es_extended, so we also give you the opportunity to use the following pre-modified versions, but if you receive any errors, you should install the modifications yourself by following the steps below

Client modifications

Modifications of es_extended on the client side
ESX OLD
ESX LEGACY

Delete event

We will remove the following event to avoid the use of the inventory default
../client/main.lua
1
Citizen.CreateThread(function()
2
while true do
3
Citizen.Wait(0)
4
if IsControlJustReleased(0, 289) then
5
if IsInputDisabled(0) and not isDead and not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then
6
ESX.ShowInventory()
7
end
8
end
9
end
10
end)
In the new versions of es_extended, we will have a configuration inside es_extended/config.lua to disable the Inventory default
ESX OLD
ESX LEGACY

Add event

We will add this between the first lines of the file
../client/main.lua
1
RegisterNetEvent('qs-core:setPlayerData')
2
AddEventHandler('qs-core:setPlayerData', function(data)
3
local Inventory = data.items
4
for _, slot in pairs(Inventory) do
5
Inventory[_].count = Inventory[_].amount
6
end
7
ESX.PlayerData.inventory = Inventory
8
end)

Add event

We will add this between the first lines of the file
../client/main.lua
1
RegisterNetEvent('qs-core:setPlayerData')
2
AddEventHandler('qs-core:setPlayerData', function(data)
3
local Inventory = data.items
4
for _, slot in pairs(Inventory) do
5
Inventory[_].count = Inventory[_].amount
6
end
7
ESX.PlayerData.inventory = Inventory
8
end)
ALL ESX

Replace events

This step is optional in case you receive errors about GetClosestObject
../client/functions.lua
1
ESX.Game.GetClosestObject = function(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetObjects(), false, coords, modelFilter) end
../client/functions.lua
1
ESX.Game.GetClosestEntity = function(entities, isPlayerEntities, coords, modelFilter)
2
local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil
3
4
if coords then
5
coords = vector3(coords.x, coords.y, coords.z)
6
else
7
local playerPed = PlayerPedId()
8
coords = GetEntityCoords(playerPed)
9
end
10
11
if modelFilter then
12
filteredEntities = {}
13
14
for k,entity in pairs(entities) do
15
if modelFilter[GetEntityModel(entity)] then
16
table.insert(filteredEntities, entity)
17
end
18
end
19
end
20
21
for k,entity in pairs(filteredEntities or entities) do
22
local distance = #(coords - GetEntityCoords(entity))
23
24
if closestEntityDistance == -1 or distance < closestEntityDistance then
25
closestEntity, closestEntityDistance = isPlayerEntities and k or entity, distance
26
end
27
end
28
29
return closestEntity, closestEntityDistance
30
end

Server modifications

ESX OLD
ESX LEGACY

Delete event

Completely remove this part of your code, so we avoid checks in inventory default
../server/main.lua
1
if result[1].inventory and result[1].inventory ~= '' then
2
local inventory = json.decode(result[1].inventory)
3
4
for name,count in pairs(inventory) do
5
local item = ESX.Items[name]
6
7
if item then
8
foundItems[name] = count
9
else
10
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
11
end
12
end
13
end
14
15
for name,item in pairs(ESX.Items) do
16
local count = foundItems[name] or 0
17
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
18
19
table.insert(userData.inventory, {
20
name = name,
21
count = count,
22
label = item.label,
23
weight = item.weight,
24
usable = ESX.UsableItemsCallbacks[name] ~= nil,
25
rare = item.rare,
26
canRemove = item.canRemove
27
})
28
end
29
30
table.sort(userData.inventory, function(a, b)
31
return a.label < b.label
32
end)

Delete event

Completely remove this part of your code, so we avoid checks in inventory default
../server/main.lua
1
if result.inventory and result.inventory ~= '' then
2
local inventory = json.decode(result.inventory)
3
4
for name,count in pairs(inventory) do
5
local item = ESX.Items[name]
6
7
if item then
8
foundItems[name] = count
9
else
10
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
11
end
12
end
13
end
14
15
for name,item in pairs(ESX.Items) do
16
local count = foundItems[name] or 0
17
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
18
19
table.insert(userData.inventory, {
20
name = name,
21
count = count,
22
label = item.label,
23
weight = item.weight,
24
usable = Core.UsableItemsCallbacks[name] ~= nil,
25
rare = item.rare,
26
canRemove = item.canRemove
27
})
28
end
29
30
table.sort(userData.inventory, function(a, b)
31
return a.label < b.label
32
end)
ESX 1.2
ESX 1.2 Final
LEGACY OLD
LEGACY LATEST

Replace event

We will replace our current SavePlayer to avoid data loss
../server/functions.lua
1
ESX.SavePlayer = function(xPlayer, cb)
2
local asyncTasks = {}
3
4
-- User accounts
5
for k,v in ipairs(xPlayer.accounts) do
6
if ESX.LastPlayerData[xPlayer.source].accounts[v.name] ~= v.money then
7
table.insert(asyncTasks, function(cb)
8
MySQL.Async.execute('UPDATE user_accounts SET money = @money WHERE identifier = @identifier AND name = @name', {
9
['@money'] = v.money,
10
['@identifier'] = xPlayer.identifier,
11
['@name'] = v.name
12
}, function(rowsChanged)
13
cb()
14
end)
15
end)
16
17
ESX.LastPlayerData[xPlayer.source].accounts[v.name] = v.money
18
end
19
end
20
21
-- Job, loadout, inventory and position
22
table.insert(asyncTasks, function(cb)
23
MySQL.Async.execute('UPDATE users SET job = @job, job_grade = @job_grade, loadout = @loadout, position = @position, WHERE identifier = @identifier', {
24
['@job'] = xPlayer.job.name,
25
['@job_grade'] = xPlayer.job.grade,
26
['@loadout'] = json.encode(xPlayer.getLoadout()),
27
['@position'] = json.encode(xPlayer.getCoords()),
28
['@identifier'] = xPlayer.identifier,
29
}, function(rowsChanged)
30
cb()
31
end)
32
end)
33
34
Async.parallel(asyncTasks, function(results)
35
print(('[es_extended] [^2INFO^7] Saved player "%s^7"'):format(xPlayer.getName()))
36
37
if cb ~= nil then
38
cb()
39
end
40
end)
41
end

Replace event

We will replace our current SavePlayer to avoid data loss
../server/functions.lua
1
ESX.SavePlayer = function(xPlayer, cb)
2
local asyncTasks = {}
3
4
table.insert(asyncTasks, function(cb2)
5
MySQL.Async.execute('UPDATE users SET accounts = @accounts, job = @job, job_grade = @job_grade, `group` = @group, loadout = @loadout, position = @position WHERE identifier = @identifier', {
6
['@accounts'] = json.encode(xPlayer.getAccounts(true)),
7
['@job'] = xPlayer.job.name,
8
['@job_grade'] = xPlayer.job.grade,
9
['@group'] = xPlayer.getGroup(),
10
['@loadout'] = json.encode(xPlayer.getLoadout(true)),
11
['@position'] = json.encode(xPlayer.getCoords()),
12
['@identifier'] = xPlayer.getIdentifier(),
13
}, function(rowsChanged)
14
cb2()
15
end)
16
end)
17
18
Async.parallel(asyncTasks, function(results)
19
print(('[es_extended] [^2INFO^7] Saved player "%s^7"'):format(xPlayer.getName()))
20
21
if cb then
22
cb()
23
end
24
end)
25
end

Replace event

We will replace our current SavePlayer to avoid data loss
../server/functions.lua
1
local savePlayers = -1
2
Citizen.CreateThread(function()
3
savePlayers = MySQL.Sync.store("UPDATE users SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ? WHERE `identifier` = ?")
4
end)
5
6
ESX.SavePlayer = function(xPlayer, cb)
7
local asyncTasks = {}
8
9
table.insert(asyncTasks, function(cb2)
10
MySQL.Async.execute(savePlayers, {
11
json.encode(xPlayer.getAccounts(true)),
12
xPlayer.job.name,
13
xPlayer.job.grade,
14
xPlayer.getGroup(),
15
json.encode(xPlayer.getCoords()),
16
xPlayer.getIdentifier()
17
}, function(rowsChanged)
18
cb2()
19
end)
20
end)
21
22
Async.parallel(asyncTasks, function(results)
23
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.getName()))
24
25
if cb then
26
cb()
27
end
28
end)
29
end

Replace event

We will replace our current SavePlayer to avoid data loss
../server/functions.lua
1
function Core.SavePlayer(xPlayer, cb)
2
MySQL.prepare('UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ? WHERE `identifier` = ?', {
3
json.encode(xPlayer.getAccounts(true)),
4
xPlayer.job.name,
5
xPlayer.job.grade,
6
xPlayer.group,
7
json.encode(xPlayer.getCoords()),
8
xPlayer.identifier
9
}, function(affectedRows)
10
if affectedRows == 1 then
11
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.name))
12
end
13
if cb then cb() end
14
end)
15
end
ESX 1.2
ESX 1.2 Final
LEGACY OLD
LEGACY LATEST

Replace event

We will replace our current SavePlayers to avoid data loss
../server/functions.lua
1
ESX.SavePlayers = function(cb)
2
local asyncTasks = {}
3
local xPlayers = ESX.GetPlayers()
4
5
for i=1, #xPlayers, 1 do
6
table.insert(asyncTasks, function(cb)
7
local xPlayer = ESX.GetPlayerFromId(xPlayers[i])
8
ESX.SavePlayer(xPlayer, cb)
9
TriggerEvent('qs-core:savePlayer', xPlayers[i])
10
end)
11
end
12
13
Async.parallelLimit(asyncTasks, 8, function(results)
14
print(('[es_extended] [^2INFO^7] Saved %s player(s)'):format(#xPlayers))
15
if cb ~= nil then
16
cb()
17
end
18
end)
19
end

Replace event

We will replace our current SavePlayers to avoid data loss
../server/functions.lua
1
ESX.SavePlayers = function(cb)
2
local xPlayers, asyncTasks = ESX.GetPlayers(), {}
3
4
for i=1, #xPlayers, 1 do
5
table.insert(asyncTasks, function(cb2)
6
local xPlayer = ESX.GetPlayerFromId(xPlayers[i])
7
ESX.SavePlayer(xPlayer, cb2)
8
TriggerEvent('qs-core:savePlayer', xPlayers[i])
9
end)
10
end
11
12
Async.parallelLimit(asyncTasks, 8, function(results)
13
print(('[es_extended] [^2INFO^7] Saved %s player(s)'):format(#xPlayers))
14
if cb then
15
cb()
16
end
17
end)
18
end

Replace event

We will replace our current SavePlayers to avoid data loss
../server/functions.lua
1
ESX.SavePlayers = function(cb)
2
local xPlayers = ESX.GetExtendedPlayers()
3
if #xPlayers > 0 then
4
local time = os.time()
5
6
local selectListWithNames = "SELECT '%s' AS identifier, '%s' AS new_accounts, '%s' AS new_job, %s AS new_job_grade, '%s' AS new_group, '%s' AS new_position "
7
local selectListNoNames = "SELECT '%s', '%s', '%s' , %s, '%s', '%s' "
8
9
local updateCommand = 'UPDATE users u JOIN ('
10
11
local selectList = selectListNoNames
12
local first = true
13
for k, xPlayer in pairs(xPlayers) do
14
if first == false then
15
updateCommand = updateCommand .. ' UNION '
16
else
17
selectList = selectListWithNames
18
end
19
20
updateCommand = updateCommand .. string.format(selectList,
21
xPlayer.identifier,
22
json.encode(xPlayer.getAccounts(true)),
23
xPlayer.job.name,
24
xPlayer.job.grade,
25
xPlayer.getGroup(),
26
json.encode(xPlayer.getCoords())
27
)
28
29
first = false
30
end
31
32
updateCommand = updateCommand .. ' ) vals ON u.identifier = vals.identifier SET accounts = new_accounts, job = new_job, job_grade = new_job_grade, `group` = new_group, `position` = new_position'
33
34
MySQL.Async.fetchAll(updateCommand, {},
35
function(result)
36
if result then
37
if cb then cb() else print(('[^2INFO^7] Saved %s of %s player(s) over %s seconds'):format(result.affectedRows, #xPlayers, os.time() - time)) end
38
end
39
end)
40
local xPlayers = ESX.GetPlayers()
41
for i=1, #xPlayers, 1 do
42
TriggerEvent('qs-core:savePlayer', xPlayers[i])
43
end
44
end
45
end

Replace event

We will replace our current SavePlayers to avoid data loss
../server/functions.lua
1
function Core.SavePlayers(cb)
2
local xPlayers = ESX.GetExtendedPlayers()
3
local count = #xPlayers
4
if count > 0 then
5
local parameters = {}
6
local time = os.time()
7
for i=1, count do
8
local xPlayer = xPlayers[i]
9
parameters[#parameters+1] = {
10
json.encode(xPlayer.getAccounts(true)),
11
xPlayer.job.name,
12
xPlayer.job.grade,
13
xPlayer.group,
14
json.encode(xPlayer.getCoords()),
15
xPlayer.identifier
16
}
17
end
18
MySQL.prepare("UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ? WHERE `identifier` = ?", parameters,
19
function(results)
20
if results then
21
if type(cb) == 'function' then cb() else print(('[^2INFO^7] Saved %s %s over %s ms'):format(count, count > 1 and 'players' or 'player', (os.time() - time) / 1000000)) end
22
end
23
end)
24
local xPlayers = ESX.GetPlayers()
25
for i=1, #xPlayers, 1 do
26
TriggerEvent('qs-core:savePlayer', xPlayers[i])
27
end
28
end
29
end
ESX OLD
ESX LEGACY

Replace event

We will replace this event completely
../server/functions.lua
1
ESX.RegisterUsableItem = function(item, cb)
2
TriggerEvent('inventory:server:useable', item, cb)
3
end

Replace event

We will replace this event completely
../server/functions.lua
1
function ESX.RegisterUsableItem(item, cb)
2
TriggerEvent('inventory:server:useable', item, cb)
3
end
ESX OLD
ESX LEGACY

Replace event

We will replace this event completely
../server/functions.lua
1
ESX.GetItemLabel = function(item)
2
local label = exports['qs-core']:GetItemLabel(item)
3
return label
4
end

Replace event

We will replace this event completely
../server/functions.lua
1
function ESX.GetItemLabel(item)
2
local label = exports['qs-core']:GetItemLabel(item)
3
return label
4
end
ALL ESX

Delete event

Delete these commands, as they will not be needed
../server/commands.lua
1
ESX.RegisterCommand('giveitem', 'admin', function(xPlayer, args, showError)
2
args.playerId.addInventoryItem(args.item, args.count)
3
end, true, {help = _U('command_giveitem'), validate = true, arguments = {
4
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'},
5
{name = 'item', help = _U('command_giveitem_item'), type = 'item'},
6
{name = 'count', help = _U('command_giveitem_count'), type = 'number'}
7
}})
../server/commands.lua
1
ESX.RegisterCommand('giveweapon', 'admin', function(xPlayer, args, showError)
2
if args.playerId.hasWeapon(args.weapon) then
3
showError(_U('command_giveweapon_hasalready'))
4
else
5
args.playerId.addWeapon(args.weapon, args.ammo)
6
end
7
end, true, {help = _U('command_giveweapon'), validate = true, arguments = {
8
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'},
9
{name = 'weapon', help = _U('command_giveweapon_weapon'), type = 'weapon'}
10
--{name = 'ammo', help = _U('command_giveweapon_ammo'), type = 'number'} This was removed because ammo are now items
11
}})
../server/commands.lua
1
ESX.RegisterCommand('giveweaponcomponent', 'admin', function(xPlayer, args, showError)
2
if args.playerId.hasWeapon(args.weaponName) then
3
local component = ESX.GetWeaponComponent(args.weaponName, args.componentName)
4
5
if component then
6
if args.playerId.hasWeaponComponent(args.weaponName, args.componentName) then
7
showError(_U('command_giveweaponcomponent_hasalready'))
8
else
9
args.playerId.addWeaponComponent(args.weaponName, args.componentName)
10
end
11
else
12
showError(_U('command_giveweaponcomponent_invalid'))
13
end
14
else
15
showError(_U('command_giveweaponcomponent_missingweapon'))
16
end
17
end, true, {help = _U('command_giveweaponcomponent'), validate = true, arguments = {
18
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'},
19
{name = 'weaponName', help = _U('command_giveweapon_weapon'), type = 'weapon'},
20
{name = 'componentName', help = _U('command_giveweaponcomponent_component'), type = 'string'}
21
}})
ESX OLD
ESX LEGACY

Replace event

We will completely replace the addAccountMoney event
../server/classes/player.lua
1
self.addAccountMoney = function(accountName, money)
2
if money > 0 then
3
local money = ESX.Math.Round(money)
4
if accountName == 'money' then
5
local cash = self.getInventoryItem('cash').count
6
if cash then
7
self.addInventoryItem("cash", money)
8
self.setAccountMoney('money', cash + money)
9
end
10
elseif accountName == 'black_money' then
11
local black_money = self.getInventoryItem('black_money').count
12
if black_money then
13
self.addInventoryItem("black_money", money)
14
self.setAccountMoney('black_money', black_money + money)
15
end
16
else
17
local account = self.getAccount(accountName)
18
if account and account.money then
19
local newMoney = account.money + money
20
self.setAccountMoney(accountName, newMoney)
21
end
22
end
23
end
24
end

Replace event

We will completely replace the addAccountMoney event
../server/classes/player.lua
1
function self.addAccountMoney(accountName, money)
2
if money > 0 then
3
local money = ESX.Math.Round(money)
4
if accountName == 'money' then
5
local cash = self.getInventoryItem('cash').count
6
if cash then
7
self.addInventoryItem("cash", money)
8
self.setAccountMoney('money', cash + money)
9
end
10
elseif accountName == 'black_money' then
11
local black_money = self.getInventoryItem('black_money').count
12
if black_money then
13
self.addInventoryItem("black_money", money)
14
self.setAccountMoney('black_money', black_money + money)
15
end
16
else
17
local account = self.getAccount(accountName)
18
if account and account.money then
19
local newMoney = account.money + money
20
self.setAccountMoney(accountName, newMoney)
21
end
22
end
23
end
24
end
ESX OLD
ESX LEGACY

Replace event

We will completely replace the removeAccountMoney event
../server/classes/player.lua
1
self.removeAccountMoney = function(accountName, money)
2
if money > 0 then
3
local money = ESX.Math.Round(money)
4
if accountName == 'money' then
5
local cash = self.getInventoryItem('cash').count
6
if cash then
7
self.removeInventoryItem("cash",