diff --git a/client/functions.lua b/client/functions.lua index 41fe9f93..7bc1d3b7 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -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 diff --git a/client/main.lua b/client/main.lua index c62cab8b..4409d4a5 100644 --- a/client/main.lua +++ b/client/main.lua @@ -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) diff --git a/config.lua b/config.lua index 89fbb379..6d9f4203 100644 --- a/config.lua +++ b/config.lua @@ -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) diff --git a/fxmanifest.lua b/fxmanifest.lua index f762a77f..7d8dbe12 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -101,5 +101,6 @@ server_exports { dependencies { 'mysql-async', - 'async' -} + 'async', + 'spawnmanager', +} \ No newline at end of file diff --git a/html/js/app.js b/html/js/app.js index 7705334c..dce07feb 100644 --- a/html/js/app.js +++ b/html/js/app.js @@ -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); } diff --git a/server/classes/player.lua b/server/classes/player.lua index 64d6c1f9..148210b4 100644 --- a/server/classes/player.lua +++ b/server/classes/player.lua @@ -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() diff --git a/server/commands.lua b/server/commands.lua index b3adaa9f..cdc668aa 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -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) diff --git a/server/common.lua b/server/common.lua index bd69ae09..95a2ff6a 100644 --- a/server/common.lua +++ b/server/common.lua @@ -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) diff --git a/server/functions.lua b/server/functions.lua index 46993bde..1732c0c9 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -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 diff --git a/server/main.lua b/server/main.lua index df1a7e18..924b5f25 100644 --- a/server/main.lua +++ b/server/main.lua @@ -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' diff --git a/version.json b/version.json index 4b7d21a6..98f31f8d 100644 --- a/version.json +++ b/version.json @@ -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" }