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.
This commit is contained in:
ASTROWwwW
2026-06-24 18:39:07 +02:00
parent 6acaedde1f
commit 409c2ccc63
2 changed files with 118 additions and 43 deletions
+29 -43
View File
@@ -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()
+89
View File
@@ -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:<key>` (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