From 409c2ccc63108a1bd906b5e48962692c34dc776f Mon Sep 17 00:00:00 2001 From: ASTROWwwW Date: Wed, 24 Jun 2026 18:39:07 +0200 Subject: [PATCH] refactor(esx_lib): move the player-state cache into the lib Adds a generic xLib.cache (ped, vehicle, seat, weapon, coords) modelled on ox_lib cache and framework agnostic, and rewires es_extended to source ped and weapon from it which removes the per-frame ped poll. The vehicle enter/exit state machine and every esx: event are kept unchanged through a thin glue. --- [core]/es_extended/client/modules/actions.lua | 72 ++++++--------- [core]/esx_lib/imports/cache/client.lua | 89 +++++++++++++++++++ 2 files changed, 118 insertions(+), 43 deletions(-) create mode 100644 [core]/esx_lib/imports/cache/client.lua diff --git a/[core]/es_extended/client/modules/actions.lua b/[core]/es_extended/client/modules/actions.lua index de174910..846e8307 100644 --- a/[core]/es_extended/client/modules/actions.lua +++ b/[core]/es_extended/client/modules/actions.lua @@ -1,10 +1,14 @@ +-- ESX glue over the generic xLib.cache. +-- ped and weapon are tracked by the lib cache; this module mirrors them into +-- ESX.PlayerData, re-emits the legacy esx: events resources depend on, and keeps +-- the vehicle enter/exit state machine that produces the richer vehicle events. + Actions = {} Actions._index = Actions Actions.inVehicle = false Actions.enteringVehicle = false Actions.inPauseMenu = false -Actions.currentWeapon = false function Actions:GetSeatPedIsIn() for i = -1, 16 do @@ -55,21 +59,6 @@ function Actions:TrackPedCoordsOnce() end) end -function Actions:TrackPed() - local playerPed = ESX.PlayerData.ped - local newPed = PlayerPedId() - - if playerPed ~= newPed then - ESX.SetPlayerData("ped", newPed) - - TriggerEvent("esx:playerPedChanged", newPed) - - if Config.EnableDebug then - print("[DEBUG] Player ped changed:", newPed) - end - end -end - function Actions:TrackPauseMenu() local isActive = IsPauseMenuActive() @@ -189,46 +178,43 @@ function Actions:TrackSeat() end end -function Actions:TrackWeapon() - ---@type number|false - local newWeapon = GetSelectedPedWeapon(ESX.PlayerData.ped) - newWeapon = newWeapon ~= `WEAPON_UNARMED` and newWeapon or false - - if newWeapon ~= self.currentWeapon then - self.currentWeapon = newWeapon - ESX.SetPlayerData("weapon", self.currentWeapon) - TriggerEvent("esx:weaponChanged", self.currentWeapon) - - if Config.EnableDebug then - print("[DEBUG] Weapon changed:", self.currentWeapon) - end - end -end - function Actions:SlowLoop() CreateThread(function() while ESX.PlayerLoaded do self:TrackPauseMenu() self:TrackVehicle() - self:TrackWeapon() Wait(500) end end) end -function Actions:PedLoop() - CreateThread(function() - while ESX.PlayerLoaded do - self:TrackPed() - Wait(0) - end - end) -end - function Actions:Init() + -- Re-seed the cached values on every (re)login. The change handlers below are + -- registered once at file load so a relogin never stacks duplicate handlers. + ESX.SetPlayerData("ped", xLib.cache.ped) + ESX.SetPlayerData("weapon", xLib.cache.weapon) + self:SlowLoop() - self:PedLoop() self:TrackPedCoordsOnce() end +-- Mirror the lib cache into ESX.PlayerData and re-emit the legacy esx: events. +AddEventHandler("xLib:cache:ped", function(ped) + ESX.SetPlayerData("ped", ped) + TriggerEvent("esx:playerPedChanged", ped) + + if Config.EnableDebug then + print("[DEBUG] Player ped changed:", ped) + end +end) + +AddEventHandler("xLib:cache:weapon", function(weapon) + ESX.SetPlayerData("weapon", weapon) + TriggerEvent("esx:weaponChanged", weapon) + + if Config.EnableDebug then + print("[DEBUG] Weapon changed:", weapon) + end +end) + Actions:Init() diff --git a/[core]/esx_lib/imports/cache/client.lua b/[core]/esx_lib/imports/cache/client.lua new file mode 100644 index 00000000..5ab1c3d2 --- /dev/null +++ b/[core]/esx_lib/imports/cache/client.lua @@ -0,0 +1,89 @@ +--[[ + Generic client-side player state cache, modelled on ox_lib cache. + Framework agnostic: natives only, no ESX coupling. + + Loaded per-resource VM through the lazy loader. PlayerPedId() and friends + return the same value in every client VM, so the cached values are correct + in each resource that requires it. + + Exposes the live values as xLib.cache and emits `xLib:cache:` (value, previous) + whenever a tracked value changes. `coords` is read on demand from the live ped + and never stored. +]] + +local playerId = PlayerId() + +local cache = setmetatable({ + playerId = playerId, + ped = PlayerPedId(), + vehicle = false, + seat = false, + weapon = false, +}, { + __index = function(self, key) + if key == "coords" then + return GetEntityCoords(self.ped) + elseif key == "serverId" then + -- resolved lazily: GetPlayerServerId can return -1 before the network + -- session is ready, so only cache it once it is valid. + local id = GetPlayerServerId(playerId) + if id and id ~= -1 then + rawset(self, "serverId", id) + end + return id + end + end, +}) + +local function set(key, value) + if cache[key] == value then + return + end + + local previous = cache[key] + rawset(cache, key, value) + TriggerEvent(("xLib:cache:%s"):format(key), value, previous) +end + +local function getSeat(ped, vehicle) + for seat = -1, 16 do + if GetPedInVehicleSeat(vehicle, seat) == ped then + return seat + end + end + return false +end + +CreateThread(function() + while true do + local ped = PlayerPedId() + if ped ~= cache.ped then + set("ped", ped) + end + + ---@type integer|false + local vehicle = GetVehiclePedIsIn(ped, false) + vehicle = vehicle ~= 0 and vehicle or false + + if vehicle ~= cache.vehicle then + set("vehicle", vehicle) + set("seat", vehicle and getSeat(ped, vehicle) or false) + elseif vehicle then + local seat = getSeat(ped, vehicle) + if seat ~= cache.seat then + set("seat", seat) + end + end + + ---@type integer|false + local weapon = GetSelectedPedWeapon(ped) + weapon = weapon ~= `WEAPON_UNARMED` and weapon or false + if weapon ~= cache.weapon then + set("weapon", weapon) + end + + Wait(100) + end +end) + +return cache