diff --git a/[core]/cron/README.md b/[core]/cron/README.md
index 8ecd4386c..df2e5eeb9 100644
--- a/[core]/cron/README.md
+++ b/[core]/cron/README.md
@@ -1,4 +1,4 @@
-
[ESX] Cron
Discord - Website - Documentation
+
[ESX] Cron
Discord - Website - Documentation
A simple, but vital, resource that allows resources to Run tasks at specific intervals.
diff --git a/[core]/es_extended/README.md b/[core]/es_extended/README.md
index f02b5f71e..32ae43b86 100644
--- a/[core]/es_extended/README.md
+++ b/[core]/es_extended/README.md
@@ -1,13 +1,13 @@
-
es_extended
Discord - Documentation
-
-## Legal
-
-es_extended
-
-Copyright (C) 2015-2024 Jérémie N'gadi
-
-This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version.
-
-This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details.
-
-You should have received a copy Of the GNU General Public License along with this program. If Not, see .
+
es_extended
Discord - Documentation
+
+## Legal
+
+es_extended
+
+Copyright (C) 2015-2024 Jérémie N'gadi
+
+This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version.
+
+This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details.
+
+You should have received a copy Of the GNU General Public License along with this program. If Not, see .
diff --git a/[core]/es_extended/imports.lua b/[core]/es_extended/imports.lua
index 6d6a862e1..719d06237 100644
--- a/[core]/es_extended/imports.lua
+++ b/[core]/es_extended/imports.lua
@@ -1,5 +1,5 @@
ESX = exports["es_extended"]:getSharedObject()
-_resourceName = GetCurrentResourceName()
+ESX.currentResourceName = GetCurrentResourceName()
OnPlayerData = function (key, val, last) end
@@ -44,67 +44,188 @@ if not IsDuplicityVersion() then -- Only register this event for the client
end
end
-if not lib?.require then
- local cachedModules = {} ---@type table
- local loadingModules = {} ---@type table
-
- ---@param modulePath string
+if GetResourceState("ox_lib") == "missing" then
+ ---@Credits: https://github.com/overextended/ox_lib/blob/master/imports/require/shared.lua - Licensed under the GNU Lesser General Public License v3.0
+ local loaded = {}
+ local _require = require
+
+ 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)
- local externalResourceName = modulePath:match("^@(.-)%.")
- if externalResourceName then
- return externalResourceName
+ local function getModuleInfo(modName)
+ local resource = modName:match('^@(.-)/.+') --[[@as string?]]
+
+ if resource then
+ return resource, modName:sub(#resource + 3)
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
- ---@param modulePath string
- ---@return string, number
- local function getModuleFilePath(modulePath)
- if modulePath:sub(1, 1) == "@" then
- modulePath = modulePath:sub(modulePath:find("%.") + 1)
+ local tempData = {}
+
+ ---@param name string
+ ---@param path string
+ ---@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
- return modulePath:gsub("%.", "/")
+ return nil, table.concat(tried, "\n\t")
end
- ---@param modulePath string
- ---@return any
- function require(modulePath)
- assert(type(modulePath) == "string", "Module path must be a string")
+ ---Attempts to load a module at the given path relative to the resource root directory.\
+ ---Returns a function to load the module chunk, or a string containing all tested paths.
+ ---@param modName string
+ ---@param env? table
+ local function loadModule(modName, env)
+ local fileName, err = package.searchpath(modName, package.path)
- if loadingModules[modulePath] then
- error(("Circular dependency detected for module '%s'."):format(modulePath))
+ if fileName then
+ 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
- if cachedModules[modulePath] then
- return cachedModules[modulePath]
+ return nil, err or 'unknown error'
+ 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
- loadingModules[modulePath] = true
+ 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
- local resourceName = getResourceNameFromModulePath(modulePath)
- local moduleFilePath = getModuleFilePath(modulePath)
- local moduleFileContent = LoadResourceFile(resourceName, moduleFilePath .. ".lua")
+ error(("json file '%s' not found\n\tno file '@%s/%s.json'"):format(filePath, resourceSrc, modPath))
+ end
- if not moduleFileContent then
- loadingModules[modulePath] = nil
- error(("Module '%s' not found in resource '%s'."):format(moduleFilePath, resourceName))
+ ---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 chunk, err = load(moduleFileContent, ("@%s/%s"):format(resourceName, moduleFilePath), "t")
+ local module = loaded[modName]
- if not chunk then
- loadingModules[modulePath] = nil
- error(("Failed to load module '%s': %s"):format(moduleFilePath, err))
+ if module == '__loading' then
+ error(("^1circular-dependency occurred when loading module '%s'^0"):format(modName), 2)
end
- local result = chunk()
+ if module ~= nil then return module end
+
+ loaded[modName] = '__loading'
- cachedModules[modulePath] = result ~= nil and result or true
- loadingModules[modulePath] = nil
+ local err = {}
- return result
+ 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
diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua
index a7566ffbc..124170650 100644
--- a/[core]/es_extended/server/functions.lua
+++ b/[core]/es_extended/server/functions.lua
@@ -563,7 +563,7 @@ end
---@return table
function ESX.GetItems()
- return Core.Items
+ return ESX.Items
end
---@return table
diff --git a/[core]/es_extended/shared/main.lua b/[core]/es_extended/shared/main.lua
index 00f775b46..5d659daaa 100644
--- a/[core]/es_extended/shared/main.lua
+++ b/[core]/es_extended/shared/main.lua
@@ -9,8 +9,8 @@ AddEventHandler("esx:getSharedObject", function(cb)
cb(ESX)
end
local invokingResource = GetInvokingResource()
- print(("^3[WARNING]^0 Resource ^5%s^0 used the ^5getSharedObject^0 event. This is not the recommended way to import ESX. Visit https://documentation.esx-framework.org/tutorials/tutorials-esx/sharedevent to find out why."):format(invokingResource))
+ print(("^3[WARNING]^0 Resource ^5%s^0 used the ^5getSharedObject^0 event. This is not the recommended way to import ESX. Visit https://docs.esx-legacy.com/tutorials/tutorials-esx/sharedevent to find out why."):format(invokingResource))
end)
--- backwards compatibility (DO NOT TOUCH !)
-Config.OxInventory = Config.CustomInventory == "ox"
\ No newline at end of file
+-- backwards compatibility (DO NOT TOUCH !)
+Config.OxInventory = Config.CustomInventory == "ox"
diff --git a/[core]/es_extended/shared/modules/table.lua b/[core]/es_extended/shared/modules/table.lua
index aacb25840..ab4b147d8 100644
--- a/[core]/es_extended/shared/modules/table.lua
+++ b/[core]/es_extended/shared/modules/table.lua
@@ -224,10 +224,18 @@ function ESX.Table.Sort(t, order)
end
end
-function ESX.Table.ToArray(table)
+---@param t table
+---@return Array
+function ESX.Table.ToArray(t)
local array = {}
- for _, v in pairs(table) do
+ 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_chat_theme/README.md b/[core]/esx_chat_theme/README.md
index c6c88f0ef..1d149d4ad 100644
--- a/[core]/esx_chat_theme/README.md
+++ b/[core]/esx_chat_theme/README.md
@@ -1,4 +1,4 @@
-[ESX] Chat Theme
Discord - Documentation
+
[ESX] Chat Theme
Discord - Documentation
A ESX-Based Chat-theme for your server
diff --git a/[core]/esx_context/README.md b/[core]/esx_context/README.md
index c4e378a94..6b6a53de4 100644
--- a/[core]/esx_context/README.md
+++ b/[core]/esx_context/README.md
@@ -1,4 +1,4 @@
-
[ESX] Context
Discord - Website - Documentation
+
[ESX] Context
Discord - Website - Documentation
A elegant, easy to use Context Menu system to make User Interactions clean and hassle free
diff --git a/[core]/esx_identity/README.md b/[core]/esx_identity/README.md
index 3747c631c..c06370815 100644
--- a/[core]/esx_identity/README.md
+++ b/[core]/esx_identity/README.md
@@ -1,4 +1,4 @@
-
[ESX] Identity
Discord - Documentation
+
[ESX] Identity
Discord - Documentation
A Core Resource that Allows the player to Pick their characters, Name, Gender, Height and Date-of-birth.
diff --git a/[core]/esx_loadingscreen/README.md b/[core]/esx_loadingscreen/README.md
index 1f51ae3e0..366c5adaa 100644
--- a/[core]/esx_loadingscreen/README.md
+++ b/[core]/esx_loadingscreen/README.md
@@ -1,4 +1,4 @@
-
[ESX] Loading Screen
Discord - Website - Documentation
+
[ESX] Loading Screen
Discord - Website - Documentation
A simple but beautiful Loading Screen for your server!
diff --git a/[core]/esx_menu_default/README.md b/[core]/esx_menu_default/README.md
index 05abc0380..29e07d1f5 100644
--- a/[core]/esx_menu_default/README.md
+++ b/[core]/esx_menu_default/README.md
@@ -1,4 +1,4 @@
-
[ESX] Menu Defualt
Discord - Website - Documentation
+
[ESX] Menu Defualt
Discord - Website - Documentation
A default List type menu for ESX.
diff --git a/[core]/esx_multicharacter/readme.md b/[core]/esx_multicharacter/readme.md
index 58b30ff43..2517728e7 100644
--- a/[core]/esx_multicharacter/readme.md
+++ b/[core]/esx_multicharacter/readme.md
@@ -1,4 +1,4 @@
-
[ESX] Multi-Character
Discord - Website - Documentation
+
[ESX] Multi-Character
Discord - Website - Documentation
A Simplistic system, that allows Players to have multiple Characters, which can be customised for all player with `Config.Slots` or personally set a players character count using `setslots`, `remslots`, `enablechar` and `disablechar` Commands :)
diff --git a/[core]/esx_notify/readme.md b/[core]/esx_notify/readme.md
index 666b34988..f6bc4c9d7 100644
--- a/[core]/esx_notify/readme.md
+++ b/[core]/esx_notify/readme.md
@@ -1,4 +1,4 @@
-
[ESX] Notify
Discord - Website - Documentation
+
[ESX] Notify
Discord - Website - Documentation
A beautiful and simple NUI notification system for ESX
diff --git a/[core]/esx_textui/readme.md b/[core]/esx_textui/readme.md
index 32796da39..922e11a86 100644
--- a/[core]/esx_textui/readme.md
+++ b/[core]/esx_textui/readme.md
@@ -1,4 +1,4 @@
-
[ESX] TextUI
Discord - Website - Documentation
+
[ESX] TextUI
Discord - Website - Documentation
A beautiful and simple Persistent Notification.
diff --git a/[core]/skinchanger/README.md b/[core]/skinchanger/README.md
index cd763d5c7..7b1b3dd6f 100644
--- a/[core]/skinchanger/README.md
+++ b/[core]/skinchanger/README.md
@@ -1,4 +1,4 @@
-
[ESX] SkinChanger
Discord - Website - Documentation
+
[ESX] SkinChanger
Discord - Website - Documentation
skinchanger is a resource used to both Set and Get Players clothing, accessories and Model - It supports the freemode peds `mp_m_freemode_01` and `mp_f_freemode_01` as well as all Ped Features.
diff --git a/readme.md b/readme.md
index ef2abad0b..3537e4307 100644
--- a/readme.md
+++ b/readme.md
@@ -1,19 +1,19 @@
-
ESX Legacy
-Discord - Website - Documentation
-
-
Want more resources? You can browse the Cfx.re Releases board for more!
-
ESX is the leading framework, trusted by over 12,000 communities to provide the highest quality roleplay servers on FiveM
-
-
-
-### 💗 Supporters
-
-Interested in helping us? [Take a look at our patreon](https://www.patreon.com/esx "Take a look at our patreon")
-
-| We would like to sincerely thank the following donors who helped fund the development of ESX. |
-| ------------ |
-| Mohamad Buhamad - Michael Hein - RoadToSix - Montree Narathong |
-| Saydoon - Muhannad alyamani - iSentrie - Wecity - Samuel Nicol |
-| Kyle McShea - Artin - Mathias Christoffersen - Jaylan Yilmaz - Callum |
-| CONGRESS KW - Michael Hein - Smery sitbon - daZepelin - CMF Community |
-------
+ESX Legacy
+Discord - Website - Documentation
+
+
Want more resources? You can browse the Cfx.re Releases board for more!
+
ESX is the leading framework, trusted by over 12,000 communities to provide the highest quality roleplay servers on FiveM
+
+
+
+### 💗 Supporters
+
+Interested in helping us? [Take a look at our patreon](https://www.patreon.com/esx "Take a look at our patreon")
+
+| We would like to sincerely thank the following donors who helped fund the development of ESX. |
+| ------------ |
+| Mohamad Buhamad - Michael Hein - RoadToSix - Montree Narathong |
+| Saydoon - Muhannad alyamani - iSentrie - Wecity - Samuel Nicol |
+| Kyle McShea - Artin - Mathias Christoffersen - Jaylan Yilmaz - Callum |
+| CONGRESS KW - Michael Hein - Smery sitbon - daZepelin - CMF Community |
+------