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.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
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -101,5 +101,6 @@ server_exports {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
'mysql-async',
|
'mysql-async',
|
||||||
'async'
|
'async',
|
||||||
}
|
'spawnmanager',
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user