The pickup loop calls ESX.Game.GetClosestPlayer at the top of every iteration, but
closestDistance is only read inside the nested check that runs when the player presses
the pickup control right next to a drop. As soon as any pickup is within five metres
the loop sets Sleep to 0, so the lookup runs every frame while standing near a drop.
GetClosestPlayer builds two tables and calls GetPlayers plus GetPlayerPed,
DoesEntityExist and GetEntityCoords per player, so the cost grows with the number of
players in scope - worst exactly when the server is busy.
Measured in game on artifact 25770 with a single player standing on a dropped pickup,
reading es_extended in the resource monitor over about 20 seconds each:
before: 0.16 - 0.18 ms, sitting around 0.17
after: 0.13 - 0.15 ms, sitting around 0.14
The two ranges do not overlap. Idle, with no pickup within five metres, is 0.04 ms in
both cases, as expected, since the loop sleeps 1500 ms then.
One player is the smallest saving this can ever show, because GetClosestPlayer scales
with the number of players in scope. The larger saving on a populated server follows
from that per player work and was not measured, testing it would need several clients.
Behaviour is unchanged, the value is computed in the same frame it is used.
Adds xLib.pubsub for server-driven topic multicast: a producer resource
subscribes players to a topic server-side and publishes data that is pushed
only to the subscribed players. Clients listen with xLib.pubsub.on and never
subscribe or publish themselves.
The subscription registry is a single shared instance in the esx_lib server VM,
reached cross-resource through exports; the client side is a per-VM listener
over one net event. Subscriptions are cleaned up on playerDropped, so a reused
serverId never inherits them, and on the owning resource stopping.
Also adds xLib.triggerClientEvent, which packs the payload once when sending to
many players; publish uses it so a broadcast to N subscribers serialises once.
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.
Moved getPlayersInArea / getClosestPlayer / getPedsInArea / getObjectsInArea /
getVehiclesInArea / getClosestPed / getClosestObject / getClosestVehicle to
xLib; es_extended re-exposes them through server compat shims.
Flagged: getNearbyPlayers iterates ESX.Players (es_extended server state),
which is not portable across the lib boundary - the lib relies on that global
being present at call time.
Integrated fix during the move:
- spawnVehicle uses the computed isNetworked for the network-id/migrate guard,
fixing the original that branched on the raw networked argument
modSmokeEnabled preserved as-is in get/setVehicleProperties (flagged: the set
path toggles mod 20 without applying a smoke color, matching legacy behavior).
Non-portable substitution documented:
- ESX.PlayerData.ped -> PlayerPedId() (ESX.PlayerData is not available in the lib VM)
Integrated fixes during the move:
- monotonic handle counter (handleCount) instead of count+1; no handle
collision after a point is removed
- unified single proximity loop: per-frame inside() plus a 500ms enter/leave
scan, adaptive Wait(next(insidePoints) and 0 or 500), replacing the previous
two separate loops
- emptiness detected via next() instead of # on a sparse-keyed table
Non-portable substitutions documented:
- ESX.PlayerData.ped -> PlayerPedId() (ESX.PlayerData is not available in the
lib VM)
- GetGameTimer() used to gate the 500ms scan within the single loop