feat(esx_lib/points): move points and Point class to xLib

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
This commit is contained in:
ASTROWwwW
2026-06-16 21:03:41 +02:00
parent 6a72181992
commit ca278be1da
7 changed files with 159 additions and 109 deletions
+6
View File
@@ -57,3 +57,9 @@ ESX.Scaleform = {
RunMethod = xLib.scaleform.utils.runMethod,
},
}
ESX.CreatePointInternal = xLib.points.create
ESX.RemovePointInternal = xLib.points.remove
ESX.HidePointInternal = xLib.points.hide
StartPointsLoop = xLib.points.startLoop
ESX.Point = xLib.point
@@ -1,53 +0,0 @@
local Point = ESX.Class()
local nearby, loop = {}, nil
function Point:constructor(properties)
self.coords = properties.coords
self.hidden = properties.hidden
self.enter = properties.enter
self.leave = properties.leave
self.inside = properties.inside
self.handle = ESX.CreatePointInternal(properties.coords, properties.distance, properties.hidden, function()
nearby[self.handle] = self
if self.enter then
self:enter()
end
if not loop then
loop = true
CreateThread(function()
while loop do
local coords = GetEntityCoords(ESX.PlayerData.ped)
for _, point in pairs(nearby) do
if point.inside then
point:inside(#(coords - point.coords))
end
end
Wait(0)
end
end)
end
end, function()
nearby[self.handle] = nil
if self.leave then
self:leave()
end
if #nearby == 0 then
loop = false
end
end)
end
function Point:delete()
ESX.RemovePointInternal(self.handle)
end
function Point:toggle(hidden)
if hidden == nil then
hidden = not self.hidden
end
self.hidden = hidden
ESX.HidePointInternal(self.handle, hidden)
end
return Point
@@ -1,54 +0,0 @@
local points = {}
function ESX.CreatePointInternal(coords, distance, hidden, enter, leave)
local point = {
coords = coords,
distance = distance,
hidden = hidden,
enter = enter,
leave = leave,
resource = GetInvokingResource()
}
local handle = ESX.Table.SizeOf(points) + 1
points[handle] = point
return handle
end
function ESX.RemovePointInternal(handle)
points[handle] = nil
end
function ESX.HidePointInternal(handle, hidden)
if points[handle] then
points[handle].hidden = hidden
end
end
function StartPointsLoop()
CreateThread(function()
while true do
local coords = GetEntityCoords(ESX.PlayerData.ped)
for handle, point in pairs(points) do
if not point.hidden and #(coords - point.coords) <= point.distance then
if not point.nearby then
points[handle].nearby = true
points[handle].enter()
end
elseif point.nearby then
points[handle].nearby = false
points[handle].leave()
end
end
Wait(500)
end
end)
end
AddEventHandler('onResourceStop', function(resource)
for handle, point in pairs(points) do
if point.resource == resource then
points[handle] = nil
end
end
end)
-1
View File
@@ -49,7 +49,6 @@ client_scripts {
'client/modules/wrapper.lua',
'client/modules/callback.lua',
'client/modules/adjustments.lua',
'client/modules/points.lua',
'client/modules/events.lua',
+1 -1
View File
@@ -87,7 +87,7 @@ if not IsDuplicityVersion() then -- Only register this event for the client
end)
end
local external = { { "Class", "class.lua" }, { "Point", "point.lua" } }
local external = { { "Class", "class.lua" } }
for i = 1, #external do
local module = external[i]
local path = string.format("client/imports/%s", module[2])
+52
View File
@@ -0,0 +1,52 @@
---@class xPoint
---@field coords vector3
---@field hidden? boolean
---@field enter? function
---@field leave? function
---@field inside? function
---@field handle integer
xLib.point = xLib.class()
---@param properties table coords, distance, hidden, enter, leave, inside
function xLib.point:constructor(properties)
self.coords = properties.coords
self.hidden = properties.hidden
self.enter = properties.enter
self.leave = properties.leave
self.inside = properties.inside
self.handle = xLib.points.create(
properties.coords,
properties.distance,
properties.hidden,
function()
if self.enter then
self:enter()
end
end,
function()
if self.leave then
self:leave()
end
end,
properties.inside and function(dist)
self:inside(dist)
end or nil
)
end
function xLib.point:delete()
xLib.points.remove(self.handle)
end
---@param hidden? boolean
function xLib.point:toggle(hidden)
if hidden == nil then
hidden = not self.hidden
end
self.hidden = hidden
xLib.points.hide(self.handle, hidden)
end
return xLib.point
+100
View File
@@ -0,0 +1,100 @@
---@class pointslib
xLib.points = {}
local points = {}
local insidePoints = {}
local handleCount = 0
---@param coords vector3
---@param distance number
---@param hidden? boolean
---@param enter function
---@param leave function
---@param inside? function
---@return integer handle
function xLib.points.create(coords, distance, hidden, enter, leave, inside)
handleCount = handleCount + 1
local handle = handleCount
points[handle] = {
coords = coords,
distance = distance,
hidden = hidden,
enter = enter,
leave = leave,
inside = inside,
resource = GetInvokingResource()
}
return handle
end
---@param handle integer
function xLib.points.remove(handle)
points[handle] = nil
insidePoints[handle] = nil
end
---@param handle integer
---@param hidden boolean
function xLib.points.hide(handle, hidden)
if points[handle] then
points[handle].hidden = hidden
end
end
function xLib.points.startLoop()
CreateThread(function()
local lastScan = 0
while true do
local coords = GetEntityCoords(PlayerPedId())
for _, point in pairs(insidePoints) do
point.inside(#(coords - point.coords))
end
local now = GetGameTimer()
if now - lastScan >= 500 then
lastScan = now
for handle, point in pairs(points) do
if not point.hidden and #(coords - point.coords) <= point.distance then
if not point.nearby then
point.nearby = true
if point.enter then
point.enter()
end
if point.inside then
insidePoints[handle] = point
end
end
elseif point.nearby then
point.nearby = false
if point.leave then
point.leave()
end
insidePoints[handle] = nil
end
end
end
Wait(next(insidePoints) and 0 or 500)
end
end)
end
AddEventHandler("onResourceStop", function(resource)
for handle, point in pairs(points) do
if point.resource == resource then
points[handle] = nil
insidePoints[handle] = nil
end
end
end)
return xLib.points