From 3c20728860b5a9eefb549724695caccc704a5f20 Mon Sep 17 00:00:00 2001 From: Zuntie Date: Sun, 9 Nov 2025 02:44:10 +0100 Subject: [PATCH 1/6] feat(esx_lib): initial DUI addition --- [core]/esx_lib/imports/dui/client.lua | 118 ++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 [core]/esx_lib/imports/dui/client.lua diff --git a/[core]/esx_lib/imports/dui/client.lua b/[core]/esx_lib/imports/dui/client.lua new file mode 100644 index 00000000..613a55d3 --- /dev/null +++ b/[core]/esx_lib/imports/dui/client.lua @@ -0,0 +1,118 @@ +--[[ + https://github.com/overextended/ox_lib + + This file is licensed under LGPL-3.0 or higher + + Copyright © 2025 Linden +]] + +---@class DuiProperties +---@field url string +---@field width number +---@field height number +---@field debug? boolean + +---@class Dui +---@field private private { id: string, debug: boolean } +---@field url string +---@field duiObject number +---@field duiHandle string +---@field runtimeTxd number +---@field txdObject number +---@field dictName string +---@field txtName string +xLib.dui = xLib.class() + +---@type table +local duis = {} + +local currentId = 0 + +---@param data DuiProperties +function xLib.dui:constructor(data) + local time = GetGameTimer() + local id = ("%s_%s_%s"):format(cache.resource, time, currentId) + currentId = currentId + 1 + local dictName = ('ox_lib_dui_dict_%s'):format(id) + local txtName = ('ox_lib_dui_txt_%s'):format(id) + local duiObject = CreateDui(data.url, data.width, data.height) + local duiHandle = GetDuiHandle(duiObject) + local runtimeTxd = CreateRuntimeTxd(dictName) + local txdObject = CreateRuntimeTextureFromDuiHandle(runtimeTxd, txtName, duiHandle) + self.private.id = id + self.private.debug = data.debug or false + self.url = data.url + self.duiObject = duiObject + self.duiHandle = duiHandle + self.runtimeTxd = runtimeTxd + self.txdObject = txdObject + self.dictName = dictName + self.txtName = txtName + duis[id] = self + + if self.private.debug then + print(('Dui %s created'):format(id)) + end +end + +function xLib.dui:remove() + SetDuiUrl(self.duiObject, 'about:blank') + DestroyDui(self.duiObject) + duis[self.private.id] = nil + + if self.private.debug then + print(('Dui %s removed'):format(self.private.id)) + end +end + +---@param url string +function xLib.dui:setUrl(url) + self.url = url + SetDuiUrl(self.duiObject, url) + + if self.private.debug then + print(('Dui %s url set to %s'):format(self.private.id, url)) + end +end + +---@param message table +function xLib.dui:sendMessage(message) + SendDuiMessage(self.duiObject, json.encode(message)) + + if self.private.debug then + print(('Dui %s message sent with data :'):format(self.private.id), json.encode(message, { indent = true })) + end +end + +---@param x number +---@param y number +function xLib.dui:sendMouseMove(x, y) + SendDuiMouseMove(self.duiObject, x, y) +end + +---@param button 'left' | 'middle' | 'right' +function xLib.dui:sendMouseDown(button) + SendDuiMouseDown(self.duiObject, button) +end + +---@param button 'left' | 'middle' | 'right' +function xLib.dui:sendMouseUp(button) + SendDuiMouseUp(self.duiObject, button) +end + +---@param deltaX number +---@param deltaY number +function xLib.dui:sendMouseWheel(deltaX, deltaY) + SendDuiMouseWheel(self.duiObject, deltaY, deltaX) +end + + +AddEventHandler('onResourceStop', function(resourceName) + if cache.resource ~= resourceName then return end + + for _, dui in pairs(duis) do + dui:remove() + end +end) + +return xLib.dui From edcfa009fb11266fbd9b1128323ed538b1826725 Mon Sep 17 00:00:00 2001 From: Zuntie Date: Sun, 9 Nov 2025 02:49:24 +0100 Subject: [PATCH 2/6] feat(esx_lib): DUI development guidelines compliant --- [core]/esx_lib/imports/dui/client.lua | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/[core]/esx_lib/imports/dui/client.lua b/[core]/esx_lib/imports/dui/client.lua index 613a55d3..4b8ffae1 100644 --- a/[core]/esx_lib/imports/dui/client.lua +++ b/[core]/esx_lib/imports/dui/client.lua @@ -13,7 +13,8 @@ ---@field debug? boolean ---@class Dui ----@field private private { id: string, debug: boolean } +---@field private_id string +---@field private_debug boolean ---@field url string ---@field duiObject number ---@field duiHandle string @@ -24,7 +25,7 @@ xLib.dui = xLib.class() ---@type table -local duis = {} +local Duis = {} local currentId = 0 @@ -33,14 +34,14 @@ function xLib.dui:constructor(data) local time = GetGameTimer() local id = ("%s_%s_%s"):format(cache.resource, time, currentId) currentId = currentId + 1 - local dictName = ('ox_lib_dui_dict_%s'):format(id) - local txtName = ('ox_lib_dui_txt_%s'):format(id) + local dictName = ("ox_lib_dui_dict_%s"):format(id) + local txtName = ("ox_lib_dui_txt_%s"):format(id) local duiObject = CreateDui(data.url, data.width, data.height) local duiHandle = GetDuiHandle(duiObject) local runtimeTxd = CreateRuntimeTxd(dictName) local txdObject = CreateRuntimeTextureFromDuiHandle(runtimeTxd, txtName, duiHandle) - self.private.id = id - self.private.debug = data.debug or false + self.private_id = id + self.private_debug = data.debug or false self.url = data.url self.duiObject = duiObject self.duiHandle = duiHandle @@ -48,20 +49,20 @@ function xLib.dui:constructor(data) self.txdObject = txdObject self.dictName = dictName self.txtName = txtName - duis[id] = self + Duis[id] = self - if self.private.debug then - print(('Dui %s created'):format(id)) + if self.private_debug then + print(("Dui %s created"):format(id)) end end function xLib.dui:remove() - SetDuiUrl(self.duiObject, 'about:blank') + SetDuiUrl(self.duiObject, "about:blank") DestroyDui(self.duiObject) - duis[self.private.id] = nil + Duis[self.private_id] = nil - if self.private.debug then - print(('Dui %s removed'):format(self.private.id)) + if self.private_debug then + print(("Dui %s removed"):format(self.private_id)) end end @@ -70,8 +71,8 @@ function xLib.dui:setUrl(url) self.url = url SetDuiUrl(self.duiObject, url) - if self.private.debug then - print(('Dui %s url set to %s'):format(self.private.id, url)) + if self.private_debug then + print(("Dui %s url set to %s"):format(self.private_id, url)) end end @@ -79,8 +80,8 @@ end function xLib.dui:sendMessage(message) SendDuiMessage(self.duiObject, json.encode(message)) - if self.private.debug then - print(('Dui %s message sent with data :'):format(self.private.id), json.encode(message, { indent = true })) + if self.private_debug then + print(("Dui %s message sent with data :"):format(self.private_id), json.encode(message, { indent = true })) end end @@ -90,12 +91,12 @@ function xLib.dui:sendMouseMove(x, y) SendDuiMouseMove(self.duiObject, x, y) end ----@param button 'left' | 'middle' | 'right' +---@param button "left" | "middle" | "right" function xLib.dui:sendMouseDown(button) SendDuiMouseDown(self.duiObject, button) end ----@param button 'left' | 'middle' | 'right' +---@param button "left" | "middle" | "right" function xLib.dui:sendMouseUp(button) SendDuiMouseUp(self.duiObject, button) end @@ -106,11 +107,10 @@ function xLib.dui:sendMouseWheel(deltaX, deltaY) SendDuiMouseWheel(self.duiObject, deltaY, deltaX) end - -AddEventHandler('onResourceStop', function(resourceName) +AddEventHandler("onResourceStop", function(resourceName) if cache.resource ~= resourceName then return end - for _, dui in pairs(duis) do + for _, dui in pairs(Duis) do dui:remove() end end) From 1a27c365b677ea1d19ced371505d240e37bd0143 Mon Sep 17 00:00:00 2001 From: Zuntie Date: Sun, 9 Nov 2025 17:44:52 +0100 Subject: [PATCH 3/6] fix(esx_lib): changed runtime texture names; tiny performance optimizaion. Working, Finished. --- [core]/esx_lib/imports/dui/client.lua | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/[core]/esx_lib/imports/dui/client.lua b/[core]/esx_lib/imports/dui/client.lua index 4b8ffae1..3fd9eb61 100644 --- a/[core]/esx_lib/imports/dui/client.lua +++ b/[core]/esx_lib/imports/dui/client.lua @@ -24,6 +24,8 @@ ---@field txtName string xLib.dui = xLib.class() +local resource = GetCurrentResourceName() + ---@type table local Duis = {} @@ -32,10 +34,10 @@ local currentId = 0 ---@param data DuiProperties function xLib.dui:constructor(data) local time = GetGameTimer() - local id = ("%s_%s_%s"):format(cache.resource, time, currentId) + local id = ("%s_%s_%s"):format(resource, time, currentId) currentId = currentId + 1 - local dictName = ("ox_lib_dui_dict_%s"):format(id) - local txtName = ("ox_lib_dui_txt_%s"):format(id) + local dictName = ("%s_dui_dict_%s"):format(resource, id) + local txtName = ("%s_lib_dui_txt_%s"):format(resource, id) local duiObject = CreateDui(data.url, data.width, data.height) local duiHandle = GetDuiHandle(duiObject) local runtimeTxd = CreateRuntimeTxd(dictName) @@ -108,11 +110,11 @@ function xLib.dui:sendMouseWheel(deltaX, deltaY) end AddEventHandler("onResourceStop", function(resourceName) - if cache.resource ~= resourceName then return end + if resource ~= resourceName then return end - for _, dui in pairs(Duis) do - dui:remove() - end + for id in next, Duis do + Duis[id]:remove() + end end) -return xLib.dui +return xLib.dui \ 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 4/6] 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 5/6] 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 6/6] 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