Merge pull request #1169 from thelindat/multichar

feat+improvements(legacy): Multicharacter support; bug fixes and optimisation; retrieve player identity and death status on load
This commit is contained in:
Taso
2021-06-04 21:17:32 -04:00
committed by GitHub
11 changed files with 265 additions and 123 deletions
+25 -13
View File
@@ -42,7 +42,9 @@ end
ESX.SetPlayerData = function(key, val) ESX.SetPlayerData = function(key, val)
ESX.PlayerData[key] = val ESX.PlayerData[key] = val
TriggerEvent('esx:setPlayerData', key, val) if key ~= 'inventory' and key ~= 'loadout' then
TriggerEvent('esx:setPlayerData', key, val)
end
end end
ESX.ShowNotification = function(msg) ESX.ShowNotification = function(msg)
@@ -141,6 +143,13 @@ ESX.UI.HUD.RemoveElement = function(name)
}) })
end end
ESX.UI.HUD.Reset = function()
SendNUIMessage({
action = 'resetHUDElements'
})
ESX.UI.HUD.RegisteredElements = {}
end
ESX.UI.HUD.UpdateElement = function(name, data) ESX.UI.HUD.UpdateElement = function(name, data)
SendNUIMessage({ SendNUIMessage({
action = 'updateHUDElement', action = 'updateHUDElement',
@@ -314,7 +323,7 @@ ESX.Game.Teleport = function(entity, coords, cb)
if DoesEntityExist(entity) then if DoesEntityExist(entity) then
RequestCollisionAtCoord(vector.xyz) RequestCollisionAtCoord(vector.xyz)
while not HasCollisionLoadedAroundEntity(entity) do while not HasCollisionLoadedAroundEntity(entity) do
Wait(0) Citizen.Wait(0)
end end
SetEntityCoords(entity, vector.xyz, false, false, false, false) SetEntityCoords(entity, vector.xyz, false, false, false, false)
@@ -332,7 +341,7 @@ ESX.Game.SpawnObject = function(object, coords, cb, networked, dynamic)
networked = networked == nil and true or false networked = networked == nil and true or false
dynamic = dynamic ~= nil and true or false dynamic = dynamic ~= nil and true or false
CreateThread(function() Citizen.CreateThread(function()
ESX.Streaming.RequestModel(model) ESX.Streaming.RequestModel(model)
-- The below has to be done just for CreateObject since for some reason CreateObjects model argument is set -- The below has to be done just for CreateObject since for some reason CreateObjects model argument is set
@@ -364,7 +373,7 @@ ESX.Game.SpawnVehicle = function(vehicle, coords, heading, cb, networked)
local model = (type(vehicle) == 'number' and vehicle or GetHashKey(vehicle)) local model = (type(vehicle) == 'number' and vehicle or GetHashKey(vehicle))
local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z) local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z)
networked = networked == nil and true or false networked = networked == nil and true or false
CreateThread(function() Citizen.CreateThread(function()
ESX.Streaming.RequestModel(model) ESX.Streaming.RequestModel(model)
local vehicle = CreateVehicle(model, vector.xyz, heading, networked, false) local vehicle = CreateVehicle(model, vector.xyz, heading, networked, false)
@@ -381,7 +390,7 @@ ESX.Game.SpawnVehicle = function(vehicle, coords, heading, cb, networked)
RequestCollisionAtCoord(vector.xyz) RequestCollisionAtCoord(vector.xyz)
while not HasCollisionLoadedAroundEntity(vehicle) do while not HasCollisionLoadedAroundEntity(vehicle) do
Wait(0) Citizen.Wait(0)
end end
if cb then if cb then
@@ -699,14 +708,20 @@ ESX.Game.SetVehicleProperties = function(vehicle, props)
end end
end end
ESX.Game.Utils.DrawText3D = function(coords, text, scale, font) ESX.Game.Utils.DrawText3D = function(coords, text, size, font)
local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z) local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z)
if scale then scale = scale + 0.0 end
if not scale then scale = 0.35 end local camCoords = GetGameplayCamCoords()
if not font then font = 4 end local distance = #(vector - camCoords)
SetTextScale(scale, scale) if not size then size = 1 end
if not font then font = 0 end
local scale = (size / distance) * 2
local fov = (1 / GetGameplayCamFov()) * 100
scale = scale * fov
SetTextScale(0.0 * scale, 0.55 * scale)
SetTextFont(font) SetTextFont(font)
SetTextProportional(1) SetTextProportional(1)
SetTextColour(255, 255, 255, 215) SetTextColour(255, 255, 255, 215)
@@ -715,9 +730,6 @@ ESX.Game.Utils.DrawText3D = function(coords, text, scale, font)
AddTextComponentString(text) AddTextComponentString(text)
SetDrawOrigin(vector.xyz, 0) SetDrawOrigin(vector.xyz, 0)
DrawText(0.0, 0.0) DrawText(0.0, 0.0)
local scaleFactor = scale / 0.35
local length = string.len(string.gsub(text, "(~[rbgypcmuosh]~)", ""))
DrawRect(0.0, 0.0 + 0.0111 * scaleFactor, 0.027 + (length / 370) * scaleFactor, 0.03 * scaleFactor, 0, 0, 0, 75)
ClearDrawOrigin() ClearDrawOrigin()
end end
+59 -36
View File
@@ -1,23 +1,52 @@
local isPaused, isDead, pickups = false, false, {} local pickups = {}
Citizen.CreateThread(function() Citizen.CreateThread(function()
while NetworkIsPlayerActive(PlayerId()) do while NetworkIsPlayerActive(PlayerId()) and not Config.Multichar do
Citizen.Wait(5) Citizen.Wait(5)
DoScreenFadeOut(0)
TriggerServerEvent('esx:onPlayerJoined') TriggerServerEvent('esx:onPlayerJoined')
break break
end end
end) end)
RegisterNetEvent('esx:playerLoaded') RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(playerData, isNew) AddEventHandler('esx:playerLoaded', function(playerData, isNew, skin)
ESX.PlayerLoaded = true ESX.PlayerLoaded = true
ESX.PlayerData = playerData ESX.PlayerData = playerData
ESX.PlayerData.ped = ESX.PlayerData.ped
FreezeEntityPosition(PlayerPedId(), true)
FreezeEntityPosition(ESX.PlayerData.ped, true) if Config.Multichar then
TriggerEvent('esx_multicharacter:SpawnCharacter', playerData.coords, isNew)
Citizen.Wait(3000)
else
exports.spawnmanager:spawnPlayer({
x = playerData.coords.x,
y = playerData.coords.y,
z = playerData.coords.z + 0.25,
heading = playerData.coords.heading,
model = `mp_m_freemode_01`,
skipFade = false
}, 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('esx:loadingScreenOff')
ShutdownLoadingScreen()
ShutdownLoadingScreenNui()
FreezeEntityPosition(ESX.PlayerData.ped, false)
end)
end
while ESX.PlayerData.ped == nil do Citizen.Wait(20) end
-- enable PVP -- enable PVP
if Config.EnablePVP then if Config.EnablePVP then
SetCanAttackFriendly(ESX.PlayerData.ped, true, false) SetCanAttackFriendly(ESX.PlayerData.ped, true, false)
@@ -41,26 +70,13 @@ AddEventHandler('esx:playerLoaded', function(playerData, isNew)
grade_label = playerData.job.grade_label grade_label = playerData.job.grade_label
}) })
end end
StartServerSyncLoops()
end)
exports.spawnmanager:spawnPlayer({ RegisterNetEvent('esx:onPlayerLogout')
x = playerData.coords.x, AddEventHandler('esx:onPlayerLogout', function()
y = playerData.coords.y, ESX.PlayerLoaded = false
z = playerData.coords.z + 0.25, if Config.EnableHud then ESX.UI.HUD.Reset() end
heading = playerData.coords.heading,
model = `mp_m_freemode_01`,
skipFade = false
}, function()
TriggerEvent('esx:onPlayerSpawn')
TriggerEvent('playerSpawned') -- compatibility with old scripts, will be removed soon.
TriggerEvent('esx:restoreLoadout')
Citizen.Wait(4000)
ShutdownLoadingScreen()
ShutdownLoadingScreenNui()
FreezeEntityPosition(ESX.PlayerData.ped, false)
StartServerSyncLoops()
end)
TriggerEvent('esx:loadingScreenOff') -- compatibility with old scripts, will be removed soon.
end) end)
RegisterNetEvent('esx:setMaxWeight') RegisterNetEvent('esx:setMaxWeight')
@@ -70,14 +86,12 @@ AddEventHandler('esx:onPlayerSpawn', function()
local playerPed = PlayerPedId() local playerPed = PlayerPedId()
if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end
ESX.SetPlayerData('dead', false) ESX.SetPlayerData('dead', false)
isDead = false
end) end)
AddEventHandler('esx:onPlayerDeath', function() AddEventHandler('esx:onPlayerDeath', function()
local playerPed = PlayerPedId() local playerPed = PlayerPedId()
if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end
ESX.SetPlayerData('dead', true) ESX.SetPlayerData('dead', true)
isDead = true
end) end)
AddEventHandler('skinchanger:modelLoaded', function() AddEventHandler('skinchanger:modelLoaded', function()
@@ -335,6 +349,7 @@ end)
-- Pause menu disables HUD display -- Pause menu disables HUD display
if Config.EnableHud then if Config.EnableHud then
Citizen.CreateThread(function() Citizen.CreateThread(function()
local isPaused = false
while true do while true do
Citizen.Wait(300) Citizen.Wait(300)
@@ -356,10 +371,17 @@ end
function StartServerSyncLoops() function StartServerSyncLoops()
-- keep track of ammo -- keep track of ammo
Citizen.CreateThread(function() Citizen.CreateThread(function()
local currentWeapon = {timer=0}
while ESX.PlayerLoaded do while ESX.PlayerLoaded do
Citizen.Wait(1000) local sleep = 5
local letSleep = true if currentWeapon.timer == sleep then
local ammoCount = GetAmmoInPedWeapon(ESX.PlayerData.ped, currentWeapon.hash)
TriggerServerEvent('esx:updateWeaponAmmo', currentWeapon.name, ammoCount)
currentWeapon.timer = 0
elseif currentWeapon.timer > sleep then
currentWeapon.timer = currentWeapon.timer - sleep
end
if IsPedArmed(ESX.PlayerData.ped, 4) then if IsPedArmed(ESX.PlayerData.ped, 4) then
if IsPedShooting(ESX.PlayerData.ped) then if IsPedShooting(ESX.PlayerData.ped) then
@@ -367,14 +389,15 @@ function StartServerSyncLoops()
local weapon = ESX.GetWeaponFromHash(weaponHash) local weapon = ESX.GetWeaponFromHash(weaponHash)
if weapon then if weapon then
local ammoCount = GetAmmoInPedWeapon(ESX.PlayerData.ped, weaponHash) currentWeapon.name = weapon.name
TriggerServerEvent('esx:updateWeaponAmmo', weapon.name, ammoCount) currentWeapon.hash = weaponHash
currentWeapon.timer = 100 * sleep
end end
end end
else
sleep = 200
end end
if letSleep then Citizen.Wait(sleep)
Citizen.Wait(500)
end
end end
end) end)
@@ -404,7 +427,7 @@ end
if Config.EnableDefaultInventory then if Config.EnableDefaultInventory then
RegisterCommand('showinv', function() RegisterCommand('showinv', function()
if not isDead and not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then if not ESX.PlayerData.dead and not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then
ESX.ShowInventory() ESX.ShowInventory()
end end
end) end)
+2
View File
@@ -18,3 +18,5 @@ Config.EnableDefaultInventory = true -- Display the default Inventory ( F2 )
Config.EnableWantedLevel = false -- Use Normal GTA wanted Level? Config.EnableWantedLevel = false -- Use Normal GTA wanted Level?
Config.EnablePVP = true -- Allow Player to player combat Config.EnablePVP = true -- Allow Player to player combat
Config.Multichar = false -- Enable support for esx_multicharacter
Config.Identity = true -- Select a characters identity data before they have loaded in (this happens by default with multichar)
+3 -2
View File
@@ -101,5 +101,6 @@ server_exports {
dependencies { dependencies {
'mysql-async', 'mysql-async',
'async' 'async',
} 'spawnmanager',
}
+10
View File
@@ -41,6 +41,11 @@
ESX.refreshHUD(); ESX.refreshHUD();
}; };
ESX.resetHUDElements = function () {
ESX.HUDElements = [];
ESX.refreshHUD();
};
ESX.refreshHUD = function () { ESX.refreshHUD = function () {
$('#hud').html(''); $('#hud').html('');
@@ -95,6 +100,11 @@
break; break;
} }
case 'resetHUDElements': {
ESX.resetHUDElements();
break;
}
case 'inventoryNotification': { case 'inventoryNotification': {
ESX.inventoryNotification(data.add, data.item, data.count); ESX.inventoryNotification(data.add, data.item, data.count);
} }
+4 -3
View File
@@ -14,8 +14,9 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
self.variables = {} self.variables = {}
self.weight = weight self.weight = weight
self.maxWeight = Config.MaxWeight self.maxWeight = Config.MaxWeight
if Config.Multichar then self.license = 'license'..string.sub(identifier, 6) else self.license = 'license:'..identifier end
ExecuteCommand(('add_principal identifier.license:%s group.%s'):format(self.identifier, self.group)) ExecuteCommand(('add_principal identifier.%s group.%s'):format(self.license, self.group))
self.triggerEvent = function(eventName, ...) self.triggerEvent = function(eventName, ...)
TriggerClientEvent(eventName, self.source, ...) TriggerClientEvent(eventName, self.source, ...)
@@ -66,9 +67,9 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
end end
self.setGroup = function(newGroup) self.setGroup = function(newGroup)
ExecuteCommand(('remove_principal identifier.license:%s group.%s'):format(self.identifier, self.group)) ExecuteCommand(('remove_principal identifier.%s group.%s'):format(self.license, self.group))
self.group = newGroup self.group = newGroup
ExecuteCommand(('add_principal identifier.license:%s group.%s'):format(self.identifier, self.group)) ExecuteCommand(('add_principal identifier.%s group.%s'):format(self.license, self.group))
end end
self.getGroup = function() self.getGroup = function()
+2 -16
View File
@@ -1,4 +1,3 @@
local crash = false
ESX.RegisterCommand('setcoords', 'admin', function(xPlayer, args, showError) ESX.RegisterCommand('setcoords', 'admin', function(xPlayer, args, showError)
xPlayer.setCoords({x = args.x, y = args.y, z = args.z}) xPlayer.setCoords({x = args.x, y = args.y, z = args.z})
end, false, {help = _U('command_setcoords'), validate = true, arguments = { end, false, {help = _U('command_setcoords'), validate = true, arguments = {
@@ -118,8 +117,8 @@ end, true, {help = _U('command_clearinventory'), validate = true, arguments = {
}}) }})
ESX.RegisterCommand('clearloadout', 'admin', function(xPlayer, args, showError) ESX.RegisterCommand('clearloadout', 'admin', function(xPlayer, args, showError)
for k,v in ipairs(args.playerId.loadout) do for i=#args.playerId.loadout, 1, -1 do
args.playerId.removeWeapon(v.name) args.playerId.removeWeapon(args.playerId.loadout[i].name)
end end
end, true, {help = _U('command_clearloadout'), validate = true, arguments = { end, true, {help = _U('command_clearloadout'), validate = true, arguments = {
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'} {name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'}
@@ -162,19 +161,6 @@ ESX.RegisterCommand('coords', "admin", function(xPlayer, args, showError)
print("".. xPlayer.getName().. ": ^5".. xPlayer.getCoords(true)) print("".. xPlayer.getName().. ": ^5".. xPlayer.getCoords(true))
end, true) end, true)
ESX.RegisterCommand('sv_restart', "admin", function(xPlayer, args, showError)
Citizen.CreateThread(function()
ESX.SavePlayers()
for _, playerId in ipairs(GetPlayers()) do
DropPlayer(playerId, "Server Restart")
end
print('^2[INFO] ^1 Server Restarting, Please Wait :)')
crash = true
while crash do
end
end)
end, true)
ESX.RegisterCommand('tpm', "admin", function(xPlayer, args, showError) ESX.RegisterCommand('tpm', "admin", function(xPlayer, args, showError)
xPlayer.triggerEvent("esx:tpm") xPlayer.triggerEvent("esx:tpm")
end, true) end, true)
+8 -6
View File
@@ -30,27 +30,29 @@ MySQL.ready(function()
end end
end) end)
local Jobs = {}
MySQL.Async.fetchAll('SELECT * FROM jobs', {}, function(jobs) MySQL.Async.fetchAll('SELECT * FROM jobs', {}, function(jobs)
for k,v in ipairs(jobs) do for k,v in ipairs(jobs) do
ESX.Jobs[v.name] = v Jobs[v.name] = v
ESX.Jobs[v.name].grades = {} Jobs[v.name].grades = {}
end end
MySQL.Async.fetchAll('SELECT * FROM job_grades', {}, function(jobGrades) MySQL.Async.fetchAll('SELECT * FROM job_grades', {}, function(jobGrades)
for k,v in ipairs(jobGrades) do for k,v in ipairs(jobGrades) do
if ESX.Jobs[v.job_name] then if Jobs[v.job_name] then
ESX.Jobs[v.job_name].grades[tostring(v.grade)] = v Jobs[v.job_name].grades[tostring(v.grade)] = v
else else
print(('[^3WARNING^7] Ignoring job grades for ^5"%s"^0 due to missing job'):format(v.job_name)) print(('[^3WARNING^7] Ignoring job grades for ^5"%s"^0 due to missing job'):format(v.job_name))
end end
end end
for k2,v2 in pairs(ESX.Jobs) do for k2,v2 in pairs(Jobs) do
if ESX.Table.SizeOf(v2.grades) == 0 then if ESX.Table.SizeOf(v2.grades) == 0 then
ESX.Jobs[v2.name] = nil Jobs[v2.name] = nil
print(('[^3WARNING^7] Ignoring job ^5"%s"^0due to no job grades found'):format(v2.name)) print(('[^3WARNING^7] Ignoring job ^5"%s"^0due to no job grades found'):format(v2.name))
end end
end end
ESX.Jobs = Jobs
end) end)
end) end)
+19 -9
View File
@@ -163,19 +163,25 @@ ESX.TriggerServerCallback = function(name, requestId, source, cb, ...)
end end
end end
local savePlayers = -1
Citizen.CreateThread(function()
savePlayers = MySQL.Sync.store("UPDATE users SET ? WHERE ?")
end)
ESX.SavePlayer = function(xPlayer, cb) ESX.SavePlayer = function(xPlayer, cb)
local asyncTasks = {} local asyncTasks = {}
table.insert(asyncTasks, function(cb2) table.insert(asyncTasks, function(cb2)
MySQL.Async.execute('UPDATE users SET accounts = @accounts, job = @job, job_grade = @job_grade, `group` = @group, loadout = @loadout, position = @position, inventory = @inventory WHERE identifier = @identifier', { MySQL.Async.execute(savePlayers, {
['@accounts'] = json.encode(xPlayer.getAccounts(true)), {
['@job'] = xPlayer.job.name, ['accounts'] = json.encode(xPlayer.getAccounts(true)),
['@job_grade'] = xPlayer.job.grade, ['job'] = xPlayer.job.name,
['@group'] = xPlayer.getGroup(), ['job_grade'] = xPlayer.job.grade,
['@loadout'] = json.encode(xPlayer.getLoadout(true)), ['group'] = xPlayer.getGroup(),
['@position'] = json.encode(xPlayer.getCoords()), ['loadout'] = json.encode(xPlayer.getLoadout(true)),
['@identifier'] = xPlayer.getIdentifier(), ['position'] = json.encode(xPlayer.getCoords()),
['@inventory'] = json.encode(xPlayer.getInventory(true)) ['inventory'] = json.encode(xPlayer.getInventory(true))
}, {['identifier'] = xPlayer.getIdentifier()}
}, function(rowsChanged) }, function(rowsChanged)
cb2() cb2()
end) end)
@@ -227,6 +233,10 @@ ESX.GetPlayers = function()
return sources return sources
end end
ESX.GetExtendedPlayers = function()
return ESX.Players
end
ESX.GetPlayerFromId = function(source) ESX.GetPlayerFromId = function(source)
return ESX.Players[tonumber(source)] return ESX.Players[tonumber(source)]
end end
+131 -36
View File
@@ -1,49 +1,54 @@
local NewPlayer, LoadPlayer = -1, -1
Citizen.CreateThread(function() Citizen.CreateThread(function()
SetMapName('San Andreas') SetMapName('San Andreas')
SetGameType('ESX Roleplay') SetGameType('ESX Roleplay')
MySQL.Async.store("INSERT INTO users SET ?", function(storeId)
NewPlayer = storeId
end)
local query = '`accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`'
if Config.Multichar or Config.Identity then query = query..', `firstname`, `lastname`, `dateofbirth`, `sex`, `height`' end
MySQL.Async.store("SELECT "..query.." FROM `users` WHERE ?? LIKE ?", function(storeId)
LoadPlayer = storeId
end)
end) end)
local awaitingRegistration = {}
RegisterNetEvent('esx:onPlayerJoined') RegisterNetEvent('esx:onPlayerJoined')
AddEventHandler('esx:onPlayerJoined', function() if Config.Multichar then
if not ESX.Players[source] then AddEventHandler('esx_identity:completedRegistration', function(playerId, data)
onPlayerJoined(source) awaitingRegistration[playerId] = data
end end)
end) AddEventHandler('esx:onPlayerJoined', function(src, char, isNew)
if not ESX.Players[src] then
onPlayerJoined(src, char, isNew)
end
end)
else
AddEventHandler('esx:onPlayerJoined', function()
if not ESX.Players[source] then
onPlayerJoined(source)
end
end)
end
function onPlayerJoined(playerId) function onPlayerJoined(playerId, char, isNew)
local identifier = ESX.GetIdentifier(playerId) local identifier = ESX.GetIdentifier(playerId)
if char then identifier = char..':'..identifier end
if identifier then if identifier then
if ESX.GetPlayerFromIdentifier(identifier) then 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)) 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))
elseif Config.Multichar and isNew then
createESXPlayer(identifier, playerId)
else else
MySQL.Async.fetchScalar('SELECT 1 FROM users WHERE identifier = @identifier', { MySQL.Async.fetchScalar('SELECT 1 FROM users WHERE identifier = @identifier', {
['@identifier'] = identifier ['@identifier'] = identifier
}, function(result) }, function(result)
if result then if result then
loadESXPlayer(identifier, playerId, false) loadESXPlayer(identifier, playerId, false)
else else createESXPlayer(identifier, playerId) end
local accounts = {}
for account,money in pairs(Config.StartingAccountMoney) do
accounts[account] = money
end
if IsPlayerAceAllowed(playerId, "command") then
print(('^2[INFO] ^0 Player ^5%s ^0Has been granted admin permissions via ^5Ace Perms.^7'):format(playerId))
defaultGroup = "admin"
else
defaultGroup = "user"
end
MySQL.Async.execute('INSERT INTO users (`accounts`, `identifier`, `group`) VALUES (@accounts, @identifier, @group)', {
['@accounts'] = json.encode(accounts),
['@identifier'] = identifier,
['@group'] = defaultGroup
}, function(rowsChanged)
loadESXPlayer(identifier, playerId, true)
end)
end
end) end)
end end
else else
@@ -51,6 +56,55 @@ function onPlayerJoined(playerId)
end end
end end
function createESXPlayer(identifier, playerId)
local accounts = {}
for account,money in pairs(Config.StartingAccountMoney) do
accounts[account] = money
end
if IsPlayerAceAllowed(playerId, "command") then
print(('^2[INFO] ^0 Player ^5%s ^0Has been granted admin permissions via ^5Ace Perms.^7'):format(playerId))
defaultGroup = "admin"
else
defaultGroup = "user"
end
if not Config.Multichar then
MySQL.Async.execute(NewPlayer, {
{
['accounts'] = json.encode(accounts),
['identifier'] = identifier,
['group'] = defaultGroup,
}
}, function(rowsChanged)
loadESXPlayer(identifier, playerId, true)
end)
else
local data
awaitingRegistration[playerId] = true
while true do
Citizen.Wait(250)
if awaitingRegistration[playerId] ~= true then data = awaitingRegistration[playerId] break end
end
awaitingRegistration[playerId] = nil
MySQL.Async.execute(NewPlayer, {
{
['accounts'] = json.encode(accounts),
['identifier'] = identifier,
['group'] = defaultGroup,
['firstname'] = data.firstname,
['lastname'] = data.lastname,
['dateofbirth'] = data.dateofbirth,
['sex'] = data.sex,
['height'] = data.height,
}
}, function(rowsChanged)
loadESXPlayer(identifier, playerId, true)
end)
end
end
AddEventHandler('playerConnecting', function(name, setCallback, deferrals) AddEventHandler('playerConnecting', function(name, setCallback, deferrals)
deferrals.defer() deferrals.defer()
local playerId = source local playerId = source
@@ -81,8 +135,7 @@ function loadESXPlayer(identifier, playerId, isNew)
} }
table.insert(tasks, function(cb) table.insert(tasks, function(cb)
MySQL.Async.fetchAll('SELECT accounts, job, job_grade, `group`, loadout, position, inventory FROM users WHERE identifier = @identifier', { MySQL.Async.fetchAll(LoadPlayer, {'identifier', {identifier}
['@identifier'] = identifier
}, function(result) }, function(result)
local job, grade, jobObject, gradeObject = result[1].job, tostring(result[1].job_grade) local job, grade, jobObject, gradeObject = result[1].job, tostring(result[1].job_grade)
local foundAccounts, foundItems = {}, {} local foundAccounts, foundItems = {}, {}
@@ -203,6 +256,23 @@ function loadESXPlayer(identifier, playerId, isNew)
userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8} userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8}
end 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() cb()
end) end)
end) end)
@@ -210,6 +280,15 @@ function loadESXPlayer(identifier, playerId, isNew)
Async.parallel(tasks, function(results) 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) 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 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) TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
xPlayer.triggerEvent('esx:playerLoaded', { xPlayer.triggerEvent('esx:playerLoaded', {
@@ -220,8 +299,9 @@ function loadESXPlayer(identifier, playerId, isNew)
job = xPlayer.getJob(), job = xPlayer.getJob(),
loadout = xPlayer.getLoadout(), loadout = xPlayer.getLoadout(),
maxWeight = xPlayer.getMaxWeight(), maxWeight = xPlayer.getMaxWeight(),
money = xPlayer.getMoney() money = xPlayer.getMoney(),
}, isNew) dead = 0
}, isNew, userData.skin)
xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups) xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups)
xPlayer.triggerEvent('esx:registerSuggestions', ESX.RegisteredCommands) xPlayer.triggerEvent('esx:registerSuggestions', ESX.RegisteredCommands)
@@ -250,6 +330,21 @@ AddEventHandler('playerDropped', function(reason)
end end
end) end)
if Config.Multichar then
AddEventHandler('esx:playerLogout', function(playerId)
local xPlayer = ESX.GetPlayerFromId(playerId)
awaitingRegistration[playerId] = nil
if xPlayer then
TriggerEvent('esx:playerDropped', playerId, reason)
ESX.SavePlayer(xPlayer, function()
ESX.Players[playerId] = nil
end)
end
TriggerClientEvent("esx:onPlayerLogout", playerId)
end)
end
RegisterNetEvent('esx:updateCoords') RegisterNetEvent('esx:updateCoords')
AddEventHandler('esx:updateCoords', function(coords) AddEventHandler('esx:updateCoords', function(coords)
local xPlayer = ESX.GetPlayerFromId(source) local xPlayer = ESX.GetPlayerFromId(source)
@@ -525,7 +620,7 @@ Citizen.CreateThread(
([[ ([[
^1---------------------------------------------------------------------- ^1----------------------------------------------------------------------
^1URGENT: YOUR ES-EXTENDED IS OUTDATED! ^1URGENT: YOUR ES_EXTENDED IS OUTDATED!
^1COMMIT UPDATE: ^5%s AVAILABLE ^1COMMIT UPDATE: ^5%s AVAILABLE
^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy ^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy
^1CHANGELOG:^5 %s ^1CHANGELOG:^5 %s
@@ -540,7 +635,7 @@ Citizen.CreateThread(
([[ ([[
^8------------------------------------------------------- ^8-------------------------------------------------------
^2Your Es-extended is the latest version! ^2Your es_extended is the latest version!
^5Version:^0 %s ^5Version:^0 %s
^5COMMIT:^0 %s ^5COMMIT:^0 %s
^5CHANGELOG:^0 %s ^5CHANGELOG:^0 %s
@@ -556,7 +651,7 @@ Citizen.CreateThread(
print( print(
([[ ([[
^1---------------------------------------------------------------------- ^1----------------------------------------------------------------------
^1URGENT: YOUR ES-EXTENDED IS OUTDATATED!!! ^1URGENT: YOUR ES_EXTENDED IS OUTDATATED!!!
^1COMMIT UPDATE: ^5%s AVAILABLE ^1COMMIT UPDATE: ^5%s AVAILABLE
^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy ^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy
^1CHANGELOG:^5 %s ^1CHANGELOG:^5 %s
@@ -568,7 +663,7 @@ Citizen.CreateThread(
) )
end end
else else
print('[^1ERROR^0] Es-Extended unable to check version!') print('[^1ERROR^0] es_extended unable to check version!')
end end
end, end,
'GET' 'GET'
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"version": "legacy", "version": "legacy",
"commit" : "1.2.0", "commit" : "1.3.1",
"changelog": "Preserve compatibility for getting hashes; store player ped and death in PlayerData; resolve typos and outdated prints" "changelog": "\n- Improved spawn event triggers\n- Improved support for relogging\n- Set PlayerData.dead initial value based on `is_dead` column (or 0)\n- Added a MySQL.store for the loadESXPlayer function\n- Ammo sync now waits for the player to stop shooting before sending a server event\n- /clearloadout now clears all player weapons on the first try\n- Minor fixes and optimisations"
} }