Merge remote-tracking branch 'Snail/esx_lib' into 1.14.0

This commit is contained in:
_Not_
2026-07-15 23:44:49 -05:00
9 changed files with 538 additions and 364 deletions
+39 -1
View File
@@ -5,4 +5,42 @@ ESX.Streaming.RequestStreamedTextureDict = xLib.streaming.requestStreamedTexture
ESX.Streaming.RequestNamedPtfxAsset = xLib.streaming.requestNamedPtfxAsset
ESX.Streaming.RequestAnimSet = xLib.streaming.requestAnimSet
ESX.Streaming.RequestAnimDict = xLib.streaming.requestAnimDict
ESX.Streaming.RequestWeaponAsset = xLib.streaming.requestWeaponAsset
ESX.Streaming.RequestWeaponAsset = xLib.streaming.requestWeaponAsset
ESX.Game.GetShapeTestResultSync = xLib.Raycast.GetShapeTestResult
ESX.Game.RaycastScreen = xLib.Raycast.FromScreen
ESX.Game.StartRaycasting = xLib.Raycast.Start
ESX.Game.GetClosestEntity = xLib.entity.closest
EnumerateEntitiesWithinDistance = xLib.entity.EnumerateWithinDistance
ESX.Game.Teleport = xLib.entity.Teleport
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
---@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
end
return nil
end
-119
View File
@@ -246,22 +246,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
@@ -474,26 +458,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
@@ -690,32 +654,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
@@ -737,63 +675,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
---@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)
+2 -2
View File
@@ -6,6 +6,7 @@ lua54 'yes'
version '1.13.5'
shared_scripts {
'@esx_lib/imports.lua',
'locale.lua',
'shared/config/main.lua',
@@ -47,6 +48,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',
@@ -60,8 +62,6 @@ client_scripts {
'client/modules/interactions.lua',
'client/modules/scaleform.lua',
'client/modules/streaming.lua',
'shared/compat.lua'
}
ui_page {
+17 -1
View File
@@ -2,4 +2,20 @@
ESX.SetTimeout = xLib.timeout.setTimeout
ESX.ClearTimeout = xLib.timeout.clearTimeout
ESX.Await = xLib.waitFor
ESX.Await = xLib.waitFor
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
-241
View File
@@ -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
@@ -0,0 +1,92 @@
--[[
https://github.com/overextended/ox_lib
This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
Copyright © 2025 Linden <https://github.com/thelindat>
]]
---@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 data KeybindProps
---@return CKeybind
function xLib.addKeybind(data)
data.hash = joaat("+" .. data.name) | 0x80000000
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 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 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
+85
View File
@@ -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
+77
View File
@@ -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 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
+226
View File
@@ -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