From 12f92e33a4c1cf564682723da7b637331fb2e31a Mon Sep 17 00:00:00 2001 From: Selt <83926739+seltonmt012@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:14:25 +0200 Subject: [PATCH] fix(es_extended/server): skip unspawned players in the bulk save Core.SavePlayer already refuses to save a player whose spawned flag is not set, because an unspawned player has no server-side ped and getCoords/health return 0,0,0 / 0. Core.SavePlayers, the bulk autosave that runs every few minutes and on restart/shutdown, had no such guard and saved every player in ESX.Players. A player still in the load or character-selection window when the autosave fires gets their row overwritten with position 0,0,0 and health 0. On the next load the default-spawn fallback does not kick in, since a decoded {"x":0.0,...} is truthy, so they spawn at the world origin, and with SaveDeathStatus on, dead. Guard the loop with the same spawned check, and skip the query entirely when no spawned player remains. Tested on artifact 25770: forced a connected player to spawned=false and ran saveall. Before, the player's row was overwritten (the save ran); after, the row was left untouched and no save was issued. --- [core]/es_extended/server/functions.lua | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 7ab5f9b4..c8020cc4 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -251,18 +251,24 @@ function Core.SavePlayers(cb) local parameters = {} for _, xPlayer in pairs(ESX.Players) do - updateHealthAndArmorInMetadata(xPlayer) - parameters[#parameters + 1] = { - json.encode(xPlayer.getAccounts(true)), - xPlayer.job.name, - xPlayer.job.grade, - xPlayer.group, - json.encode(xPlayer.getCoords(false, true)), - json.encode(xPlayer.getInventory(true)), - json.encode(xPlayer.getLoadout(true)), - json.encode(xPlayer.getMeta()), - xPlayer.identifier, - } + if xPlayer.spawned then + updateHealthAndArmorInMetadata(xPlayer) + parameters[#parameters + 1] = { + json.encode(xPlayer.getAccounts(true)), + xPlayer.job.name, + xPlayer.job.grade, + xPlayer.group, + json.encode(xPlayer.getCoords(false, true)), + json.encode(xPlayer.getInventory(true)), + json.encode(xPlayer.getLoadout(true)), + json.encode(xPlayer.getMeta()), + xPlayer.identifier, + } + end + end + + if not parameters[1] then + return cb and cb() end MySQL.prepare(