mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-29 01:08:54 +00:00
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:
+25
-13
@@ -42,7 +42,9 @@ end
|
||||
|
||||
ESX.SetPlayerData = function(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
|
||||
|
||||
ESX.ShowNotification = function(msg)
|
||||
@@ -141,6 +143,13 @@ ESX.UI.HUD.RemoveElement = function(name)
|
||||
})
|
||||
end
|
||||
|
||||
ESX.UI.HUD.Reset = function()
|
||||
SendNUIMessage({
|
||||
action = 'resetHUDElements'
|
||||
})
|
||||
ESX.UI.HUD.RegisteredElements = {}
|
||||
end
|
||||
|
||||
ESX.UI.HUD.UpdateElement = function(name, data)
|
||||
SendNUIMessage({
|
||||
action = 'updateHUDElement',
|
||||
@@ -314,7 +323,7 @@ ESX.Game.Teleport = function(entity, coords, cb)
|
||||
if DoesEntityExist(entity) then
|
||||
RequestCollisionAtCoord(vector.xyz)
|
||||
while not HasCollisionLoadedAroundEntity(entity) do
|
||||
Wait(0)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
|
||||
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
|
||||
dynamic = dynamic ~= nil and true or false
|
||||
|
||||
CreateThread(function()
|
||||
Citizen.CreateThread(function()
|
||||
ESX.Streaming.RequestModel(model)
|
||||
|
||||
-- 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 vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z)
|
||||
networked = networked == nil and true or false
|
||||
CreateThread(function()
|
||||
Citizen.CreateThread(function()
|
||||
ESX.Streaming.RequestModel(model)
|
||||
|
||||
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)
|
||||
while not HasCollisionLoadedAroundEntity(vehicle) do
|
||||
Wait(0)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
|
||||
if cb then
|
||||
@@ -699,14 +708,20 @@ ESX.Game.SetVehicleProperties = function(vehicle, props)
|
||||
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)
|
||||
if scale then scale = scale + 0.0 end
|
||||
|
||||
if not scale then scale = 0.35 end
|
||||
if not font then font = 4 end
|
||||
local camCoords = GetGameplayCamCoords()
|
||||
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)
|
||||
SetTextProportional(1)
|
||||
SetTextColour(255, 255, 255, 215)
|
||||
@@ -715,9 +730,6 @@ ESX.Game.Utils.DrawText3D = function(coords, text, scale, font)
|
||||
AddTextComponentString(text)
|
||||
SetDrawOrigin(vector.xyz, 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()
|
||||
end
|
||||
|
||||
|
||||
+59
-36
@@ -1,23 +1,52 @@
|
||||
local isPaused, isDead, pickups = false, false, {}
|
||||
local pickups = {}
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while NetworkIsPlayerActive(PlayerId()) do
|
||||
while NetworkIsPlayerActive(PlayerId()) and not Config.Multichar do
|
||||
Citizen.Wait(5)
|
||||
DoScreenFadeOut(0)
|
||||
TriggerServerEvent('esx:onPlayerJoined')
|
||||
break
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
RegisterNetEvent('esx:playerLoaded')
|
||||
AddEventHandler('esx:playerLoaded', function(playerData, isNew)
|
||||
AddEventHandler('esx:playerLoaded', function(playerData, isNew, skin)
|
||||
ESX.PlayerLoaded = true
|
||||
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
|
||||
if Config.EnablePVP then
|
||||
SetCanAttackFriendly(ESX.PlayerData.ped, true, false)
|
||||
@@ -41,26 +70,13 @@ AddEventHandler('esx:playerLoaded', function(playerData, isNew)
|
||||
grade_label = playerData.job.grade_label
|
||||
})
|
||||
end
|
||||
StartServerSyncLoops()
|
||||
end)
|
||||
|
||||
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()
|
||||
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.
|
||||
RegisterNetEvent('esx:onPlayerLogout')
|
||||
AddEventHandler('esx:onPlayerLogout', function()
|
||||
ESX.PlayerLoaded = false
|
||||
if Config.EnableHud then ESX.UI.HUD.Reset() end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:setMaxWeight')
|
||||
@@ -70,14 +86,12 @@ AddEventHandler('esx:onPlayerSpawn', function()
|
||||
local playerPed = PlayerPedId()
|
||||
if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end
|
||||
ESX.SetPlayerData('dead', false)
|
||||
isDead = false
|
||||
end)
|
||||
|
||||
AddEventHandler('esx:onPlayerDeath', function()
|
||||
local playerPed = PlayerPedId()
|
||||
if ESX.PlayerData.ped ~= playerPed then ESX.SetPlayerData('ped', playerPed) end
|
||||
ESX.SetPlayerData('dead', true)
|
||||
isDead = true
|
||||
end)
|
||||
|
||||
AddEventHandler('skinchanger:modelLoaded', function()
|
||||
@@ -335,6 +349,7 @@ end)
|
||||
-- Pause menu disables HUD display
|
||||
if Config.EnableHud then
|
||||
Citizen.CreateThread(function()
|
||||
local isPaused = false
|
||||
while true do
|
||||
Citizen.Wait(300)
|
||||
|
||||
@@ -356,10 +371,17 @@ end
|
||||
function StartServerSyncLoops()
|
||||
-- keep track of ammo
|
||||
Citizen.CreateThread(function()
|
||||
local currentWeapon = {timer=0}
|
||||
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 IsPedShooting(ESX.PlayerData.ped) then
|
||||
@@ -367,14 +389,15 @@ function StartServerSyncLoops()
|
||||
local weapon = ESX.GetWeaponFromHash(weaponHash)
|
||||
|
||||
if weapon then
|
||||
local ammoCount = GetAmmoInPedWeapon(ESX.PlayerData.ped, weaponHash)
|
||||
TriggerServerEvent('esx:updateWeaponAmmo', weapon.name, ammoCount)
|
||||
currentWeapon.name = weapon.name
|
||||
currentWeapon.hash = weaponHash
|
||||
currentWeapon.timer = 100 * sleep
|
||||
end
|
||||
end
|
||||
else
|
||||
sleep = 200
|
||||
end
|
||||
if letSleep then
|
||||
Citizen.Wait(500)
|
||||
end
|
||||
Citizen.Wait(sleep)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -404,7 +427,7 @@ end
|
||||
|
||||
if Config.EnableDefaultInventory then
|
||||
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()
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -18,3 +18,5 @@ Config.EnableDefaultInventory = true -- Display the default Inventory ( F2 )
|
||||
Config.EnableWantedLevel = false -- Use Normal GTA wanted Level?
|
||||
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
@@ -101,5 +101,6 @@ server_exports {
|
||||
|
||||
dependencies {
|
||||
'mysql-async',
|
||||
'async'
|
||||
}
|
||||
'async',
|
||||
'spawnmanager',
|
||||
}
|
||||
@@ -41,6 +41,11 @@
|
||||
ESX.refreshHUD();
|
||||
};
|
||||
|
||||
ESX.resetHUDElements = function () {
|
||||
ESX.HUDElements = [];
|
||||
ESX.refreshHUD();
|
||||
};
|
||||
|
||||
ESX.refreshHUD = function () {
|
||||
$('#hud').html('');
|
||||
|
||||
@@ -95,6 +100,11 @@
|
||||
break;
|
||||
}
|
||||
|
||||
case 'resetHUDElements': {
|
||||
ESX.resetHUDElements();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'inventoryNotification': {
|
||||
ESX.inventoryNotification(data.add, data.item, data.count);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
self.variables = {}
|
||||
self.weight = weight
|
||||
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, ...)
|
||||
TriggerClientEvent(eventName, self.source, ...)
|
||||
@@ -66,9 +67,9 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
self.getGroup = function()
|
||||
|
||||
+2
-16
@@ -1,4 +1,3 @@
|
||||
local crash = false
|
||||
ESX.RegisterCommand('setcoords', 'admin', function(xPlayer, args, showError)
|
||||
xPlayer.setCoords({x = args.x, y = args.y, z = args.z})
|
||||
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)
|
||||
for k,v in ipairs(args.playerId.loadout) do
|
||||
args.playerId.removeWeapon(v.name)
|
||||
for i=#args.playerId.loadout, 1, -1 do
|
||||
args.playerId.removeWeapon(args.playerId.loadout[i].name)
|
||||
end
|
||||
end, true, {help = _U('command_clearloadout'), validate = true, arguments = {
|
||||
{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))
|
||||
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)
|
||||
xPlayer.triggerEvent("esx:tpm")
|
||||
end, true)
|
||||
|
||||
+8
-6
@@ -30,27 +30,29 @@ MySQL.ready(function()
|
||||
end
|
||||
end)
|
||||
|
||||
local Jobs = {}
|
||||
MySQL.Async.fetchAll('SELECT * FROM jobs', {}, function(jobs)
|
||||
for k,v in ipairs(jobs) do
|
||||
ESX.Jobs[v.name] = v
|
||||
ESX.Jobs[v.name].grades = {}
|
||||
Jobs[v.name] = v
|
||||
Jobs[v.name].grades = {}
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM job_grades', {}, function(jobGrades)
|
||||
for k,v in ipairs(jobGrades) do
|
||||
if ESX.Jobs[v.job_name] then
|
||||
ESX.Jobs[v.job_name].grades[tostring(v.grade)] = v
|
||||
if Jobs[v.job_name] then
|
||||
Jobs[v.job_name].grades[tostring(v.grade)] = v
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring job grades for ^5"%s"^0 due to missing job'):format(v.job_name))
|
||||
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
|
||||
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))
|
||||
end
|
||||
end
|
||||
ESX.Jobs = Jobs
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
+19
-9
@@ -163,19 +163,25 @@ ESX.TriggerServerCallback = function(name, requestId, source, cb, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local savePlayers = -1
|
||||
Citizen.CreateThread(function()
|
||||
savePlayers = MySQL.Sync.store("UPDATE users SET ? WHERE ?")
|
||||
end)
|
||||
|
||||
ESX.SavePlayer = function(xPlayer, cb)
|
||||
local asyncTasks = {}
|
||||
|
||||
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', {
|
||||
['@accounts'] = json.encode(xPlayer.getAccounts(true)),
|
||||
['@job'] = xPlayer.job.name,
|
||||
['@job_grade'] = xPlayer.job.grade,
|
||||
['@group'] = xPlayer.getGroup(),
|
||||
['@loadout'] = json.encode(xPlayer.getLoadout(true)),
|
||||
['@position'] = json.encode(xPlayer.getCoords()),
|
||||
['@identifier'] = xPlayer.getIdentifier(),
|
||||
['@inventory'] = json.encode(xPlayer.getInventory(true))
|
||||
MySQL.Async.execute(savePlayers, {
|
||||
{
|
||||
['accounts'] = json.encode(xPlayer.getAccounts(true)),
|
||||
['job'] = xPlayer.job.name,
|
||||
['job_grade'] = xPlayer.job.grade,
|
||||
['group'] = xPlayer.getGroup(),
|
||||
['loadout'] = json.encode(xPlayer.getLoadout(true)),
|
||||
['position'] = json.encode(xPlayer.getCoords()),
|
||||
['inventory'] = json.encode(xPlayer.getInventory(true))
|
||||
}, {['identifier'] = xPlayer.getIdentifier()}
|
||||
}, function(rowsChanged)
|
||||
cb2()
|
||||
end)
|
||||
@@ -227,6 +233,10 @@ ESX.GetPlayers = function()
|
||||
return sources
|
||||
end
|
||||
|
||||
ESX.GetExtendedPlayers = function()
|
||||
return ESX.Players
|
||||
end
|
||||
|
||||
ESX.GetPlayerFromId = function(source)
|
||||
return ESX.Players[tonumber(source)]
|
||||
end
|
||||
|
||||
+131
-36
@@ -1,49 +1,54 @@
|
||||
local NewPlayer, LoadPlayer = -1, -1
|
||||
Citizen.CreateThread(function()
|
||||
SetMapName('San Andreas')
|
||||
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)
|
||||
|
||||
local awaitingRegistration = {}
|
||||
RegisterNetEvent('esx:onPlayerJoined')
|
||||
AddEventHandler('esx:onPlayerJoined', function()
|
||||
if not ESX.Players[source] then
|
||||
onPlayerJoined(source)
|
||||
end
|
||||
end)
|
||||
if Config.Multichar then
|
||||
AddEventHandler('esx_identity:completedRegistration', function(playerId, data)
|
||||
awaitingRegistration[playerId] = data
|
||||
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)
|
||||
if char then identifier = char..':'..identifier end
|
||||
|
||||
if 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))
|
||||
elseif Config.Multichar and isNew then
|
||||
createESXPlayer(identifier, playerId)
|
||||
else
|
||||
MySQL.Async.fetchScalar('SELECT 1 FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result then
|
||||
loadESXPlayer(identifier, playerId, false)
|
||||
else
|
||||
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
|
||||
else createESXPlayer(identifier, playerId) end
|
||||
end)
|
||||
end
|
||||
else
|
||||
@@ -51,6 +56,55 @@ function onPlayerJoined(playerId)
|
||||
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)
|
||||
deferrals.defer()
|
||||
local playerId = source
|
||||
@@ -81,8 +135,7 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
}
|
||||
|
||||
table.insert(tasks, function(cb)
|
||||
MySQL.Async.fetchAll('SELECT accounts, job, job_grade, `group`, loadout, position, inventory FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
MySQL.Async.fetchAll(LoadPlayer, {'identifier', {identifier}
|
||||
}, function(result)
|
||||
local job, grade, jobObject, gradeObject = result[1].job, tostring(result[1].job_grade)
|
||||
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}
|
||||
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)
|
||||
@@ -210,6 +280,15 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
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
|
||||
end
|
||||
|
||||
TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
|
||||
|
||||
xPlayer.triggerEvent('esx:playerLoaded', {
|
||||
@@ -220,8 +299,9 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
job = xPlayer.getJob(),
|
||||
loadout = xPlayer.getLoadout(),
|
||||
maxWeight = xPlayer.getMaxWeight(),
|
||||
money = xPlayer.getMoney()
|
||||
}, isNew)
|
||||
money = xPlayer.getMoney(),
|
||||
dead = 0
|
||||
}, isNew, userData.skin)
|
||||
|
||||
xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups)
|
||||
xPlayer.triggerEvent('esx:registerSuggestions', ESX.RegisteredCommands)
|
||||
@@ -250,6 +330,21 @@ AddEventHandler('playerDropped', function(reason)
|
||||
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')
|
||||
AddEventHandler('esx:updateCoords', function(coords)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
@@ -525,7 +620,7 @@ Citizen.CreateThread(
|
||||
([[
|
||||
|
||||
^1----------------------------------------------------------------------
|
||||
^1URGENT: YOUR ES-EXTENDED IS OUTDATED!
|
||||
^1URGENT: YOUR ES_EXTENDED IS OUTDATED!
|
||||
^1COMMIT UPDATE: ^5%s AVAILABLE
|
||||
^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy
|
||||
^1CHANGELOG:^5 %s
|
||||
@@ -540,7 +635,7 @@ Citizen.CreateThread(
|
||||
([[
|
||||
|
||||
^8-------------------------------------------------------
|
||||
^2Your Es-extended is the latest version!
|
||||
^2Your es_extended is the latest version!
|
||||
^5Version:^0 %s
|
||||
^5COMMIT:^0 %s
|
||||
^5CHANGELOG:^0 %s
|
||||
@@ -556,7 +651,7 @@ Citizen.CreateThread(
|
||||
print(
|
||||
([[
|
||||
^1----------------------------------------------------------------------
|
||||
^1URGENT: YOUR ES-EXTENDED IS OUTDATATED!!!
|
||||
^1URGENT: YOUR ES_EXTENDED IS OUTDATATED!!!
|
||||
^1COMMIT UPDATE: ^5%s AVAILABLE
|
||||
^1DOWNLOAD:^5 https://github.com/esx-framework/es_extended/tree/legacy
|
||||
^1CHANGELOG:^5 %s
|
||||
@@ -568,7 +663,7 @@ Citizen.CreateThread(
|
||||
)
|
||||
end
|
||||
else
|
||||
print('[^1ERROR^0] Es-Extended unable to check version!')
|
||||
print('[^1ERROR^0] es_extended unable to check version!')
|
||||
end
|
||||
end,
|
||||
'GET'
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "legacy",
|
||||
"commit" : "1.2.0",
|
||||
"changelog": "Preserve compatibility for getting hashes; store player ped and death in PlayerData; resolve typos and outdated prints"
|
||||
"commit" : "1.3.1",
|
||||
"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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user