BREAKING COMMIT - Player loading improvements (read description!)

This commit implements a new way of saving and getting player coords.

Player coords are synced with its assigned xPlayer, the syncing interval is defined in the configuration file.
Also coords wont sync if the player is too close to their previous location.

Existing servers must change their database to account for the new length of the position varchar, at 53 chars max. There's also a column default value specified in the sql file.

New xPlayer functions:
- .getCoords()
- .setCoords() (x,y,z,heading (optional) table
- .updateCoords()

The last one is used for syncing coords from client (interval). The second one will teleport the player. First one is a renamed .getLastPosition()
This commit is contained in:
ElPumpo
2020-01-12 22:13:30 +01:00
parent 5566abca4f
commit f89e051756
6 changed files with 277 additions and 311 deletions
+38 -51
View File
@@ -1,6 +1,4 @@
local isLoadoutLoaded, isPaused, isPlayerSpawned, isDead = false, false, false, false
local lastLoadout, pickups = {}, {}
local isLoadoutLoaded, isPaused, isDead, isFirstSpawn, pickups = false, false, false, true, {}
RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(xPlayer)
@@ -47,33 +45,26 @@ end)
AddEventHandler('playerSpawned', function()
while not ESX.PlayerLoaded do
Citizen.Wait(1)
Citizen.Wait(10)
end
local playerPed = PlayerPedId()
TriggerEvent('esx:restoreLoadout')
-- Restore position
if ESX.PlayerData.lastPosition then
SetEntityCoords(playerPed, ESX.PlayerData.lastPosition.x, ESX.PlayerData.lastPosition.y, ESX.PlayerData.lastPosition.z)
if isFirstSpawn then
ESX.Game.Teleport(PlayerPedId(), ESX.PlayerData.coords)
isFirstSpawn = false
end
TriggerEvent('esx:restoreLoadout') -- restore loadout
isLoadoutLoaded, isPlayerSpawned, isDead = true, true, false
isLoadoutLoaded, isDead = true, false
if Config.EnablePvP then
SetCanAttackFriendly(playerPed, true, false)
SetCanAttackFriendly(PlayerPedId(), true, false)
NetworkSetFriendlyFireOption(true)
end
end)
AddEventHandler('esx:onPlayerDeath', function()
isDead = true
end)
AddEventHandler('skinchanger:loadDefaultModel', function()
isLoadoutLoaded = false
end)
AddEventHandler('esx:onPlayerDeath', function() isDead = true end)
AddEventHandler('skinchanger:loadDefaultModel', function() isLoadoutLoaded = false end)
AddEventHandler('skinchanger:modelLoaded', function()
while not ESX.PlayerLoaded do
@@ -175,7 +166,6 @@ AddEventHandler('esx:addWeapon', function(weaponName, ammo)
local weaponHash = GetHashKey(weaponName)
GiveWeaponToPed(playerPed, weaponHash, ammo, false, false)
--AddAmmoToPed(playerPed, weaponHash, ammo) possibly not needed
end)
RegisterNetEvent('esx:addWeaponComponent')
@@ -211,7 +201,6 @@ AddEventHandler('esx:removeWeapon', function(weaponName, ammo)
end
end)
RegisterNetEvent('esx:removeWeaponComponent')
AddEventHandler('esx:removeWeaponComponent', function(weaponName, weaponComponent)
local playerPed = PlayerPedId()
@@ -221,21 +210,16 @@ AddEventHandler('esx:removeWeaponComponent', function(weaponName, weaponComponen
RemoveWeaponComponentFromPed(playerPed, weaponHash, componentHash)
end)
-- Commands
RegisterNetEvent('esx:teleport')
AddEventHandler('esx:teleport', function(pos)
pos.x = pos.x + 0.0
pos.y = pos.y + 0.0
pos.z = pos.z + 0.0
AddEventHandler('esx:teleport', function(coords)
local playerPed = PlayerPedId()
RequestCollisionAtCoord(pos.x, pos.y, pos.z)
-- ensure decmial number
coords.x = coords.x + 0.0
coords.y = coords.y + 0.0
coords.z = coords.z + 0.0
while not HasCollisionLoadedAroundEntity(PlayerPedId()) do
RequestCollisionAtCoord(pos.x, pos.y, pos.z)
Citizen.Wait(1)
end
SetEntityCoords(PlayerPedId(), pos.x, pos.y, pos.z)
ESX.Game.Teleport(playerPed, coords)
end)
RegisterNetEvent('esx:setJob')
@@ -384,7 +368,7 @@ AddEventHandler('esx:deleteVehicle', function(radius)
end
end)
-- Pause menu disable HUD display
-- Pause menu disables HUD display
if Config.EnableHud then
Citizen.CreateThread(function()
while true do
@@ -405,12 +389,11 @@ end
-- Save loadout
Citizen.CreateThread(function()
local lastLoadout = {}
while true do
Citizen.Wait(5000)
local playerPed = PlayerPedId()
local loadout = {}
local loadoutChanged = false
local playerPed, loadout, loadoutChanged = PlayerPedId(), {}, false
for k,v in ipairs(Config.Weapons) do
local weaponName = v.name
@@ -469,8 +452,8 @@ if Config.DisableWantedLevel then
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
local playerId = PlayerId()
if GetPlayerWantedLevel(playerId) ~= 0 then
SetPlayerWantedLevel(playerId, 0, false)
SetPlayerWantedLevelNow(playerId, false)
@@ -528,22 +511,26 @@ Citizen.CreateThread(function()
end
end)
-- Last position
-- Update current player coords
Citizen.CreateThread(function()
while true do
local previousCoords = vector3(0, 0, 0)
-- wait for player to restore coords
while not isLoadoutLoaded do
Citizen.Wait(1000)
end
while true do
Citizen.Wait(Config.CoordsSyncInterval)
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
local distance = #(playerCoords - previousCoords)
if ESX.PlayerLoaded and isPlayerSpawned then
local coords = GetEntityCoords(playerPed)
if not IsEntityDead(playerPed) then
ESX.PlayerData.lastPosition = {x = coords.x, y = coords.y, z = coords.z}
end
end
if IsEntityDead(playerPed) and isPlayerSpawned then
isPlayerSpawned = false
if distance > 10 then
previousCoords = playerCoords
local playerHeading = ESX.Math.Round(GetEntityHeading(playerPed), 1)
local formattedCoords = {x = ESX.Math.Round(playerCoords.x, 1), y = ESX.Math.Round(playerCoords.y, 1), z = ESX.Math.Round(playerCoords.z, 1), heading = playerHeading}
TriggerServerEvent('esx:updateCoords', formattedCoords)
end
end
end)
+2 -1
View File
@@ -10,6 +10,7 @@ Config.EnableHud = true -- enable the default hud? Display current jo
Config.EnablePvP = true -- enable pvp?
Config.MaxWeight = 24 -- the max inventory weight without backpack
Config.PaycheckInterval = 7 * 60000
Config.PaycheckInterval = 7 * 60000 -- how often to recieve pay checks in milliseconds
Config.CoordsSyncInterval = 2 * 60000 -- how often to sync coords with server in milliseconds
Config.EnableDebug = false
+1 -1
View File
@@ -6,7 +6,7 @@ ALTER TABLE `users`
ADD COLUMN `job` VARCHAR(50) NULL DEFAULT 'unemployed' AFTER `skin`,
ADD COLUMN `job_grade` INT NULL DEFAULT 0 AFTER `job`,
ADD COLUMN `loadout` LONGTEXT NULL AFTER `job_grade`,
ADD COLUMN `position` VARCHAR(36) NULL AFTER `loadout`
ADD COLUMN `position` VARCHAR(53) NULL DEFAULT '{"x":-269.4,"y":-955.3,"z":31.2,"heading":205.8}' AFTER `loadout`
;
CREATE TABLE `items` (
+35 -47
View File
@@ -1,4 +1,4 @@
function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, lastPosition)
function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, coords)
local self = {}
self.player = player
@@ -7,8 +7,8 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.job = job
self.loadout = loadout
self.name = name
self.lastPosition = lastPosition
self.maxWeight = Config.MaxWeight
self.coords = coords
self.source = self.player.get('source')
self.identifier = self.player.get('identifier')
@@ -41,23 +41,25 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
return self.player.get('bank')
end
self.getCoords = function(vectorType)
local coords = self.player.get('coords')
coords = {x = ESX.Math.Round(coords.x, 1), y = ESX.Math.Round(coords.y, 1), z = ESX.Math.Round(coords.z, 1)}
self.setCoords = function(coords)
self.updateCoords(coords)
self.triggerEvent('esx:teleport', coords)
end
if vectorType then
return vector3(coords.x, coords.y, coords.z)
self.updateCoords = function(coords)
self.coords = {x = ESX.Math.Round(coords.x, 1), y = ESX.Math.Round(coords.y, 1), z = ESX.Math.Round(coords.z, 1), heading = ESX.Math.Round(coords.heading, 1)}
end
self.getCoords = function(vector)
if vector then
return vector3(self.coords.x, self.coords.y, self.coords.z)
else
return coords
return self.coords
end
end
self.setCoords = function(x, y, z)
self.player.coords = {x = x, y = y, z = z}
end
self.kick = function(reason)
self.player.kick(reason)
DropPlayer(self.source, reason)
end
self.addMoney = function(money)
@@ -194,20 +196,6 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.name = newName
end
self.getLastPosition = function()
if self.lastPosition and self.lastPosition.x and self.lastPosition.y and self.lastPosition.z then
self.lastPosition.x = ESX.Math.Round(self.lastPosition.x, 1)
self.lastPosition.y = ESX.Math.Round(self.lastPosition.y, 1)
self.lastPosition.z = ESX.Math.Round(self.lastPosition.z, 1)
end
return self.lastPosition
end
self.setLastPosition = function(position)
self.lastPosition = position
end
self.getMissingAccounts = function(cb)
MySQL.Async.fetchAll('SELECT name FROM user_accounts WHERE identifier = @identifier', {
['@identifier'] = self.getIdentifier()
@@ -262,7 +250,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.set('bank', newMoney)
end
TriggerClientEvent('esx:setAccountMoney', self.source, account)
self.triggerEvent('esx:setAccountMoney', account)
end
end
end
@@ -279,7 +267,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.set('bank', newMoney)
end
TriggerClientEvent('esx:setAccountMoney', self.source, account)
self.triggerEvent('esx:setAccountMoney', account)
end
end
end
@@ -296,7 +284,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.set('bank', newMoney)
end
TriggerClientEvent('esx:setAccountMoney', self.source, account)
self.triggerEvent('esx:setAccountMoney', account)
end
end
end
@@ -319,7 +307,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
item.count = newCount
TriggerEvent('esx:onAddInventoryItem', self.source, item, count)
TriggerClientEvent('esx:addInventoryItem', self.source, item, count)
self.triggerEvent('esx:addInventoryItem', item, count)
end
end
@@ -333,7 +321,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
item.count = newCount
TriggerEvent('esx:onRemoveInventoryItem', self.source, item, count)
TriggerClientEvent('esx:removeInventoryItem', self.source, item, count)
self.triggerEvent('esx:removeInventoryItem', item, count)
end
end
end
@@ -347,10 +335,10 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
if oldCount > item.count then
TriggerEvent('esx:onRemoveInventoryItem', self.source, item, oldCount - item.count)
TriggerClientEvent('esx:removeInventoryItem', self.source, item, oldCount - item.count)
self.triggerEvent('esx:removeInventoryItem', item, oldCount - item.count)
else
TriggerEvent('esx:onAddInventoryItem', self.source, item, item.count - oldCount)
TriggerClientEvent('esx:addInventoryItem', self.source, item, item.count - oldCount)
self.triggerEvent('esx:addInventoryItem', item, item.count - oldCount)
end
end
end
@@ -388,7 +376,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
self.setMaxWeight = function(newWeight)
self.maxWeight = newWeight
TriggerClientEvent('esx:setMaxWeight', self.source, self.maxWeight)
self.triggerEvent('esx:setMaxWeight', self.maxWeight)
end
self.setJob = function(job, grade)
@@ -419,7 +407,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
end
TriggerEvent('esx:setJob', self.source, self.job, lastJob)
TriggerClientEvent('esx:setJob', self.source, self.job)
self.triggerEvent('esx:setJob', self.job)
else
print(('[es_extended] [^3WARNING^7] Ignoring invalid .setJob() usage for "%s"'):format(self.identifier))
end
@@ -436,8 +424,8 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
components = {}
})
TriggerClientEvent('esx:addWeapon', self.source, weaponName, ammo)
TriggerClientEvent('esx:addInventoryItem', self.source, {label = weaponLabel}, 1)
self.triggerEvent('esx:addWeapon', weaponName, ammo)
self.triggerEvent('esx:addInventoryItem', {label = weaponLabel}, 1)
end
end
@@ -447,7 +435,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
if weapon then
if not self.hasWeaponComponent(weaponName, weaponComponent) then
table.insert(self.loadout[loadoutNum].components, weaponComponent)
TriggerClientEvent('esx:addWeaponComponent', self.source, weaponName, weaponComponent)
self.triggerEvent('esx:addWeaponComponent', weaponName, weaponComponent)
end
end
end
@@ -457,7 +445,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
if weapon then
weapon.ammo = weapon.ammo + ammoCount
TriggerClientEvent('esx:setWeaponAmmo', self.source, weaponName, weapon.ammo)
self.triggerEvent('esx:setWeaponAmmo', weaponName, weapon.ammo)
end
end
@@ -469,7 +457,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
weaponLabel = v.label
for k2,v2 in ipairs(v.components) do
TriggerClientEvent('esx:removeWeaponComponent', self.source, weaponName, v2)
self.triggerEvent('esx:removeWeaponComponent', weaponName, v2)
end
table.remove(self.loadout, k)
@@ -478,8 +466,8 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
end
if weaponLabel then
TriggerClientEvent('esx:removeWeapon', self.source, weaponName, ammo)
TriggerClientEvent('esx:removeInventoryItem', self.source, {label = weaponLabel}, 1)
self.triggerEvent('esx:removeWeapon', weaponName, ammo)
self.triggerEvent('esx:removeInventoryItem', {label = weaponLabel}, 1)
end
end
@@ -494,7 +482,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
end
end
TriggerClientEvent('esx:removeWeaponComponent', self.source, weaponName, weaponComponent)
self.triggerEvent('esx:removeWeaponComponent', weaponName, weaponComponent)
end
end
@@ -503,7 +491,7 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
if weapon then
weapon.ammo = weapon.ammo - ammoCount
TriggerClientEvent('esx:setWeaponAmmo', self.source, weaponName, weapon.ammo)
self.triggerEvent('esx:setWeaponAmmo', weaponName, weapon.ammo)
end
end
@@ -544,11 +532,11 @@ function CreateExtendedPlayer(player, accounts, inventory, job, loadout, name, l
end
self.showNotification = function(msg)
TriggerClientEvent('esx:showNotification', self.source, msg)
self.triggerEvent('esx:showNotification', msg)
end
self.showHelpNotification = function(msg)
TriggerClientEvent('esx:showHelpNotification', self.source, msg)
self.triggerEvent('esx:showHelpNotification', msg)
end
return self
+3 -4
View File
@@ -38,7 +38,6 @@ end
ESX.SavePlayer = function(xPlayer, cb)
local asyncTasks = {}
xPlayer.setLastPosition(xPlayer.getCoords())
-- User accounts
for k,v in ipairs(xPlayer.accounts) do
@@ -80,7 +79,7 @@ ESX.SavePlayer = function(xPlayer, cb)
['@job'] = xPlayer.job.name,
['@job_grade'] = xPlayer.job.grade,
['@loadout'] = json.encode(xPlayer.getLoadout()),
['@position'] = json.encode(xPlayer.getLastPosition()),
['@position'] = json.encode(xPlayer.getCoords()),
['@identifier'] = xPlayer.identifier
}, function(rowsChanged)
cb()
@@ -88,7 +87,7 @@ ESX.SavePlayer = function(xPlayer, cb)
end)
Async.parallel(asyncTasks, function(results)
print(('[es_extended] [^2INFO^7] Saved %s'):format(xPlayer.getName()))
print(('[es_extended] [^2INFO^7] Saved player "%s^7"'):format(xPlayer.getName()))
if cb ~= nil then
cb()
@@ -190,4 +189,4 @@ ESX.DoesJobExist = function(job, grade)
end
return false
end
end
+198 -207
View File
@@ -1,233 +1,225 @@
AddEventHandler('es:playerLoaded', function(source, _player)
local playerId = source
local tasks = {}
AddEventHandler('es:playerLoaded', function(playerId, player)
local tasks = {}
local userData = {
accounts = {},
inventory = {},
job = {},
loadout = {},
playerName = GetPlayerName(playerId),
lastPosition = nil
accounts = {},
inventory = {},
job = {},
loadout = {},
playerName = GetPlayerName(playerId),
coords = nil
}
TriggerEvent('es:getPlayerFromId', playerId, function(player)
-- Update user name in DB
table.insert(tasks, function(cb)
MySQL.Async.execute('UPDATE users SET name = @name WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier(),
['@name'] = userData.playerName
}, function(rowsChanged)
cb()
end)
-- Update user name in DB
table.insert(tasks, function(cb)
MySQL.Async.execute('UPDATE users SET name = @name WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier(),
['@name'] = userData.playerName
}, function(rowsChanged)
cb()
end)
end)
-- Get accounts
table.insert(tasks, function(cb)
MySQL.Async.fetchAll('SELECT name, money FROM user_accounts WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(accounts)
local validAccounts = ESX.Table.Set(Config.Accounts)
for k,v in ipairs(accounts) do
if validAccounts[v.name] then
table.insert(userData.accounts, {
name = v.name,
money = v.money,
label = Config.AccountLabels[v.name]
})
end
-- Get accounts
table.insert(tasks, function(cb)
MySQL.Async.fetchAll('SELECT name, money FROM user_accounts WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(accounts)
local validAccounts = ESX.Table.Set(Config.Accounts)
for k,v in ipairs(accounts) do
if validAccounts[v.name] then
table.insert(userData.accounts, {
name = v.name,
money = v.money,
label = Config.AccountLabels[v.name]
})
end
end
cb()
end)
cb()
end)
end)
-- Get inventory
table.insert(tasks, function(cb)
-- Get inventory
table.insert(tasks, function(cb)
MySQL.Async.fetchAll('SELECT item, count FROM user_inventory WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(inventory)
local tasks2, foundItems = {}, {}
MySQL.Async.fetchAll('SELECT item, count FROM user_inventory WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(inventory)
local tasks2, foundItems = {}, {}
for k,v in ipairs(inventory) do
local item = ESX.Items[v.item]
for k,v in ipairs(inventory) do
local item = ESX.Items[v.item]
if item then
foundItems[v.item] = true
if item then
foundItems[v.item] = true
table.insert(userData.inventory, {
name = v.item,
count = v.count,
label = item.label,
weight = item.weight,
usable = ESX.UsableItemsCallbacks[v.item] ~= nil,
rare = item.rare,
canRemove = item.canRemove
})
else
print(('[es_extended] [^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(v.item, player.getIdentifier()))
end
table.insert(userData.inventory, {
name = v.item,
count = v.count,
label = item.label,
weight = item.weight,
usable = ESX.UsableItemsCallbacks[v.item] ~= nil,
rare = item.rare,
canRemove = item.canRemove
})
else
print(('[es_extended] [^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(v.item, player.getIdentifier()))
end
end
for itemName,item in pairs(ESX.Items) do
if not foundItems[itemName] then
table.insert(userData.inventory, {
name = itemName,
count = 0,
label = item.label,
weight = item.weight,
usable = ESX.UsableItemsCallbacks[itemName] ~= nil,
rare = item.rare,
canRemove = item.canRemove
})
for name,item in pairs(ESX.Items) do
if not foundItems[name] then
table.insert(userData.inventory, {
name = name,
count = 0,
label = item.label,
weight = item.weight,
usable = ESX.UsableItemsCallbacks[name] ~= nil,
rare = item.rare,
canRemove = item.canRemove
})
local scope = function(item, identifier)
table.insert(tasks2, function(cb2)
MySQL.Async.execute('INSERT INTO user_inventory (identifier, item, count) VALUES (@identifier, @item, @count)', {
['@identifier'] = identifier,
['@item'] = item,
['@count'] = 0
}, function(rowsChanged)
cb2()
end)
local scope = function(item, identifier)
table.insert(tasks2, function(cb2)
MySQL.Async.execute('INSERT INTO user_inventory (identifier, item, count) VALUES (@identifier, @item, @count)', {
['@identifier'] = identifier,
['@item'] = item,
['@count'] = 0
}, function(rowsChanged)
cb2()
end)
end
end)
end
scope(itemName, player.getIdentifier())
scope(name, player.getIdentifier())
end
end
Async.parallelLimit(tasks2, 5, function(results) end)
table.sort(userData.inventory, function(a,b)
return a.label < b.label
end)
cb()
end)
end)
-- Get job and loadout
table.insert(tasks, function(cb)
local tasks2 = {}
-- Get job name, grade and coords
table.insert(tasks2, function(cb2)
MySQL.Async.fetchAll('SELECT job, job_grade, loadout, position FROM users WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(result)
local job, grade = result[1].job, tostring(result[1].job_grade)
if ESX.DoesJobExist(job, grade) then
local jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
userData.job = {}
userData.job.id = jobObject.id
userData.job.name = jobObject.name
userData.job.label = jobObject.label
userData.job.grade = tonumber(grade)
userData.job.grade_name = gradeObject.name
userData.job.grade_label = gradeObject.label
userData.job.grade_salary = gradeObject.salary
userData.job.skin_male = {}
userData.job.skin_female = {}
if gradeObject.skin_male then
userData.job.skin_male = json.decode(gradeObject.skin_male)
end
if gradeObject.skin_female then
userData.job.skin_female = json.decode(gradeObject.skin_female)
end
else
print(('[es_extended] [^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(player.getIdentifier(), job, grade))
local job, grade = 'unemployed', '0'
local jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
userData.job = {}
userData.job.id = jobObject.id
userData.job.name = jobObject.name
userData.job.label = jobObject.label
userData.job.grade = tonumber(grade)
userData.job.grade_name = gradeObject.name
userData.job.grade_label = gradeObject.label
userData.job.grade_salary = gradeObject.salary
userData.job.skin_male = {}
userData.job.skin_female = {}
end
if result[1].loadout then
userData.loadout = json.decode(result[1].loadout)
-- Compatibility with old loadouts prior to components update
for k,v in ipairs(userData.loadout) do
if v.components == nil then
v.components = {}
end
end
end
Async.parallelLimit(tasks2, 5, function(results) end)
table.sort(userData.inventory, function(a,b)
return a.label < b.label
end)
cb()
userData.coords = json.decode(result[1].position)
cb2()
end)
end)
-- Get job and loadout
table.insert(tasks, function(cb)
Async.series(tasks2, cb)
local tasks2 = {}
end)
-- Get job name, grade and last position
table.insert(tasks2, function(cb2)
-- Run Tasks
Async.parallel(tasks, function(results)
local xPlayer = CreateExtendedPlayer(player, userData.accounts, userData.inventory, userData.job, userData.loadout, userData.playerName, userData.coords)
MySQL.Async.fetchAll('SELECT job, job_grade, loadout, position FROM users WHERE identifier = @identifier', {
['@identifier'] = player.getIdentifier()
}, function(result)
local job, grade = result[1].job, tostring(result[1].job_grade)
if ESX.DoesJobExist(job, grade) then
local jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
userData.job = {}
userData.job.id = jobObject.id
userData.job.name = jobObject.name
userData.job.label = jobObject.label
userData.job.grade = tonumber(grade)
userData.job.grade_name = gradeObject.name
userData.job.grade_label = gradeObject.label
userData.job.grade_salary = gradeObject.salary
userData.job.skin_male = {}
userData.job.skin_female = {}
if gradeObject.skin_male ~= nil then
userData.job.skin_male = json.decode(gradeObject.skin_male)
end
if gradeObject.skin_female ~= nil then
userData.job.skin_female = json.decode(gradeObject.skin_female)
end
else
print(('[es_extended] [^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(player.getIdentifier(), job, grade))
local job, grade = 'unemployed', '0'
local jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
userData.job = {}
userData.job.id = jobObject.id
userData.job.name = jobObject.name
userData.job.label = jobObject.label
userData.job.grade = tonumber(grade)
userData.job.grade_name = gradeObject.name
userData.job.grade_label = gradeObject.label
userData.job.grade_salary = gradeObject.salary
userData.job.skin_male = {}
userData.job.skin_female = {}
end
if result[1].loadout ~= nil then
userData.loadout = json.decode(result[1].loadout)
-- Compatibility with old loadouts prior to components update
for k,v in ipairs(userData.loadout) do
if v.components == nil then
v.components = {}
end
end
end
if result[1].position ~= nil then
userData.lastPosition = json.decode(result[1].position)
end
cb2()
end)
end)
Async.series(tasks2, cb)
end)
-- Run Tasks
Async.parallel(tasks, function(results)
local xPlayer = CreateExtendedPlayer(player, userData.accounts, userData.inventory, userData.job, userData.loadout, userData.playerName, userData.lastPosition)
xPlayer.getMissingAccounts(function(missingAccounts)
if #missingAccounts > 0 then
for i=1, #missingAccounts, 1 do
table.insert(xPlayer.accounts, {
name = missingAccounts[i],
money = 0,
label = Config.AccountLabels[missingAccounts[i]]
})
end
xPlayer.createAccounts(missingAccounts)
xPlayer.getMissingAccounts(function(missingAccounts)
if #missingAccounts > 0 then
for i=1, #missingAccounts, 1 do
table.insert(xPlayer.accounts, {
name = missingAccounts[i],
money = 0,
label = Config.AccountLabels[missingAccounts[i]]
})
end
ESX.Players[playerId] = xPlayer
xPlayer.createAccounts(missingAccounts)
end
TriggerEvent('esx:playerLoaded', playerId, xPlayer)
ESX.Players[playerId] = xPlayer
TriggerClientEvent('esx:playerLoaded', playerId, {
identifier = xPlayer.identifier,
accounts = xPlayer.getAccounts(),
inventory = xPlayer.getInventory(),
job = xPlayer.getJob(),
loadout = xPlayer.getLoadout(),
lastPosition = xPlayer.getLastPosition(),
money = xPlayer.getMoney(),
maxWeight = xPlayer.maxWeight
})
TriggerEvent('esx:playerLoaded', playerId, xPlayer)
xPlayer.displayMoney(xPlayer.getMoney())
TriggerClientEvent('esx:createMissingPickups', playerId, ESX.Pickups)
end)
xPlayer.triggerEvent('esx:playerLoaded', {
identifier = xPlayer.identifier,
accounts = xPlayer.getAccounts(),
coords = xPlayer.getCoords(),
inventory = xPlayer.getInventory(),
job = xPlayer.getJob(),
loadout = xPlayer.getLoadout(),
money = xPlayer.getMoney(),
maxWeight = xPlayer.maxWeight
})
xPlayer.displayMoney(xPlayer.getMoney())
xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups)
end)
end)
end)
@@ -245,15 +237,16 @@ AddEventHandler('playerDropped', function(reason)
end
end)
RegisterNetEvent('esx:updateCoords')
AddEventHandler('esx:updateCoords', function(coords)
local xPlayer = ESX.GetPlayerFromId(source)
RegisterServerEvent('esx:updateLastPosition')
AddEventHandler('esx:updateLastPosition', function(position)
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.setLastPosition(position)
if xPlayer then
xPlayer.updateCoords(coords)
end
end)
RegisterServerEvent('esx:giveInventoryItem')
RegisterNetEvent('esx:giveInventoryItem')
AddEventHandler('esx:giveInventoryItem', function(target, type, itemName, itemCount)
local playerId = source
local sourceXPlayer = ESX.GetPlayerFromId(playerId)
@@ -306,7 +299,7 @@ AddEventHandler('esx:giveInventoryItem', function(target, type, itemName, itemCo
sourceXPlayer.removeWeapon(itemName)
targetXPlayer.addWeapon(itemName, itemCount)
if itemCount > 0 then
sourceXPlayer.showNotification(_U('gave_weapon_withammo', weaponLabel, itemCount, targetXPlayer.name))
targetXPlayer.showNotification(_U('received_weapon_withammo', weaponLabel, itemCount, sourceXPlayer.name))
@@ -339,7 +332,7 @@ AddEventHandler('esx:giveInventoryItem', function(target, type, itemName, itemCo
end
end)
RegisterServerEvent('esx:removeInventoryItem')
RegisterNetEvent('esx:removeInventoryItem')
AddEventHandler('esx:removeInventoryItem', function(type, itemName, itemCount)
local playerId = source
local xPlayer = ESX.GetPlayerFromId(source)
@@ -406,7 +399,7 @@ AddEventHandler('esx:removeInventoryItem', function(type, itemName, itemCount)
end
end)
RegisterServerEvent('esx:useItem')
RegisterNetEvent('esx:useItem')
AddEventHandler('esx:useItem', function(itemName)
local xPlayer = ESX.GetPlayerFromId(source)
local count = xPlayer.getInventoryItem(itemName).count
@@ -418,7 +411,7 @@ AddEventHandler('esx:useItem', function(itemName)
end
end)
RegisterServerEvent('esx:onPickup')
RegisterNetEvent('esx:onPickup')
AddEventHandler('esx:onPickup', function(id)
local pickup, xPlayer, success = ESX.Pickups[id], ESX.GetPlayerFromId(source)
@@ -465,7 +458,6 @@ ESX.RegisterServerCallback('esx:getPlayerData', function(source, cb)
inventory = xPlayer.getInventory(),
job = xPlayer.getJob(),
loadout = xPlayer.getLoadout(),
lastPosition = xPlayer.getLastPosition(),
money = xPlayer.getMoney()
})
end)
@@ -479,7 +471,6 @@ ESX.RegisterServerCallback('esx:getOtherPlayerData', function(source, cb, target
inventory = xPlayer.getInventory(),
job = xPlayer.getJob(),
loadout = xPlayer.getLoadout(),
lastPosition = xPlayer.getLastPosition(),
money = xPlayer.getMoney()
})
end)