From ae19c7cd253aadaec7eab0977d67e3f07c19bded Mon Sep 17 00:00:00 2001 From: SNAILX Date: Tue, 28 Oct 2025 18:29:36 +0100 Subject: [PATCH 1/8] Refactor keybind registration to use xLib.addKeybind --- [core]/es_extended/client/compat.lua | 4 +- [core]/es_extended/client/functions.lua | 16 --- [core]/es_extended/fxmanifest.lua | 4 +- [core]/esx_lib/imports/addKeybind/client.lua | 107 +++++++++++++++++++ 4 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 [core]/esx_lib/imports/addKeybind/client.lua diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index 254148ac..6b1babe8 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -1 +1,3 @@ ---All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: \ No newline at end of file +--All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: + +ESX.RegisterInput = xLib.addKeybind \ No newline at end of file diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 953296d2..2884bc93 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -245,22 +245,6 @@ function ESX.RefreshContext(...) return IsResourceFound('esx_context') and exports['esx_context']:Refresh(...) end ----@param command_name string The command name ----@param label string The label to show ----@param input_group string The input group ----@param key string The key to bind ----@param on_press function The function to call on press ----@param on_release? function The function to call on release -function ESX.RegisterInput(command_name, label, input_group, key, on_press, on_release) - local command = on_release and '+' .. command_name or command_name - RegisterCommand(command, on_press, false) - Core.Input[command_name] = ESX.HashString(command) - if on_release then - RegisterCommand('-' .. command_name, on_release, false) - end - RegisterKeyMapping(command, label or '', input_group or 'keyboard', key or '') -end - ---@param menuType string ---@param open function The function to call on open ---@param close function The function to call on close diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index 88f1b760..f4005de1 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -6,6 +6,7 @@ lua54 'yes' version '1.13.3' shared_scripts { + '@esx_lib/imports.lua', 'locale.lua', 'shared/config/main.lua', @@ -44,6 +45,7 @@ server_scripts { client_scripts { 'client/main.lua', 'client/functions.lua', + 'client/compat.lua', 'client/modules/wrapper.lua', 'client/modules/callback.lua', 'client/modules/adjustments.lua', @@ -57,8 +59,6 @@ client_scripts { 'client/modules/interactions.lua', 'client/modules/scaleform.lua', 'client/modules/streaming.lua', - - 'shared/compat.lua' } ui_page { diff --git a/[core]/esx_lib/imports/addKeybind/client.lua b/[core]/esx_lib/imports/addKeybind/client.lua new file mode 100644 index 00000000..8533c8f7 --- /dev/null +++ b/[core]/esx_lib/imports/addKeybind/client.lua @@ -0,0 +1,107 @@ +--[[ + https://github.com/overextended/ox_lib + + This file is licensed under LGPL-3.0 or higher + + Copyright © 2025 Linden +]] + +---@class KeybindProps +---@field name string +---@field description string +---@field defaultMapper? string (see: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/) +---@field defaultKey? string +---@field disabled? boolean +---@field disable? fun(self: CKeybind, toggle: boolean) +---@field onPressed? fun(self: CKeybind) +---@field onReleased? fun(self: CKeybind) +---@field remove fun(self: CKeybind) +---@field [string] any + +---@class CKeybind : KeybindProps +---@field currentKey string +---@field disabled boolean +---@field isPressed boolean +---@field hash number +---@field getCurrentKey fun(): string +---@field isControlPressed fun(): boolean + +local keybinds = {} + +local IsPauseMenuActive = IsPauseMenuActive +local GetControlInstructionalButton = GetControlInstructionalButton + +local keybind_mt = { + disabled = false, + isPressed = false, + defaultKey = '', + defaultMapper = 'keyboard', +} + +function keybind_mt:__index(index) + return index == 'currentKey' and self:getCurrentKey() or keybind_mt[index] +end + +function keybind_mt:getCurrentKey() + return GetControlInstructionalButton(0, self.hash, true):sub(3) +end + +function keybind_mt:isControlPressed() + return self.isPressed +end + +function keybind_mt:disable(toggle) + self.disabled = toggle +end + +function keybind_mt:remove() + if not keybinds[self.name] then return end + keybinds[self.name] = nil +end + +---@param command_name string The keybind name +---@param label string The description to show +---@param input_group? string The input group (default: 'keyboard') +---@param key? string The default key +---@param on_press? function The function to call on press +---@param on_release? function The function to call on release +---@return CKeybind +function xLib.addKeybind(command_name, label, input_group, key, on_press, on_release) + + local data = { + name = command_name, + description = label, + defaultMapper = input_group or 'keyboard', + defaultKey = key or '', + onPressed = on_press, + onReleased = on_release, + hash = joaat('+' .. command_name) | 0x80000000 + } + + keybinds[data.name] = setmetatable(data, keybind_mt) + + RegisterCommand('+' .. data.name, function() + if data.disabled or IsPauseMenuActive() then return end + data.isPressed = true + if not keybinds[data.name] then return end + if data.onPressed then data:onPressed() end + end) + + RegisterCommand('-' .. data.name, function() + if data.disabled or IsPauseMenuActive() then return end + data.isPressed = false + if not keybinds[data.name] then return end + if data.onReleased then data:onReleased() end + end) + + RegisterKeyMapping('+' .. data.name, data.description, data.defaultMapper, data.defaultKey) + + SetTimeout(500, function() + TriggerEvent('chat:removeSuggestion', ('/+%s'):format(data.name)) + TriggerEvent('chat:removeSuggestion', ('/-%s'):format(data.name)) + end) + + return data +end + +return xLib.addKeybind \ No newline at end of file From fa52482720d756166cb86fef9ca89de170429977 Mon Sep 17 00:00:00 2001 From: SNAILX Date: Wed, 29 Oct 2025 01:54:53 +0100 Subject: [PATCH 2/8] Fix keybind command order for proper validation --- [core]/esx_lib/imports/addKeybind/client.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/esx_lib/imports/addKeybind/client.lua b/[core]/esx_lib/imports/addKeybind/client.lua index 8533c8f7..01233d11 100644 --- a/[core]/esx_lib/imports/addKeybind/client.lua +++ b/[core]/esx_lib/imports/addKeybind/client.lua @@ -81,16 +81,16 @@ function xLib.addKeybind(command_name, label, input_group, key, on_press, on_rel keybinds[data.name] = setmetatable(data, keybind_mt) RegisterCommand('+' .. data.name, function() + if not keybinds[data.name] then return end if data.disabled or IsPauseMenuActive() then return end data.isPressed = true - if not keybinds[data.name] then return end if data.onPressed then data:onPressed() end end) RegisterCommand('-' .. data.name, function() + if not keybinds[data.name] then return end if data.disabled or IsPauseMenuActive() then return end data.isPressed = false - if not keybinds[data.name] then return end if data.onReleased then data:onReleased() end end) From 7ebc92ec0fdfcff34e345c514568c783d71a9d6f Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Thu, 30 Oct 2025 19:14:08 +0100 Subject: [PATCH 3/8] refactor(lib/keybind) Change args structure --- [core]/esx_lib/imports/addKeybind/client.lua | 23 ++++---------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/[core]/esx_lib/imports/addKeybind/client.lua b/[core]/esx_lib/imports/addKeybind/client.lua index 01233d11..65c4467c 100644 --- a/[core]/esx_lib/imports/addKeybind/client.lua +++ b/[core]/esx_lib/imports/addKeybind/client.lua @@ -15,7 +15,7 @@ ---@field disable? fun(self: CKeybind, toggle: boolean) ---@field onPressed? fun(self: CKeybind) ---@field onReleased? fun(self: CKeybind) ----@field remove fun(self: CKeybind) +---@field remove? fun(self: CKeybind) ---@field [string] any ---@class CKeybind : KeybindProps @@ -59,25 +59,10 @@ function keybind_mt:remove() keybinds[self.name] = nil end ----@param command_name string The keybind name ----@param label string The description to show ----@param input_group? string The input group (default: 'keyboard') ----@param key? string The default key ----@param on_press? function The function to call on press ----@param on_release? function The function to call on release +---@param data KeybindProps ---@return CKeybind -function xLib.addKeybind(command_name, label, input_group, key, on_press, on_release) - - local data = { - name = command_name, - description = label, - defaultMapper = input_group or 'keyboard', - defaultKey = key or '', - onPressed = on_press, - onReleased = on_release, - hash = joaat('+' .. command_name) | 0x80000000 - } - +function xLib.addKeybind(data) + data.hash = joaat("+" .. data.name) | 0x80000000 keybinds[data.name] = setmetatable(data, keybind_mt) RegisterCommand('+' .. data.name, function() From cb41916ad1a1018818df0fde95fbbdd13a25e139 Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Thu, 30 Oct 2025 19:14:24 +0100 Subject: [PATCH 4/8] refactor(lib/addKeybind) Compat for old core structure --- [core]/es_extended/client/compat.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index 6b1babe8..a173f77b 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -1,3 +1,12 @@ --All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: -ESX.RegisterInput = xLib.addKeybind \ No newline at end of file +function ESX.RegisterInput(command_name, label, input_group, key, on_press, on_release) + return xLib.addKeybind({ + name = command_name, + description = label, + defaultMapper = input_group, + defaultKey = key, + onPressed = on_press, + onReleased = on_release + }) +end \ No newline at end of file From d9084186376045759dcd5ef4780c5c98cafeb625 Mon Sep 17 00:00:00 2001 From: SNAILX <180862994+SNAILX808@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:57:39 +0100 Subject: [PATCH 5/8] Move-refactor-raycast-functions-esx_lib --- [core]/es_extended/client/compat.lua | 23 ++++++- [core]/es_extended/client/functions.lua | 21 ------- [core]/esx_lib/imports/raycast/client.lua | 77 +++++++++++++++++++++++ 3 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 [core]/esx_lib/imports/raycast/client.lua diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index 254148ac..feef08bf 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -1 +1,22 @@ ---All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: \ No newline at end of file +--All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: + +ESX.Game.GetShapeTestResultSync = xLib.Raycast.GetShapeTestResult +ESX.Game.RaycastScreen = xLib.Raycast.FromScreen +ESX.Game.StartRaycasting = xLib.Raycast.Start +ESX.Game.StopRaycasting = function(raycast) + if raycast and raycast.active then + raycast:Stop() + end +end +ESX.Game.IsRaycastActive = function(raycast) + if raycast and raycast.active then + return raycast:IsActive() + end + return false +end +ESX.Game.GetRaycastResult = function(raycast) + if raycast and raycast.active then + return raycast.result + end + return nil +end \ No newline at end of file diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 953296d2..5e9dbd3b 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -736,27 +736,6 @@ function ESX.Game.IsSpawnPointClear(coords, maxDistance) return #ESX.Game.GetVehiclesInArea(coords, maxDistance) == 0 end ----@param shape integer The shape to get the test result from ----@return boolean, table, table, integer, integer -function ESX.Game.GetShapeTestResultSync(shape) - local handle, hit, coords, normal, material, entity - repeat - handle, hit, coords, normal, material, entity = GetShapeTestResultIncludingMaterial(shape) - Wait(0) - until handle ~= 1 - return hit, coords, normal, material, entity -end - ----@param depth number The depth to raycast ----@vararg any The arguments to pass to the shape test ----@return table, boolean, table, table, integer, integer -function ESX.Game.RaycastScreen(depth, ...) - local world, normal = GetWorldCoordFromScreenCoord(.5, .5) - local origin = world + normal - local target = world + normal * depth - return target, ESX.Game.GetShapeTestResultSync(StartShapeTestLosProbe(origin.x, origin.y, origin.z, target.x, target.y, target.z, ...)) -end - ---@param entities table The entities to search through ---@param isPlayerEntities boolean Whether the entities are players ---@param coords? table | vector3 The coords to search from diff --git a/[core]/esx_lib/imports/raycast/client.lua b/[core]/esx_lib/imports/raycast/client.lua new file mode 100644 index 00000000..f0464be5 --- /dev/null +++ b/[core]/esx_lib/imports/raycast/client.lua @@ -0,0 +1,77 @@ +xLib.Raycast = {} +xLib.Raycast.__index = xLib.Raycast + +local unpack = table.unpack +local GetShapeTestResultIncludingMaterial = GetShapeTestResultIncludingMaterial +local StartShapeTestLosProbe = StartShapeTestLosProbe +local GetWorldCoordFromScreenCoord = GetWorldCoordFromScreenCoord + +---@param shape integer The shape test handle to wait for +---@return boolean, table, table, integer, integer +function xLib.Raycast.GetShapeTestResult(shape) + local handle, hit, coords, normal, material, entity + + repeat + handle, hit, coords, normal, material, entity = GetShapeTestResultIncludingMaterial(shape) + Wait(0) + until handle ~= 1 + + return hit, coords, normal, material, entity +end + +---@param depth number The raycast distance +---@vararg any Additional arguments to pass to shape test +---@return table, boolean, table, table, integer, integer +function xLib.Raycast.FromScreen(depth, ...) + local worldCoords, normalVector = GetWorldCoordFromScreenCoord(0.5, 0.5) + local origin = worldCoords + normalVector + local target = worldCoords + normalVector * depth + return target, xLib.Raycast.GetShapeTestResult(StartShapeTestLosProbe(origin.x, origin.y, origin.z, target.x, target.y, target.z, ...)) +end + +-- Start continuous raycasting +---@param depth number The raycast distance +---@vararg any Additional arguments to pass to shape test +function xLib.Raycast.Start(depth, ...) + local self = setmetatable({}, xLib.Raycast) + + self.refreshRate = refreshRate + self.depth = depth + self.args = {...} + + self.active = true + + self._thread = CreateThread(function() + while self.active do + local target, hit, coords, normal, material, entity = xLib.Raycast.FromScreen(self.depth, unpack(self.args)) + self.result = { + hit = hit, + coords = coords, + normal = normal, + material = material, + entity = entity, + } + Wait(1) + end + end) + + return self +end + +-- Stop raycasting +function xLib.Raycast:Stop() + if not self and not self.active then print('already stopped') return end + self.active = false + if self._thread then + self._thread = nil + end +end + +-- Check if the raycating is active +---@return boolean +function xLib.Raycast:IsActive() + if not self and not self.active then return end + return self.active +end + +return xLib.Raycast From 109a5c6038094048248bc2f708cbcd26f29cc22a Mon Sep 17 00:00:00 2001 From: SNAILX <180862994+SNAILX808@users.noreply.github.com> Date: Tue, 11 Nov 2025 03:31:19 +0100 Subject: [PATCH 6/8] small fixes --- [core]/es_extended/client/compat.lua | 3 +++ [core]/esx_lib/imports/raycast/client.lua | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index feef08bf..a4b3cfce 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -3,17 +3,20 @@ ESX.Game.GetShapeTestResultSync = xLib.Raycast.GetShapeTestResult ESX.Game.RaycastScreen = xLib.Raycast.FromScreen ESX.Game.StartRaycasting = xLib.Raycast.Start +---@param raycast table The raycast object returned from ESX.Game.StartRaycasting ESX.Game.StopRaycasting = function(raycast) if raycast and raycast.active then raycast:Stop() end end +---@param raycast table The raycast object returned from ESX.Game.StartRaycasting ESX.Game.IsRaycastActive = function(raycast) if raycast and raycast.active then return raycast:IsActive() end return false end +---@param raycast table The raycast object returned from ESX.Game.StartRaycasting ESX.Game.GetRaycastResult = function(raycast) if raycast and raycast.active then return raycast.result diff --git a/[core]/esx_lib/imports/raycast/client.lua b/[core]/esx_lib/imports/raycast/client.lua index f0464be5..131fc79b 100644 --- a/[core]/esx_lib/imports/raycast/client.lua +++ b/[core]/esx_lib/imports/raycast/client.lua @@ -60,7 +60,7 @@ end -- Stop raycasting function xLib.Raycast:Stop() - if not self and not self.active then print('already stopped') return end + if not self and not self.active then return end self.active = false if self._thread then self._thread = nil From 9b9b4b3fa4ea73ed2c60849e101415db3665bfe3 Mon Sep 17 00:00:00 2001 From: SNAILX <180862994+SNAILX808@users.noreply.github.com> Date: Tue, 11 Nov 2025 05:52:38 +0100 Subject: [PATCH 7/8] Move-refactor-entity-functions-esx_lib --- [core]/es_extended/client/compat.lua | 6 +- [core]/es_extended/client/functions.lua | 82 ----------------------- [core]/esx_lib/imports/entity/client.lua | 85 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 83 deletions(-) create mode 100644 [core]/esx_lib/imports/entity/client.lua diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index 254148ac..96a57a55 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -1 +1,5 @@ ---All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: \ No newline at end of file +--All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: + +ESX.Game.GetClosestEntity = xLib.entity.closest +EnumerateEntitiesWithinDistance = xLib.entity.EnumerateWithinDistance +ESX.Game.Teleport = xLib.entity.Teleport \ No newline at end of file diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 953296d2..419adc4e 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -473,26 +473,6 @@ function ESX.Game.GetPedMugshot(ped, transparent) return mugshot, GetPedheadshotTxdString(mugshot) end ----@param entity integer The entity to get the coords of ----@param coords table | vector3 | vector4 The coords to teleport the entity to ----@param cb? function The callback function -function ESX.Game.Teleport(entity, coords, cb) - - if DoesEntityExist(entity) then - RequestCollisionAtCoord(coords.x, coords.y, coords.z) - while not HasCollisionLoadedAroundEntity(entity) do - Wait(0) - end - - SetEntityCoords(entity, coords.x, coords.y, coords.z, false, false, false, false) - SetEntityHeading(entity, coords.w or coords.heading or 0.0) - end - - if cb then - cb() - end -end - ---@param object integer | string The object to spawn ---@param coords table | vector3 The coords to spawn the object at ---@param cb? function The callback function @@ -689,32 +669,6 @@ function ESX.Game.GetClosestVehicle(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetVehicles(), false, coords, modelFilter) end ----@param entities table The entities to search through ----@param isPlayerEntities boolean Whether the entities are players ----@param coords table | vector3 The coords to search from ----@param maxDistance number The max distance to search within ----@return table -local function EnumerateEntitiesWithinDistance(entities, isPlayerEntities, coords, maxDistance) - local nearbyEntities = {} - - if coords then - coords = vector3(coords.x, coords.y, coords.z) - else - local playerPed = ESX.PlayerData.ped - coords = GetEntityCoords(playerPed) - end - - for k, entity in pairs(entities) do - local distance = #(coords - GetEntityCoords(entity)) - - if distance <= maxDistance then - nearbyEntities[#nearbyEntities + 1] = isPlayerEntities and k or entity - end - end - - return nearbyEntities -end - ---@param coords table | vector3 The coords to search from ---@param maxDistance number The max distance to search within ---@return table @@ -757,42 +711,6 @@ function ESX.Game.RaycastScreen(depth, ...) return target, ESX.Game.GetShapeTestResultSync(StartShapeTestLosProbe(origin.x, origin.y, origin.z, target.x, target.y, target.z, ...)) end ----@param entities table The entities to search through ----@param isPlayerEntities boolean Whether the entities are players ----@param coords? table | vector3 The coords to search from ----@param modelFilter? table The model filter ----@return integer, integer -function ESX.Game.GetClosestEntity(entities, isPlayerEntities, coords, modelFilter) - local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil - - if coords then - coords = vector3(coords.x, coords.y, coords.z) - else - local playerPed = ESX.PlayerData.ped - coords = GetEntityCoords(playerPed) - end - - if modelFilter then - filteredEntities = {} - - for currentEntityIndex = 1, #entities do - if modelFilter[GetEntityModel(entities[currentEntityIndex])] then - filteredEntities[#filteredEntities + 1] = entities[currentEntityIndex] - end - end - end - - for k, entity in pairs(filteredEntities or entities) do - local distance = #(coords - GetEntityCoords(entity)) - - if closestEntityDistance == -1 or distance < closestEntityDistance then - closestEntity, closestEntityDistance = isPlayerEntities and k or entity, distance - end - end - - return closestEntity, closestEntityDistance -end - ---@return integer | nil, vector3 | nil function ESX.Game.GetVehicleInDirection() local _, hit, coords, _, _, entity = ESX.Game.RaycastScreen(5, 10, ESX.PlayerData.ped) diff --git a/[core]/esx_lib/imports/entity/client.lua b/[core]/esx_lib/imports/entity/client.lua new file mode 100644 index 00000000..6b861e3b --- /dev/null +++ b/[core]/esx_lib/imports/entity/client.lua @@ -0,0 +1,85 @@ +xLib.entity = {} + +---@param entities table The entities to search through +---@param isPlayerEntities boolean Whether the entities are players +---@param coords? table | vector3 The coords to search from +---@param modelFilter? table The model filter +---@return integer, integer +function xLib.entity.closest(entities, isPlayerEntities, coords, modelFilter) + local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil + + if coords then + coords = vector3(coords.x, coords.y, coords.z) + else + local playerPed = PlayerPedId() + coords = GetEntityCoords(playerPed) + end + + if modelFilter then + filteredEntities = {} + + for currentEntityIndex = 1, #entities do + if modelFilter[GetEntityModel(entities[currentEntityIndex])] then + filteredEntities[#filteredEntities + 1] = entities[currentEntityIndex] + end + end + end + + for k, entity in pairs(filteredEntities or entities) do + local distance = #(coords - GetEntityCoords(entity)) + + if closestEntityDistance == -1 or distance < closestEntityDistance then + closestEntity, closestEntityDistance = isPlayerEntities and k or entity, distance + end + end + + return closestEntity, closestEntityDistance +end + +---@param entities table The entities to search through +---@param isPlayerEntities boolean Whether the entities are players +---@param coords? table | vector3 The coords to search from +---@param maxDistance number The maximum distance +---@return table +function xLib.entity.EnumerateWithinDistance(entities, isPlayerEntities, coords, maxDistance) + local nearbyEntities = {} + + if coords then + coords = vector3(coords.x, coords.y, coords.z) + else + local playerPed = PlayerPedId() + coords = GetEntityCoords(playerPed) + end + + for k, entity in pairs(entities) do + local distance = #(coords - GetEntityCoords(entity)) + + if distance <= maxDistance then + nearbyEntities[#nearbyEntities + 1] = isPlayerEntities and k or entity + end + end + + return nearbyEntities +end + +---@param entity integer The entity to get the coords of +---@param coords table | vector3 | vector4 The coords to teleport the entity to +---@param cb? function The callback function +function xLib.entity.Teleport(entity, coords, cb) + + if DoesEntityExist(entity) then + RequestCollisionAtCoord(coords.x, coords.y, coords.z) + while not HasCollisionLoadedAroundEntity(entity) do + Wait(0) + end + + SetEntityCoords(entity, coords.x, coords.y, coords.z, false, false, false, false) + SetEntityHeading(entity, coords.w or coords.heading or 0.0) + end + + if cb then + cb() + end +end + +return xLib.entity \ No newline at end of file From d3868429ed02461809702712a648616fc2882852 Mon Sep 17 00:00:00 2001 From: SNAILX Date: Mon, 15 Jun 2026 02:08:30 +0100 Subject: [PATCH 8/8] Moved esx.table from the core to esx_lib --- [core]/es_extended/shared/compat.lua | 18 +- [core]/es_extended/shared/modules/table.lua | 241 -------------------- [core]/esx_lib/imports/table/shared.lua | 226 ++++++++++++++++++ 3 files changed, 243 insertions(+), 242 deletions(-) delete mode 100644 [core]/es_extended/shared/modules/table.lua diff --git a/[core]/es_extended/shared/compat.lua b/[core]/es_extended/shared/compat.lua index 68492380..182657d8 100644 --- a/[core]/es_extended/shared/compat.lua +++ b/[core]/es_extended/shared/compat.lua @@ -1 +1,17 @@ ---All shared functions outsourced from the Core to the lib will be stored here for compatability, e.g: \ No newline at end of file +--All shared functions outsourced from the Core to the lib will be stored here for compatability, e.g: +ESX.Table.SizeOf = xLib.table.size +ESX.Table.Set = xLib.table.set +ESX.Table.IndexOf = xLib.table.indexOf +ESX.Table.LastIndexOf = xLib.table.lastIndexOf +ESX.Table.Find = xLib.table.find +ESX.Table.FindIndex = xLib.table.findIndex +ESX.Table.Filter = xLib.table.filter +ESX.Table.Map = xLib.table.map +ESX.Table.Reverse = xLib.table.reverse +ESX.Table.Clone = xLib.table.clone +ESX.Table.Concat = xLib.table.concat +ESX.Table.Join = xLib.table.join +ESX.Table.TableContains = xLib.table.contains +ESX.Table.Sort = xLib.table.sort +ESX.Table.ToArray = xLib.table.toArray +ESX.Table.Wipe = xLib.table.wipe diff --git a/[core]/es_extended/shared/modules/table.lua b/[core]/es_extended/shared/modules/table.lua deleted file mode 100644 index ab4b147d..00000000 --- a/[core]/es_extended/shared/modules/table.lua +++ /dev/null @@ -1,241 +0,0 @@ -ESX.Table = {} - --- nil proof alternative to #table ----@param t table ----@return number -function ESX.Table.SizeOf(t) - local count = 0 - - for _, _ in pairs(t) do - count = count + 1 - end - - return count -end - ----@param t table ----@return table -function ESX.Table.Set(t) - local set = {} - for _, v in ipairs(t) do - set[v] = true - end - return set -end - ----@param t table ----@param value any ----@return number -function ESX.Table.IndexOf(t, value) - for i = 1, #t, 1 do - if t[i] == value then - return i - end - end - - return -1 -end - ----@param t table ----@param value any ----@return number -function ESX.Table.LastIndexOf(t, value) - for i = #t, 1, -1 do - if t[i] == value then - return i - end - end - - return -1 -end - ----@param t table ----@param cb function ----@return any -function ESX.Table.Find(t, cb) - for i = 1, #t, 1 do - if cb(t[i]) then - return t[i] - end - end - - return nil -end - ----@param t table ----@param cb function ----@return number -function ESX.Table.FindIndex(t, cb) - for i = 1, #t, 1 do - if cb(t[i]) then - return i - end - end - - return -1 -end - ----@param t table ----@param cb function ----@return table -function ESX.Table.Filter(t, cb) - local newTable = {} - - for i = 1, #t, 1 do - if cb(t[i]) then - newTable[#newTable + 1] = t[i] - end - end - - return newTable -end - ----@param t table ----@param cb function ----@return table -function ESX.Table.Map(t, cb) - local newTable = {} - - for i = 1, #t, 1 do - newTable[i] = cb(t[i], i) - end - - return newTable -end - ----@param t table ----@return table -function ESX.Table.Reverse(t) - local newTable = {} - - for i = #t, 1, -1 do - table.insert(newTable, t[i]) - end - - return newTable -end - ----@param t table ----@return table -function ESX.Table.Clone(t) - if type(t) ~= "table" then - return t - end - - local meta = getmetatable(t) - local target = {} - - for k, v in pairs(t) do - if type(v) == "table" then - target[k] = ESX.Table.Clone(v) - else - target[k] = v - end - end - - setmetatable(target, meta) - - return target -end - ----@param t1 table ----@param t2 table ----@return table -function ESX.Table.Concat(t1, t2) - local t3 = ESX.Table.Clone(t1) - - for i = 1, #t2, 1 do - table.insert(t3, t2[i]) - end - - return t3 -end - ----@param t table ----@param sep string ----@return string -function ESX.Table.Join(t, sep) - local str = "" - - for i = 1, #t, 1 do - if i > 1 then - str = str .. (sep or ",") - end - - str = str .. t[i] - end - - return str -end - --- Credits: https://github.com/JonasDev99/qb-garages/blob/b0335d67cb72a6b9ac60f62a87fb3946f5c2f33d/server/main.lua#L5 ----@param tab table ----@param val any ----@return boolean -function ESX.Table.TableContains(tab, val) - if type(val) == "table" then - for _, value in pairs(tab) do - if ESX.Table.TableContains(val, value) then - return true - end - end - return false - else - for _, value in pairs(tab) do - if value == val then - return true - end - end - end - return false -end - --- Credit: https://stackoverflow.com/a/15706820 --- Description: sort function for pairs ----@param t table ----@param order function ----@return function -function ESX.Table.Sort(t, order) - -- collect the keys - local keys = {} - - for k, _ in pairs(t) do - keys[#keys + 1] = k - end - - -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys - if order then - table.sort(keys, function(a, b) - return order(t, a, b) - end) - else - table.sort(keys) - end - - -- return the iterator function - local i = 0 - - return function() - i = i + 1 - if keys[i] then - return keys[i], t[keys[i]] - end - end -end - ----@param t table ----@return Array -function ESX.Table.ToArray(t) - local array = {} - for _, v in pairs(t) do - array[#array + 1] = v - end - return array -end - ----@param t table ----@return table -function ESX.Table.Wipe(t) - return table.wipe(t) -end \ No newline at end of file diff --git a/[core]/esx_lib/imports/table/shared.lua b/[core]/esx_lib/imports/table/shared.lua index c3550717..ed49db65 100644 --- a/[core]/esx_lib/imports/table/shared.lua +++ b/[core]/esx_lib/imports/table/shared.lua @@ -130,4 +130,230 @@ function xLib.table.dump(tbl) end end +-- nil proof alternative to #table +---@param t table +---@return number +function xLib.table.sizeOf(t) + local count = 0 + + for _, _ in pairs(t) do + count = count + 1 + end + + return count +end + +---@param t table +---@return table +function xLib.table.set(t) + local set = {} + for _, v in ipairs(t) do + set[v] = true + end + return set +end + +---@param t table +---@param value any +---@return number +function xLib.table.indexOf(t, value) + for i = 1, #t, 1 do + if t[i] == value then + return i + end + end + + return -1 +end + +---@param t table +---@param value any +---@return number +function xLib.table.lastIndexOf(t, value) + for i = #t, 1, -1 do + if t[i] == value then + return i + end + end + + return -1 +end + +---@param t table +---@param cb function +---@return any +function xLib.table.find(t, cb) + for i = 1, #t, 1 do + if cb(t[i]) then + return t[i] + end + end + + return nil +end + +---@param t table +---@param cb function +---@return number +function xLib.table.findIndex(t, cb) + for i = 1, #t, 1 do + if cb(t[i]) then + return i + end + end + + return -1 +end + +---@param t table +---@param cb function +---@return table +function xLib.table.map(t, cb) + local newTable = {} + + for i = 1, #t, 1 do + newTable[i] = cb(t[i], i) + end + + return newTable +end + +---@param t table +---@return table +function xLib.table.reverse(t) + local newTable = {} + + for i = #t, 1, -1 do + table.insert(newTable, t[i]) + end + + return newTable +end + +---@param t table +---@return table +function xLib.table.clone(t) + if type(t) ~= "table" then + return t + end + + local meta = getmetatable(t) + local target = {} + + for k, v in pairs(t) do + if type(v) == "table" then + target[k] = xLib.table.clone(v) + else + target[k] = v + end + end + + setmetatable(target, meta) + + return target +end + +---@param t1 table +---@param t2 table +---@return table +function xLib.table.concat(t1, t2) + local t3 = xLib.table.clone(t1) + + for i = 1, #t2, 1 do + table.insert(t3, t2[i]) + end + + return t3 +end + +---@param t table +---@param sep string +---@return string +function xLib.table.join(t, sep) + local str = "" + + for i = 1, #t, 1 do + if i > 1 then + str = str .. (sep or ",") + end + + str = str .. t[i] + end + + return str +end + +-- Credits: https://github.com/JonasDev99/qb-garages/blob/b0335d67cb72a6b9ac60f62a87fb3946f5c2f33d/server/main.lua#L5 +---@param tab table +---@param val any +---@return boolean +function xLib.table.contains(tab, val) + if type(val) == "table" then + for _, value in pairs(tab) do + if xLib.table.contains(val, value) then + return true + end + end + return false + else + for _, value in pairs(tab) do + if value == val then + return true + end + end + end + return false +end + +-- Credit: https://stackoverflow.com/a/15706820 +-- Description: sort function for pairs +---@param t table +---@param order function +---@return function +function xLib.table.sort(t, order) + -- collect the keys + local keys = {} + + for k, _ in pairs(t) do + keys[#keys + 1] = k + end + + -- if order function given, sort by it by passing the table and keys a, b, + -- otherwise just sort the keys + if order then + table.sort(keys, function(a, b) + return order(t, a, b) + end) + else + table.sort(keys) + end + + -- return the iterator function + local i = 0 + + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] + end + end +end + +---@param t table +---@return Array +function xLib.table.toArray(t) + local array = {} + for _, v in pairs(t) do + array[#array + 1] = v + end + return array +end + +---@param t table +---@return table +function xLib.table.wipe(t) + return table.wipe(t) +end + + return xLib.table