mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-09-04 16:23:21 +00:00
refactor(esx): various changes
Minor improvements to spawning. Moved internal variables out of the ESX namespace. SavePlayer function now uses a prepared statement. Replaced bulk SavePlayers query with prepared statements. Cleaned up newPlayer/loadPlayer queries. Player load and creation functions use prepared statements. Removed async dependency.
This commit is contained in:
@@ -21,7 +21,6 @@ Many more resources are included in this repository, or you can browse the [ESX
|
||||
|
||||
#### Optimisation
|
||||
- Utilise compile-time jenkins hashing over the GetHashKey native
|
||||
- Update old MySQL queries to use MySQL.store to improve performance, especially during player saving
|
||||
- Several loops will now sleep when their tasks are not necessary to perform
|
||||
- Improved support when using ESX Identity to reduce events and queries during player login
|
||||
- Support for the latest weapons and components
|
||||
|
||||
@@ -2,6 +2,6 @@ AddEventHandler('esx:getSharedObject', function(cb)
|
||||
cb(ESX)
|
||||
end)
|
||||
|
||||
function getSharedObject()
|
||||
exports('getSharedObject', function()
|
||||
return ESX
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
ESX = {}
|
||||
Core = {}
|
||||
ESX.PlayerData = {}
|
||||
ESX.PlayerLoaded = false
|
||||
ESX.CurrentRequestId = 0
|
||||
ESX.ServerCallbacks = {}
|
||||
ESX.TimeoutCallbacks = {}
|
||||
Core.CurrentRequestId = 0
|
||||
Core.ServerCallbacks = {}
|
||||
Core.TimeoutCallbacks = {}
|
||||
|
||||
ESX.UI = {}
|
||||
ESX.UI.HUD = {}
|
||||
@@ -21,15 +22,15 @@ ESX.Scaleform.Utils = {}
|
||||
ESX.Streaming = {}
|
||||
|
||||
ESX.SetTimeout = function(msec, cb)
|
||||
table.insert(ESX.TimeoutCallbacks, {
|
||||
table.insert(Core.TimeoutCallbacks, {
|
||||
time = GetGameTimer() + msec,
|
||||
cb = cb
|
||||
})
|
||||
return #ESX.TimeoutCallbacks
|
||||
return #Core.TimeoutCallbacks
|
||||
end
|
||||
|
||||
ESX.ClearTimeout = function(i)
|
||||
ESX.TimeoutCallbacks[i] = nil
|
||||
Core.TimeoutCallbacks[i] = nil
|
||||
end
|
||||
|
||||
ESX.IsPlayerLoaded = function()
|
||||
@@ -86,14 +87,14 @@ ESX.ShowFloatingHelpNotification = function(msg, coords)
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback = function(name, cb, ...)
|
||||
ESX.ServerCallbacks[ESX.CurrentRequestId] = cb
|
||||
Core.ServerCallbacks[Core.CurrentRequestId] = cb
|
||||
|
||||
TriggerServerEvent('esx:triggerServerCallback', name, ESX.CurrentRequestId, ...)
|
||||
TriggerServerEvent('esx:triggerServerCallback', name, Core.CurrentRequestId, ...)
|
||||
|
||||
if ESX.CurrentRequestId < 65535 then
|
||||
ESX.CurrentRequestId = ESX.CurrentRequestId + 1
|
||||
if Core.CurrentRequestId < 65535 then
|
||||
Core.CurrentRequestId = Core.CurrentRequestId + 1
|
||||
else
|
||||
ESX.CurrentRequestId = 0
|
||||
Core.CurrentRequestId = 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -849,7 +850,7 @@ ESX.ShowInventory = function()
|
||||
players[GetPlayerServerId(playerNearby)] = true
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback('esx:getPlayerNames', function(returnedPlayers)
|
||||
Core.TriggerServerCallback('esx:getPlayerNames', function(returnedPlayers)
|
||||
for playerId,playerName in pairs(returnedPlayers) do
|
||||
table.insert(elements, {
|
||||
label = playerName,
|
||||
@@ -989,8 +990,8 @@ end
|
||||
|
||||
RegisterNetEvent('esx:serverCallback')
|
||||
AddEventHandler('esx:serverCallback', function(requestId, ...)
|
||||
ESX.ServerCallbacks[requestId](...)
|
||||
ESX.ServerCallbacks[requestId] = nil
|
||||
Core.ServerCallbacks[requestId](...)
|
||||
Core.ServerCallbacks[requestId] = nil
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:showNotification')
|
||||
@@ -1012,13 +1013,13 @@ end)
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
local sleep = 100
|
||||
if #ESX.TimeoutCallbacks > 0 then
|
||||
if #Core.TimeoutCallbacks > 0 then
|
||||
local currTime = GetGameTimer()
|
||||
sleep = 0
|
||||
for i=1, #ESX.TimeoutCallbacks, 1 do
|
||||
if currTime >= ESX.TimeoutCallbacks[i].time then
|
||||
ESX.TimeoutCallbacks[i].cb()
|
||||
ESX.TimeoutCallbacks[i] = nil
|
||||
for i=1, #Core.TimeoutCallbacks, 1 do
|
||||
if currTime >= Core.TimeoutCallbacks[i].time then
|
||||
Core.TimeoutCallbacks[i].cb()
|
||||
Core.TimeoutCallbacks[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,15 +32,14 @@ AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin)
|
||||
}, function()
|
||||
TriggerServerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('playerSpawned') -- compatibility with old scripts
|
||||
TriggerEvent('esx:restoreLoadout')
|
||||
|
||||
if isNew then
|
||||
if skin.sex == 0 then
|
||||
TriggerEvent('skinchanger:loadDefaultModel', true)
|
||||
else
|
||||
TriggerEvent('skinchanger:loadDefaultModel', false)
|
||||
end
|
||||
elseif skin then TriggerEvent('skinchanger:loadSkin', skin) end
|
||||
TriggerEvent('skinchanger:loadDefaultModel', skin.sex == 0)
|
||||
elseif skin then
|
||||
TriggerEvent('skinchanger:loadSkin', skin)
|
||||
end
|
||||
|
||||
TriggerEvent('esx:loadingScreenOff')
|
||||
ShutdownLoadingScreen()
|
||||
ShutdownLoadingScreenNui()
|
||||
@@ -65,7 +64,7 @@ AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin)
|
||||
|
||||
local gradeLabel = ESX.PlayerData.job.grade_label ~= ESX.PlayerData.job.label and ESX.PlayerData.job.grade_label or ''
|
||||
if gradeLabel ~= '' then gradeLabel = ' - '..gradeLabel end
|
||||
|
||||
|
||||
ESX.UI.HUD.RegisterElement('job', #ESX.PlayerData.accounts, 0, jobTpl, {
|
||||
job_label = ESX.PlayerData.job.label,
|
||||
grade_label = gradeLabel
|
||||
@@ -83,10 +82,15 @@ end)
|
||||
RegisterNetEvent('esx:setMaxWeight')
|
||||
AddEventHandler('esx:setMaxWeight', function(newMaxWeight) ESX.PlayerData.maxWeight = newMaxWeight end)
|
||||
|
||||
AddEventHandler('esx:onPlayerSpawn', function()
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
ESX.SetPlayerData('dead', false)
|
||||
end)
|
||||
local function onPlayerSpawn()
|
||||
if ESX.PlayerLoaded then
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
ESX.SetPlayerData('dead', false)
|
||||
end
|
||||
end
|
||||
|
||||
AddEventHandler('playerSpawned', onPlayerSpawn)
|
||||
AddEventHandler('esx:onPlayerSpawn', onPlayerSpawn)
|
||||
|
||||
AddEventHandler('esx:onPlayerDeath', function()
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
@@ -282,7 +286,7 @@ end)
|
||||
|
||||
RegisterNetEvent('esx:createMissingPickups')
|
||||
AddEventHandler('esx:createMissingPickups', function(missingPickups)
|
||||
for pickupId,pickup in pairs(missingPickups) do
|
||||
for pickupId, pickup in pairs(missingPickups) do
|
||||
TriggerEvent('esx:createPickup', pickupId, pickup.label, pickup.coords, pickup.type, pickup.name, pickup.components, pickup.tintIndex)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -24,7 +24,6 @@ shared_scripts {
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@async/async.lua',
|
||||
'@mysql-async/lib/MySQL.lua',
|
||||
|
||||
'server/common.lua',
|
||||
@@ -78,16 +77,8 @@ files {
|
||||
'html/img/accounts/money.png'
|
||||
}
|
||||
|
||||
exports {
|
||||
'getSharedObject'
|
||||
}
|
||||
|
||||
server_exports {
|
||||
'getSharedObject'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
'mysql-async',
|
||||
'oxmysql',
|
||||
'async',
|
||||
'spawnmanager',
|
||||
}
|
||||
@@ -134,13 +134,13 @@ end, true, {help = _U('command_setgroup'), validate = true, arguments = {
|
||||
}})
|
||||
|
||||
ESX.RegisterCommand('save', 'admin', function(xPlayer, args, showError)
|
||||
ESX.SavePlayer(args.playerId)
|
||||
Core.SavePlayer(args.playerId)
|
||||
end, true, {help = _U('command_save'), validate = true, arguments = {
|
||||
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'}
|
||||
}})
|
||||
|
||||
ESX.RegisterCommand('saveall', 'admin', function(xPlayer, args, showError)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end, true, {help = _U('command_saveall')})
|
||||
|
||||
ESX.RegisterCommand('group', {"user", "admin"}, function(xPlayer, args, showError)
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
ESX = {}
|
||||
ESX.Players = {}
|
||||
ESX.UsableItemsCallbacks = {}
|
||||
ESX.Items = {}
|
||||
ESX.ServerCallbacks = {}
|
||||
ESX.TimeoutCount = -1
|
||||
ESX.CancelledTimeouts = {}
|
||||
ESX.Pickups = {}
|
||||
ESX.PickupId = 0
|
||||
ESX.Jobs = {}
|
||||
ESX.RegisteredCommands = {}
|
||||
ESX.Items = {}
|
||||
Core = {}
|
||||
Core.UsableItemsCallbacks = {}
|
||||
Core.ServerCallbacks = {}
|
||||
Core.TimeoutCount = -1
|
||||
Core.CancelledTimeouts = {}
|
||||
Core.RegisteredCommands = {}
|
||||
Core.Pickups = {}
|
||||
Core.PickupId = 0
|
||||
|
||||
AddEventHandler('esx:getSharedObject', function(cb)
|
||||
cb(ESX)
|
||||
end)
|
||||
|
||||
function getSharedObject()
|
||||
exports('getSharedObject', function()
|
||||
return ESX
|
||||
end
|
||||
end)
|
||||
|
||||
local function StartDBSync()
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(10 * 60 * 1000)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MySQL.ready(function()
|
||||
MySQL.Async.fetchAll('SELECT * FROM items', {}, function(result)
|
||||
MySQL.query('SELECT * FROM items', {}, function(result)
|
||||
for k,v in ipairs(result) do
|
||||
ESX.Items[v.name] = {
|
||||
label = v.label,
|
||||
@@ -40,13 +41,13 @@ MySQL.ready(function()
|
||||
end)
|
||||
|
||||
local Jobs = {}
|
||||
MySQL.Async.fetchAll('SELECT * FROM jobs', {}, function(jobs)
|
||||
MySQL.query('SELECT * FROM jobs', {}, function(jobs)
|
||||
for k,v in ipairs(jobs) do
|
||||
Jobs[v.name] = v
|
||||
Jobs[v.name].grades = {}
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM job_grades', {}, function(jobGrades)
|
||||
MySQL.query('SELECT * FROM job_grades', {}, function(jobGrades)
|
||||
for k,v in ipairs(jobGrades) do
|
||||
if Jobs[v.job_name] then
|
||||
Jobs[v.job_name].grades[tostring(v.grade)] = v
|
||||
@@ -80,7 +81,7 @@ RegisterServerEvent('esx:triggerServerCallback')
|
||||
AddEventHandler('esx:triggerServerCallback', function(name, requestId, ...)
|
||||
local playerId = source
|
||||
|
||||
ESX.TriggerServerCallback(name, requestId, playerId, function(...)
|
||||
Core.TriggerServerCallback(name, requestId, playerId, function(...)
|
||||
TriggerClientEvent('esx:serverCallback', playerId, requestId, ...)
|
||||
end, ...)
|
||||
end)
|
||||
|
||||
@@ -5,17 +5,17 @@ ESX.Trace = function(msg)
|
||||
end
|
||||
|
||||
ESX.SetTimeout = function(msec, cb)
|
||||
local id = ESX.TimeoutCount + 1
|
||||
local id = Core.TimeoutCount + 1
|
||||
|
||||
SetTimeout(msec, function()
|
||||
if ESX.CancelledTimeouts[id] then
|
||||
ESX.CancelledTimeouts[id] = nil
|
||||
if Core.CancelledTimeouts[id] then
|
||||
Core.CancelledTimeouts[id] = nil
|
||||
else
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
|
||||
ESX.TimeoutCount = id
|
||||
Core.TimeoutCount = id
|
||||
|
||||
return id
|
||||
end
|
||||
@@ -29,10 +29,10 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
return
|
||||
end
|
||||
|
||||
if ESX.RegisteredCommands[name] then
|
||||
if Core.RegisteredCommands[name] then
|
||||
print(('[^3WARNING^7] Command ^5"%s" already registered, overriding command'):format(name))
|
||||
|
||||
if ESX.RegisteredCommands[name].suggestion then
|
||||
if Core.RegisteredCommands[name].suggestion then
|
||||
TriggerClientEvent('chat:removeSuggestion', -1, ('/%s'):format(name))
|
||||
end
|
||||
end
|
||||
@@ -44,10 +44,10 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
TriggerClientEvent('chat:addSuggestion', -1, ('/%s'):format(name), suggestion.help, suggestion.arguments)
|
||||
end
|
||||
|
||||
ESX.RegisteredCommands[name] = {group = group, cb = cb, allowConsole = allowConsole, suggestion = suggestion}
|
||||
Core.RegisteredCommands[name] = {group = group, cb = cb, allowConsole = allowConsole, suggestion = suggestion}
|
||||
|
||||
RegisterCommand(name, function(playerId, args, rawCommand)
|
||||
local command = ESX.RegisteredCommands[name]
|
||||
local command = Core.RegisteredCommands[name]
|
||||
|
||||
if not command.allowConsole and playerId == 0 then
|
||||
print(('[^3WARNING^7] ^5%s'):format(_U('commanderror_console')))
|
||||
@@ -148,92 +148,60 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
end
|
||||
|
||||
ESX.ClearTimeout = function(id)
|
||||
ESX.CancelledTimeouts[id] = true
|
||||
Core.CancelledTimeouts[id] = true
|
||||
end
|
||||
|
||||
ESX.RegisterServerCallback = function(name, cb)
|
||||
ESX.ServerCallbacks[name] = cb
|
||||
Core.ServerCallbacks[name] = cb
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback = function(name, requestId, source, cb, ...)
|
||||
if ESX.ServerCallbacks[name] then
|
||||
ESX.ServerCallbacks[name](source, cb, ...)
|
||||
Core.TriggerServerCallback = function(name, requestId, source, cb, ...)
|
||||
if Core.ServerCallbacks[name] then
|
||||
Core.ServerCallbacks[name](source, cb, ...)
|
||||
else
|
||||
print(('[^3WARNING^7] Server callback ^5"%s"^0 does not exist. ^1Please Check The Server File for Errors!'):format(name))
|
||||
end
|
||||
end
|
||||
|
||||
local savePlayers = -1
|
||||
Citizen.CreateThread(function()
|
||||
savePlayers = MySQL.Sync.store("UPDATE users SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ? WHERE `identifier` = ?")
|
||||
end)
|
||||
|
||||
ESX.SavePlayer = function(xPlayer, cb)
|
||||
local asyncTasks = {}
|
||||
|
||||
table.insert(asyncTasks, function(cb2)
|
||||
MySQL.Async.execute(savePlayers, {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.getGroup(),
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.getIdentifier()
|
||||
}, function(rowsChanged)
|
||||
cb2()
|
||||
end)
|
||||
end)
|
||||
|
||||
Async.parallel(asyncTasks, function(results)
|
||||
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.getName()))
|
||||
|
||||
if cb then
|
||||
cb()
|
||||
Core.SavePlayer = function(xPlayer, cb)
|
||||
MySQL.prepare('UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ? WHERE `identifier` = ?'', {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.group,
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
xPlayer.identifier
|
||||
}, function(affectedRows)
|
||||
if affectedRows == 1 then
|
||||
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.name))
|
||||
end
|
||||
if cb then cb() end
|
||||
end)
|
||||
end
|
||||
|
||||
ESX.SavePlayers = function(cb)
|
||||
Core.SavePlayers = function(cb)
|
||||
local xPlayers = ESX.GetExtendedPlayers()
|
||||
if #xPlayers > 0 then
|
||||
local count = #xPlayers
|
||||
if count > 0 then
|
||||
local parameters = {}
|
||||
local time = os.time()
|
||||
|
||||
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_loadout, '%s' AS new_position, '%s' AS new_inventory "
|
||||
local selectListNoNames = "SELECT '%s', '%s', '%s' , %s, '%s', '%s', '%s', '%s' "
|
||||
|
||||
local updateCommand = 'UPDATE users u JOIN ('
|
||||
|
||||
local selectList = selectListNoNames
|
||||
local first = true
|
||||
for k, xPlayer in pairs(xPlayers) do
|
||||
if first == false then
|
||||
updateCommand = updateCommand .. ' UNION '
|
||||
else
|
||||
selectList = selectListWithNames
|
||||
end
|
||||
|
||||
updateCommand = updateCommand .. string.format(selectList,
|
||||
xPlayer.identifier,
|
||||
for i=1, count do
|
||||
local xPlayer = xPlayers[i]
|
||||
parameters[#parameters+1] = {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.getGroup(),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.group,
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true))
|
||||
)
|
||||
|
||||
first = false
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
xPlayer.identifier
|
||||
}
|
||||
end
|
||||
|
||||
updateCommand = updateCommand .. ' ) vals ON u.identifier = vals.identifier SET accounts = new_accounts, job = new_job, job_grade = new_job_grade, `group` = new_group, loadout = new_loadout, `position` = new_position, inventory = new_inventory'
|
||||
|
||||
MySQL.Async.fetchAll(updateCommand, {},
|
||||
function(result)
|
||||
if result then
|
||||
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
|
||||
MySQL.prepare("UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ? WHERE `identifier` = ?", parameters,
|
||||
function(results)
|
||||
if results then
|
||||
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
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -285,11 +253,11 @@ ESX.GetIdentifier = function(playerId)
|
||||
end
|
||||
|
||||
ESX.RegisterUsableItem = function(item, cb)
|
||||
ESX.UsableItemsCallbacks[item] = cb
|
||||
Core.UsableItemsCallbacks[item] = cb
|
||||
end
|
||||
|
||||
ESX.UseItem = function(source, item)
|
||||
ESX.UsableItemsCallbacks[item](source, item)
|
||||
Core.UsableItemsCallbacks[item](source, item)
|
||||
end
|
||||
|
||||
ESX.GetItemLabel = function(item)
|
||||
@@ -298,24 +266,36 @@ ESX.GetItemLabel = function(item)
|
||||
end
|
||||
end
|
||||
|
||||
ESX.GetJobs = function()
|
||||
return ESX.Jobs
|
||||
end
|
||||
|
||||
ESX.GetUsableItems = function()
|
||||
local Usables = {}
|
||||
for k in pairs(Core.UsableItemsCallbacks) do
|
||||
Usables[k] = true
|
||||
end
|
||||
return Usables
|
||||
end
|
||||
|
||||
ESX.CreatePickup = function(type, name, count, label, playerId, components, tintIndex)
|
||||
local pickupId = (ESX.PickupId == 65635 and 0 or ESX.PickupId + 1)
|
||||
local pickupId = (Core.PickupId == 65635 and 0 or Core.PickupId + 1)
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
local coords = xPlayer.getCoords()
|
||||
|
||||
ESX.Pickups[pickupId] = {
|
||||
Core.Pickups[pickupId] = {
|
||||
type = type, name = name,
|
||||
count = count, label = label,
|
||||
coords = coords
|
||||
}
|
||||
|
||||
if type == 'item_weapon' then
|
||||
ESX.Pickups[pickupId].components = components
|
||||
ESX.Pickups[pickupId].tintIndex = tintIndex
|
||||
Core.Pickups[pickupId].components = components
|
||||
Core.Pickups[pickupId].tintIndex = tintIndex
|
||||
end
|
||||
|
||||
TriggerClientEvent('esx:createPickup', -1, pickupId, label, coords, type, name, components, tintIndex)
|
||||
ESX.PickupId = pickupId
|
||||
Core.PickupId = pickupId
|
||||
end
|
||||
|
||||
ESX.DoesJobExist = function(job, grade)
|
||||
@@ -329,3 +309,18 @@ ESX.DoesJobExist = function(job, grade)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
Core.IsPlayerAdmin = function(playerId)
|
||||
if (IsPlayerAceAllowed(playerId, 'command') or GetConvar('sv_lan', '') == 'true') and true or false then
|
||||
return true
|
||||
end
|
||||
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
if xPlayer then
|
||||
if xPlayer.group == 'admin' or xPlayer.group == 'superadmin' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
+182
-204
@@ -1,26 +1,16 @@
|
||||
local NewPlayer, LoadPlayer = -1, -1
|
||||
local newPlayer = 'INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?'
|
||||
local loadPlayer = 'SELECT `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`'
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
SetMapName('San Andreas')
|
||||
SetGameType('ESX Legacy')
|
||||
|
||||
local query = '`accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`' -- Select these fields from the database
|
||||
if Config.Multichar or Config.Identity then -- append these fields to the select query
|
||||
query = query..', `firstname`, `lastname`, `dateofbirth`, `sex`, `height`'
|
||||
|
||||
if Config.Multichar or Config.Identity then
|
||||
newPlayer = newPlayer..', `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?"'
|
||||
loadPlayer = loadPlayer..', `firstname`, `lastname`, `dateofbirth`, `sex`, `height`'
|
||||
end
|
||||
|
||||
if Config.Multichar then -- insert identity data with creation
|
||||
MySQL.Async.store("INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?, `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?", function(storeId)
|
||||
NewPlayer = storeId
|
||||
end)
|
||||
else
|
||||
MySQL.Async.store("INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?", function(storeId)
|
||||
NewPlayer = storeId
|
||||
end)
|
||||
end
|
||||
|
||||
MySQL.Async.store("SELECT "..query.." FROM `users` WHERE identifier = ?", function(storeId)
|
||||
LoadPlayer = storeId
|
||||
end)
|
||||
loadPlayer = loadPlayer..' FROM `users` WHERE identifier = ?'
|
||||
end)
|
||||
|
||||
if Config.Multichar then
|
||||
@@ -49,13 +39,12 @@ function onPlayerJoined(playerId)
|
||||
if ESX.GetPlayerFromIdentifier(identifier) then
|
||||
DropPlayer(playerId, ('there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s'):format(identifier))
|
||||
else
|
||||
MySQL.Async.fetchScalar('SELECT 1 FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result then
|
||||
loadESXPlayer(identifier, playerId, false)
|
||||
else createESXPlayer(identifier, playerId) end
|
||||
end)
|
||||
local result = MySQL.scalar.await('SELECT 1 FROM users WHERE identifier = ?', { identifier })
|
||||
if result then
|
||||
loadESXPlayer(identifier, playerId, false)
|
||||
else
|
||||
createESXPlayer(identifier, playerId)
|
||||
end
|
||||
end
|
||||
else
|
||||
DropPlayer(playerId, 'there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.')
|
||||
@@ -69,7 +58,7 @@ function createESXPlayer(identifier, playerId, data)
|
||||
accounts[account] = money
|
||||
end
|
||||
|
||||
if IsPlayerAceAllowed(playerId, "command") then
|
||||
if Core.IsPlayerAdmin(playerId) then
|
||||
print(('^2[INFO] ^0 Player ^5%s ^0Has been granted admin permissions via ^5Ace Perms.^7'):format(playerId))
|
||||
defaultGroup = "admin"
|
||||
else
|
||||
@@ -77,15 +66,15 @@ function createESXPlayer(identifier, playerId, data)
|
||||
end
|
||||
|
||||
if not Config.Multichar then
|
||||
MySQL.Async.execute(NewPlayer, {
|
||||
MySQL.prepare(newPlayer, {
|
||||
json.encode(accounts),
|
||||
identifier,
|
||||
defaultGroup,
|
||||
}, function(rowsChanged)
|
||||
}, function()
|
||||
loadESXPlayer(identifier, playerId, true)
|
||||
end)
|
||||
else
|
||||
MySQL.Async.execute(NewPlayer, {
|
||||
MySQL.prepare(newPlayer, {
|
||||
json.encode(accounts),
|
||||
identifier,
|
||||
defaultGroup,
|
||||
@@ -94,7 +83,7 @@ function createESXPlayer(identifier, playerId, data)
|
||||
data.dateofbirth,
|
||||
data.sex,
|
||||
data.height,
|
||||
}, function(rowsChanged)
|
||||
}, function()
|
||||
loadESXPlayer(identifier, playerId, true)
|
||||
end)
|
||||
end
|
||||
@@ -104,7 +93,6 @@ AddEventHandler('playerConnecting', function(name, setCallback, deferrals)
|
||||
deferrals.defer()
|
||||
local playerId = source
|
||||
local identifier = ESX.GetIdentifier(playerId)
|
||||
Citizen.Wait(100)
|
||||
|
||||
if identifier then
|
||||
if ESX.GetPlayerFromIdentifier(identifier) then
|
||||
@@ -118,8 +106,6 @@ AddEventHandler('playerConnecting', function(name, setCallback, deferrals)
|
||||
end)
|
||||
|
||||
function loadESXPlayer(identifier, playerId, isNew)
|
||||
local tasks = {}
|
||||
|
||||
local userData = {
|
||||
accounts = {},
|
||||
inventory = {},
|
||||
@@ -129,179 +115,171 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
weight = 0
|
||||
}
|
||||
|
||||
table.insert(tasks, function(cb)
|
||||
MySQL.Async.fetchAll(LoadPlayer, { identifier
|
||||
}, function(result)
|
||||
local job, grade, jobObject, gradeObject = result[1].job, tostring(result[1].job_grade)
|
||||
local foundAccounts, foundItems = {}, {}
|
||||
local result = MySQL.prepare.await(loadPlayer, { identifier })
|
||||
local job, grade, jobObject, gradeObject = result.job, tostring(result.job_grade)
|
||||
local foundAccounts, foundItems = {}, {}
|
||||
|
||||
-- Accounts
|
||||
if result[1].accounts and result[1].accounts ~= '' then
|
||||
local accounts = json.decode(result[1].accounts)
|
||||
-- Accounts
|
||||
if result.accounts and result.accounts ~= '' then
|
||||
local accounts = json.decode(result.accounts)
|
||||
|
||||
for account,money in pairs(accounts) do
|
||||
foundAccounts[account] = money
|
||||
end
|
||||
end
|
||||
|
||||
for account,label in pairs(Config.Accounts) do
|
||||
table.insert(userData.accounts, {
|
||||
name = account,
|
||||
money = foundAccounts[account] or Config.StartingAccountMoney[account] or 0,
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
-- Job
|
||||
if ESX.DoesJobExist(job, grade) then
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(identifier, job, grade))
|
||||
job, grade = 'unemployed', '0'
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
-- Inventory
|
||||
if result[1].inventory and result[1].inventory ~= '' then
|
||||
local inventory = json.decode(result[1].inventory)
|
||||
|
||||
for name,count in pairs(inventory) do
|
||||
local item = ESX.Items[name]
|
||||
|
||||
if item then
|
||||
foundItems[name] = count
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for name,item in pairs(ESX.Items) do
|
||||
local count = foundItems[name] or 0
|
||||
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
|
||||
|
||||
table.insert(userData.inventory, {
|
||||
name = name,
|
||||
count = count,
|
||||
label = item.label,
|
||||
weight = item.weight,
|
||||
usable = ESX.UsableItemsCallbacks[name] ~= nil,
|
||||
rare = item.rare,
|
||||
canRemove = item.canRemove
|
||||
})
|
||||
end
|
||||
|
||||
table.sort(userData.inventory, function(a, b)
|
||||
return a.label < b.label
|
||||
end)
|
||||
|
||||
-- Group
|
||||
if result[1].group then
|
||||
if result[1].group == "superadmin" then
|
||||
userData.group = "admin"
|
||||
else
|
||||
userData.group = result[1].group
|
||||
end
|
||||
else
|
||||
userData.group = 'user'
|
||||
end
|
||||
|
||||
-- Loadout
|
||||
if result[1].loadout and result[1].loadout ~= '' then
|
||||
local loadout = json.decode(result[1].loadout)
|
||||
|
||||
for name,weapon in pairs(loadout) do
|
||||
local label = ESX.GetWeaponLabel(name)
|
||||
|
||||
if label then
|
||||
if not weapon.components then weapon.components = {} end
|
||||
if not weapon.tintIndex then weapon.tintIndex = 0 end
|
||||
|
||||
table.insert(userData.loadout, {
|
||||
name = name,
|
||||
ammo = weapon.ammo,
|
||||
label = label,
|
||||
components = weapon.components,
|
||||
tintIndex = weapon.tintIndex
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Position
|
||||
if result[1].position and result[1].position ~= '' then
|
||||
userData.coords = json.decode(result[1].position)
|
||||
else
|
||||
print('[^3WARNING^7] Column ^5"position"^0 in ^5"users"^0 table is missing required default value. Using backup coords, fix your database.')
|
||||
userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8}
|
||||
end
|
||||
|
||||
-- Skin
|
||||
if result[1].skin and result[1].skin ~= '' then
|
||||
userData.skin = json.decode(result[1].skin)
|
||||
else
|
||||
if userData.sex == 'f' then userData.skin = {sex=1} else userData.skin = {sex=0} end
|
||||
end
|
||||
|
||||
-- Identity
|
||||
if result[1].firstname and result[1].firstname ~= '' then
|
||||
userData.firstname = result[1].firstname
|
||||
userData.lastname = result[1].lastname
|
||||
userData.playerName = userData.firstname..' '..userData.lastname
|
||||
if result[1].dateofbirth then userData.dateofbirth = result[1].dateofbirth end
|
||||
if result[1].sex then userData.sex = result[1].sex end
|
||||
if result[1].height then userData.height = result[1].height end
|
||||
end
|
||||
|
||||
cb()
|
||||
end)
|
||||
end)
|
||||
|
||||
Async.parallel(tasks, function(results)
|
||||
local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, userData.playerName, userData.coords)
|
||||
ESX.Players[playerId] = xPlayer
|
||||
|
||||
if userData.firstname then
|
||||
xPlayer.set('firstName', userData.firstname)
|
||||
xPlayer.set('lastName', userData.lastname)
|
||||
if userData.dateofbirth then xPlayer.set('dateofbirth', userData.dateofbirth) end
|
||||
if userData.sex then xPlayer.set('sex', userData.sex) end
|
||||
if userData.height then xPlayer.set('height', userData.height) end
|
||||
for account,money in pairs(accounts) do
|
||||
foundAccounts[account] = money
|
||||
end
|
||||
end
|
||||
|
||||
TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
|
||||
for account,label in pairs(Config.Accounts) do
|
||||
table.insert(userData.accounts, {
|
||||
name = account,
|
||||
money = foundAccounts[account] or Config.StartingAccountMoney[account] or 0,
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
xPlayer.triggerEvent('esx:playerLoaded', {
|
||||
accounts = xPlayer.getAccounts(),
|
||||
coords = xPlayer.getCoords(),
|
||||
identifier = xPlayer.getIdentifier(),
|
||||
inventory = xPlayer.getInventory(),
|
||||
job = xPlayer.getJob(),
|
||||
loadout = xPlayer.getLoadout(),
|
||||
maxWeight = xPlayer.getMaxWeight(),
|
||||
money = xPlayer.getMoney(),
|
||||
dead = false
|
||||
}, isNew, userData.skin)
|
||||
-- Job
|
||||
if ESX.DoesJobExist(job, grade) then
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(identifier, job, grade))
|
||||
job, grade = 'unemployed', '0'
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
end
|
||||
|
||||
xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups)
|
||||
xPlayer.triggerEvent('esx:registerSuggestions', ESX.RegisteredCommands)
|
||||
print(('[^2INFO^0] Player ^5"%s" ^0has connected to the server. ID: ^5%s^7'):format(xPlayer.getName(), playerId))
|
||||
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
|
||||
|
||||
-- Inventory
|
||||
if result.inventory and result.inventory ~= '' then
|
||||
local inventory = json.decode(result.inventory)
|
||||
|
||||
for name,count in pairs(inventory) do
|
||||
local item = ESX.Items[name]
|
||||
|
||||
if item then
|
||||
foundItems[name] = count
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for name,item in pairs(ESX.Items) do
|
||||
local count = foundItems[name] or 0
|
||||
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
|
||||
|
||||
table.insert(userData.inventory, {
|
||||
name = name,
|
||||
count = count,
|
||||
label = item.label,
|
||||
weight = item.weight,
|
||||
usable = Core.UsableItemsCallbacks[name] ~= nil,
|
||||
rare = item.rare,
|
||||
canRemove = item.canRemove
|
||||
})
|
||||
end
|
||||
|
||||
table.sort(userData.inventory, function(a, b)
|
||||
return a.label < b.label
|
||||
end)
|
||||
|
||||
-- Group
|
||||
if result.group then
|
||||
if result.group == "superadmin" then
|
||||
userData.group = "admin"
|
||||
else
|
||||
userData.group = result.group
|
||||
end
|
||||
else
|
||||
userData.group = 'user'
|
||||
end
|
||||
|
||||
-- Loadout
|
||||
if result.loadout and result.loadout ~= '' then
|
||||
local loadout = json.decode(result.loadout)
|
||||
|
||||
for name,weapon in pairs(loadout) do
|
||||
local label = ESX.GetWeaponLabel(name)
|
||||
|
||||
if label then
|
||||
if not weapon.components then weapon.components = {} end
|
||||
if not weapon.tintIndex then weapon.tintIndex = 0 end
|
||||
|
||||
table.insert(userData.loadout, {
|
||||
name = name,
|
||||
ammo = weapon.ammo,
|
||||
label = label,
|
||||
components = weapon.components,
|
||||
tintIndex = weapon.tintIndex
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Position
|
||||
if result.position and result.position ~= '' then
|
||||
userData.coords = json.decode(result.position)
|
||||
else
|
||||
print('[^3WARNING^7] Column ^5"position"^0 in ^5"users"^0 table is missing required default value. Using backup coords, fix your database.')
|
||||
userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8}
|
||||
end
|
||||
|
||||
-- Skin
|
||||
if result.skin and result.skin ~= '' then
|
||||
userData.skin = json.decode(result.skin)
|
||||
else
|
||||
if userData.sex == 'f' then userData.skin = {sex=1} else userData.skin = {sex=0} end
|
||||
end
|
||||
|
||||
-- Identity
|
||||
if result.firstname and result.firstname ~= '' then
|
||||
userData.firstname = result.firstname
|
||||
userData.lastname = result.lastname
|
||||
userData.playerName = userData.firstname..' '..userData.lastname
|
||||
if result.dateofbirth then userData.dateofbirth = result.dateofbirth end
|
||||
if result.sex then userData.sex = result.sex end
|
||||
if result.height then userData.height = result.height end
|
||||
end
|
||||
|
||||
local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, userData.playerName, userData.coords)
|
||||
ESX.Players[playerId] = xPlayer
|
||||
|
||||
if userData.firstname then
|
||||
xPlayer.set('firstName', userData.firstname)
|
||||
xPlayer.set('lastName', userData.lastname)
|
||||
if userData.dateofbirth then xPlayer.set('dateofbirth', userData.dateofbirth) end
|
||||
if userData.sex then xPlayer.set('sex', userData.sex) end
|
||||
if userData.height then xPlayer.set('height', userData.height) end
|
||||
end
|
||||
|
||||
TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
|
||||
|
||||
xPlayer.triggerEvent('esx:playerLoaded', {
|
||||
accounts = xPlayer.getAccounts(),
|
||||
coords = xPlayer.getCoords(),
|
||||
identifier = xPlayer.getIdentifier(),
|
||||
inventory = xPlayer.getInventory(),
|
||||
job = xPlayer.getJob(),
|
||||
loadout = xPlayer.getLoadout(),
|
||||
maxWeight = xPlayer.getMaxWeight(),
|
||||
money = xPlayer.getMoney(),
|
||||
dead = false
|
||||
}, isNew, userData.skin)
|
||||
|
||||
xPlayer.triggerEvent('esx:createMissingPickups', Core.Pickups)
|
||||
xPlayer.triggerEvent('esx:registerSuggestions', Core.RegisteredCommands)
|
||||
print(('[^2INFO^0] Player ^5"%s" ^0has connected to the server. ID: ^5%s^7'):format(xPlayer.getName(), playerId))
|
||||
end
|
||||
|
||||
AddEventHandler('chatMessage', function(playerId, author, message)
|
||||
@@ -319,7 +297,7 @@ AddEventHandler('playerDropped', function(reason)
|
||||
if xPlayer then
|
||||
TriggerEvent('esx:playerDropped', playerId, reason)
|
||||
|
||||
ESX.SavePlayer(xPlayer, function()
|
||||
Core.SavePlayer(xPlayer, function()
|
||||
ESX.Players[playerId] = nil
|
||||
end)
|
||||
end
|
||||
@@ -331,7 +309,7 @@ if Config.Multichar then
|
||||
if xPlayer then
|
||||
TriggerEvent('esx:playerDropped', playerId, reason)
|
||||
|
||||
ESX.SavePlayer(xPlayer, function()
|
||||
Core.SavePlayer(xPlayer, function()
|
||||
ESX.Players[playerId] = nil
|
||||
end)
|
||||
end
|
||||
@@ -512,7 +490,7 @@ end)
|
||||
|
||||
RegisterNetEvent('esx:onPickup')
|
||||
AddEventHandler('esx:onPickup', function(pickupId)
|
||||
local pickup, xPlayer, success = ESX.Pickups[pickupId], ESX.GetPlayerFromId(source)
|
||||
local pickup, xPlayer, success = Core.Pickups[pickupId], ESX.GetPlayerFromId(source)
|
||||
|
||||
if pickup then
|
||||
if pickup.type == 'item_standard' then
|
||||
@@ -540,7 +518,7 @@ AddEventHandler('esx:onPickup', function(pickupId)
|
||||
end
|
||||
|
||||
if success then
|
||||
ESX.Pickups[pickupId] = nil
|
||||
Core.Pickups[pickupId] = nil
|
||||
TriggerClientEvent('esx:removePickup', -1, pickupId)
|
||||
end
|
||||
end
|
||||
@@ -592,7 +570,7 @@ AddEventHandler('txAdmin:events:scheduledRestart', function(eventData)
|
||||
if eventData.secondsRemaining == 60 then
|
||||
Citizen.CreateThread(function()
|
||||
Citizen.Wait(50000)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user