Merge branch 'dev' into new-docs-link

This commit is contained in:
Kasey FItton
2025-01-04 02:26:49 +00:00
3 changed files with 182 additions and 53 deletions
+171 -50
View File
@@ -1,5 +1,5 @@
ESX = exports["es_extended"]:getSharedObject() ESX = exports["es_extended"]:getSharedObject()
_resourceName = GetCurrentResourceName() ESX.currentResourceName = GetCurrentResourceName()
OnPlayerData = function (key, val, last) end OnPlayerData = function (key, val, last) end
@@ -44,67 +44,188 @@ if not IsDuplicityVersion() then -- Only register this event for the client
end end
end end
if not lib?.require then if GetResourceState("ox_lib") == "missing" then
local cachedModules = {} ---@type table<string, any> ---@Credits: https://github.com/overextended/ox_lib/blob/master/imports/require/shared.lua - Licensed under the GNU Lesser General Public License v3.0
local loadingModules = {} ---@type table<string, true?> local loaded = {}
local _require = require
---@param modulePath string package = {
path = './?.lua;./?/init.lua',
preload = {},
loaded = setmetatable({}, {
__index = loaded,
__newindex = function() end,
__metatable = false,
})
}
---@param modName string
---@return string ---@return string
local function getResourceNameFromModulePath(modulePath) ---@return string
local externalResourceName = modulePath:match("^@(.-)%.") local function getModuleInfo(modName)
if externalResourceName then local resource = modName:match('^@(.-)/.+') --[[@as string?]]
return externalResourceName
if resource then
return resource, modName:sub(#resource + 3)
end end
return _resourceName local idx = 4 -- call stack depth (kept slightly lower than expected depth "just in case")
while true do
local dbgInfo = debug.getinfo(idx, 'S')
local src = dbgInfo and dbgInfo.source
if not src then
return ESX.currentResourceName, modName
end
resource = src:match('^@@([^/]+)/.+')
if resource and not src:find('^@@es_extended/imports') then
return resource, modName
end
idx = idx + 1
end
end end
---@param modulePath string local tempData = {}
---@return string, number
local function getModuleFilePath(modulePath) ---@param name string
if modulePath:sub(1, 1) == "@" then ---@param path string
modulePath = modulePath:sub(modulePath:find("%.") + 1) ---@return string? filename
---@return string? errmsg
---@diagnostic disable-next-line: duplicate-set-field
function package.searchpath(name, path)
local resource, modName = getModuleInfo(name:gsub('%.', '/'))
local tried = {}
for template in path:gmatch('[^;]+') do
local fileName = template:gsub('^%./', ''):gsub('?', modName:gsub('%.', '/') or modName)
local file = LoadResourceFile(resource, fileName)
if file then
tempData[1] = file
tempData[2] = resource
return fileName
end
tried[#tried + 1] = ("no file '@%s/%s'"):format(resource, fileName)
end end
return modulePath:gsub("%.", "/") return nil, table.concat(tried, "\n\t")
end end
---@param modulePath string ---Attempts to load a module at the given path relative to the resource root directory.\
---@return any ---Returns a function to load the module chunk, or a string containing all tested paths.
function require(modulePath) ---@param modName string
assert(type(modulePath) == "string", "Module path must be a string") ---@param env? table
local function loadModule(modName, env)
local fileName, err = package.searchpath(modName, package.path)
if loadingModules[modulePath] then if fileName then
error(("Circular dependency detected for module '%s'."):format(modulePath)) local file = tempData[1]
local resource = tempData[2]
ESX.Table.Wipe(tempData)
return assert(load(file, ('@@%s/%s'):format(resource, fileName), 't', env or _ENV))
end end
if cachedModules[modulePath] then return nil, err or 'unknown error'
return cachedModules[modulePath]
end
loadingModules[modulePath] = true
local resourceName = getResourceNameFromModulePath(modulePath)
local moduleFilePath = getModuleFilePath(modulePath)
local moduleFileContent = LoadResourceFile(resourceName, moduleFilePath .. ".lua")
if not moduleFileContent then
loadingModules[modulePath] = nil
error(("Module '%s' not found in resource '%s'."):format(moduleFilePath, resourceName))
end
local chunk, err = load(moduleFileContent, ("@%s/%s"):format(resourceName, moduleFilePath), "t")
if not chunk then
loadingModules[modulePath] = nil
error(("Failed to load module '%s': %s"):format(moduleFilePath, err))
end
local result = chunk()
cachedModules[modulePath] = result ~= nil and result or true
loadingModules[modulePath] = nil
return result
end end
---@alias PackageSearcher
---| fun(modName: string): function loader
---| fun(modName: string): nil, string errmsg
---@type PackageSearcher[]
package.searchers = {
function(modName)
local ok, result = pcall(_require, modName)
if ok then return result end
return ok, result
end,
function(modName)
if package.preload[modName] ~= nil then
return package.preload[modName]
end
return nil, ("no field package.preload['%s']"):format(modName)
end,
function(modName) return loadModule(modName) end,
}
---@param filePath string
---@param env? table
---@return unknown
---Loads and runs a Lua file at the given path. Unlike require, the chunk is not cached for future use.
function ESX.load(filePath, env)
if type(filePath) ~= 'string' then
error(("file path must be a string (received '%s')"):format(filePath), 2)
end
local result, err = loadModule(filePath, env)
if result then return result() end
error(("file '%s' not found\n\t%s"):format(filePath, err))
end
---@param filePath string
---@return table
---Loads and decodes a json file at the given path.
function ESX.loadJson(filePath)
if type(filePath) ~= 'string' then
error(("file path must be a string (received '%s')"):format(filePath), 2)
end
local resourceSrc, modPath = getModuleInfo(filePath:gsub('%.', '/'))
local resourceFile = LoadResourceFile(resourceSrc, ('%s.json'):format(modPath))
if resourceFile then
return json.decode(resourceFile)
end
error(("json file '%s' not found\n\tno file '@%s/%s.json'"):format(filePath, resourceSrc, modPath))
end
---Loads the given module, returns any value returned by the seacher (`true` when `nil`).\
---Passing `@resourceName.modName` loads a module from a remote resource.
---@param modName string
---@return unknown
function ESX.require(modName)
if type(modName) ~= 'string' then
error(("module name must be a string (received '%s')"):format(modName), 3)
end
local module = loaded[modName]
if module == '__loading' then
error(("^1circular-dependency occurred when loading module '%s'^0"):format(modName), 2)
end
if module ~= nil then return module end
loaded[modName] = '__loading'
local err = {}
for i = 1, #package.searchers do
local result, errMsg = package.searchers[i](modName)
if result then
if type(result) == 'function' then result = result() end
loaded[modName] = result or result == nil
return loaded[modName]
end
err[#err + 1] = errMsg
end
error(("%s"):format(table.concat(err, "\n\t")))
end
require = ESX.require
end end
+1 -1
View File
@@ -563,7 +563,7 @@ end
---@return table ---@return table
function ESX.GetItems() function ESX.GetItems()
return Core.Items return ESX.Items
end end
---@return table ---@return table
+10 -2
View File
@@ -224,10 +224,18 @@ function ESX.Table.Sort(t, order)
end end
end end
function ESX.Table.ToArray(table) ---@param t table
---@return Array
function ESX.Table.ToArray(t)
local array = {} local array = {}
for _, v in pairs(table) do for _, v in pairs(t) do
array[#array + 1] = v array[#array + 1] = v
end end
return array return array
end end
---@param t table
---@return table
function ESX.Table.Wipe(t)
return table.wipe(t)
end