From 6b6667b4c5a20e96d3711e37e6bcfcd46b7efee0 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Dec 2024 22:33:47 +0100 Subject: [PATCH 001/132] Merge pull request #1539 from Kenshiin13/spawn-vehicle feat(es_extended): add promise support for vehicle spawn functions --- [core]/es_extended/client/functions.lua | 25 +++++++-- [core]/es_extended/server/modules/onesync.lua | 56 +++++++++++++------ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 8c35eae6..8a577e7e 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -530,10 +530,14 @@ end ---@param vehicleModel integer | string The vehicle to spawn ---@param coords table | vector3 The coords to spawn the vehicle at ---@param heading number The heading of the vehicle ----@param cb? function The callback function +---@param cb? fun(vehicle: number) The callback function ---@param networked? boolean Whether the vehicle should be networked ----@return nil +---@return number? vehicle function ESX.Game.SpawnVehicle(vehicleModel, coords, heading, cb, networked) + if cb and not ESX.IsFunctionReference(cb) then + error("Invalid callback function") + end + local model = type(vehicleModel) == "number" and vehicleModel or joaat(vehicleModel) local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z) local isNetworked = networked == nil or networked @@ -549,8 +553,15 @@ function ESX.Game.SpawnVehicle(vehicleModel, coords, heading, cb, networked) return error(("Resource ^5%s^1 Tried to spawn vehicle on the client but the position is too far away (Out of onesync range)."):format(executingResource)) end + local promise = not cb and promise.new() CreateThread(function() - ESX.Streaming.RequestModel(model) + local modelHash = ESX.Streaming.RequestModel(model) + if not modelHash then + if promise then + return promise:reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) + end + error(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) + end local vehicle = CreateVehicle(model, vector.x, vector.y, vector.z, heading, isNetworked, true) @@ -569,10 +580,16 @@ function ESX.Game.SpawnVehicle(vehicleModel, coords, heading, cb, networked) Wait(0) end - if cb then + if promise then + promise:resolve(vehicle) + elseif cb then cb(vehicle) end end) + + if promise then + return Citizen.Await(promise) + end end ---@param vehicle integer The vehicle to spawn diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index 87c5f7f0..cb914a17 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -82,35 +82,57 @@ end ---@param coords vector3|table ---@param heading number ---@param properties table ----@param cb function +---@param cb? fun(netId: number) +---@return number? netId function ESX.OneSync.SpawnVehicle(model, coords, heading, properties, cb) + if cb and not ESX.IsFunctionReference(cb) then + error("Invalid callback function") + end + + local vehicleModel = joaat(model) local vehicleProperties = properties + local promise = not cb and promise.new() CreateThread(function() local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) ESX.GetVehicleType(vehicleModel, xPlayer.id, function(vehicleType) - if vehicleType then - local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) - local tries = 0 - - while not createdVehicle or createdVehicle == 0 or not GetEntityCoords(createdVehicle) do - Wait(200) - tries = tries + 1 - if tries > 20 then - return error(("Could not spawn vehicle - ^5%s^7!"):format(model)) - end + if not vehicleType then + if (promise) then + return promise:reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) end - -- luacheck: ignore - SetEntityOrphanMode(createdVehicle, 2) - local networkId = NetworkGetNetworkIdFromEntity(createdVehicle) - Entity(createdVehicle).state:set("VehicleProperties", vehicleProperties, true) - cb(networkId) - else error(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) end + + local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) + local tries = 0 + + while not createdVehicle or createdVehicle == 0 or not GetEntityCoords(createdVehicle) do + Wait(200) + tries = tries + 1 + if tries > 20 then + if promise then + return promise:reject(("Could not spawn vehicle - ^5%s^7!"):format(model)) + end + error(("Could not spawn vehicle - ^5%s^7!"):format(model)) + end + end + + -- luacheck: ignore + SetEntityOrphanMode(createdVehicle, 2) + local networkId = NetworkGetNetworkIdFromEntity(createdVehicle) + Entity(createdVehicle).state:set("VehicleProperties", vehicleProperties, true) + if promise then + promise:resolve(networkId) + elseif cb then + cb(networkId) + end end) end) + + if promise then + return Citizen.Await(promise) + end end ---@param model number|string From 8ea8b4a69f8347b974560e7c6b98c52eecff29c7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:02:19 +0100 Subject: [PATCH 002/132] feat(es_extended/server/classes/vehicle): add vehicle class --- [core]/es_extended/fxmanifest.lua | 1 + [core]/es_extended/server/classes/vehicle.lua | 209 ++++++++++++++++++ [core]/es_extended/server/common.lua | 2 + 3 files changed, 212 insertions(+) create mode 100644 [core]/es_extended/server/classes/vehicle.lua diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index 848289bf..ffc8aa8c 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -25,6 +25,7 @@ server_scripts { 'server/common.lua', 'server/modules/callback.lua', 'server/classes/player.lua', + 'server/classes/vehicle.lua', 'server/classes/overrides/*.lua', 'server/functions.lua', 'server/modules/onesync.lua', diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua new file mode 100644 index 00000000..2f5d87f3 --- /dev/null +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -0,0 +1,209 @@ +---@class DbVehicle +---@field owner string +---@field plate string +---@field vehicle string +---@field type string +---@field job? string +---@field stored boolean +---@field parking? string +---@field pound? string + +---@class VehicleData +---@field plate string +---@field netId number +---@field entity number +---@field modelHash number +---@field owner string + +---@class VehicleClass +---@field plate string +---@field isValid fun(self:VehicleClass):boolean +---@field new fun(owner:string, plate:string, coords:vector4): VehicleClass? +---@field getPlate fun(self:VehicleClass):string? +---@field getNetId fun(self:VehicleClass):number? +---@field getEntity fun(self:VehicleClass):number? +---@field getModelHash fun(self:VehicleClass):number? +---@field getProps fun(self:VehicleClass):table? +---@field getOwner fun(self:VehicleClass):string? +---@field setPlate fun(self:VehicleClass, plate:string):boolean +---@field setProps fun(self:VehicleClass, props:table):boolean +---@field setOwner fun(self:VehicleClass, owner:string):boolean +---@field delete fun(self:VehicleClass):nil +Core.vehicleClass = { + plate = "", + new = function(owner, plate, coords) + assert(type(owner) == "string", "Expected 'owner' to be a string") + assert(type(plate) == "string", "Expected 'plate' to be a string") + assert(type(coords) == "vector4", "Expected 'coords' to be a vector4") + + if Core.vehicles[plate] then + local obj = table.clone(Core.vehicleClass) + obj.plate = plate + + if obj:isValid() then + return obj + end + end + + local dbVehicle = MySQL.single.await("SELECT * FROM `owned_vehicles` WHERE `stored` = true AND `owner` = ? AND `plate` = ?", { owner, plate }) --[[@as DbVehicle]] + if not dbVehicle then + return + end + + local vehicleProps = json.decode(dbVehicle.vehicle) + if type(vehicleProps.model) ~= "number" then + model = joaat(model) + end + + local netId = ESX.OneSync.SpawnVehicle(model, coords.xyz, coords.w, vehicleProps) + if not netId then + return + end + + local entity = NetworkGetEntityFromNetworkId(netId) + if entity <= 0 then + return + end + + Entity(entity).state:set("owner", owner, false) + + local vehicle = { + plate = plate, + entity = entity, + netId = netId, + modelHash = model, + owner = owner, + } + Core.vehicles[plate] = vehicle + + MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 0 WHERE `owner` = ? AND `plate` = ?", { owner, plate }) + + local obj = table.clone(Core.vehicleClass) + obj.plate = plate + TriggerEvent("esx:createdExtendedVehicle", obj) + + return obj + end, + isValid = function(self) + local xVehicle = Core.vehicles[self.plate] + if not xVehicle then + return false + end + + local entity = NetworkGetEntityFromNetworkId(xVehicle.netId) + if entity <= 0 or Entity(entity).state.owner ~= xVehicle.owner then + self:delete() + return false + end + + local plate = ESX.Math.Trim(GetVehicleNumberPlateText(entity)) + if plate ~= xVehicle.plate then + self:delete() + return false + end + + xVehicle.entity = entity + + return true + end, + getNetId = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].netId + end, + getEntity = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].entity + end, + getPlate = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].plate + end, + getModelHash = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].modelHash + end, + getProps = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].props + end, + getOwner = function(self) + if not self:isValid() then + return + end + + return Core.vehicles[self.plate].owner + end, + setPlate = function(self, plate) + if not self:isValid() then + return false + end + assert(type(plate) == "string", "Expected 'plate' to be a string") + + local xVehicle = Core.vehicles[self.plate] + SetVehicleNumberPlateText(xVehicle.entity, plate) + xVehicle.plate = plate + MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) + + return true + end, + setProps = function(self, props) + if not self:isValid() then + return false + end + assert(type(props) == "table", "Expected 'props' to be a table") + + local xVehicle = Core.vehicles[self.plate] + Entity(xVehicle.entity).state:set("VehicleProperties", props, true) + MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(props), xVehicle.plate, xVehicle.owner) + + return true + end, + setOwner = function(self, owner) + if not self:isValid() then + return false + end + assert(type(owner) == "string", "Expected 'owner' to be a string") + + local xVehicle = Core.vehicles[self.plate] + if xVehicle.owner == owner then + return true + end + + Entity(xVehicle.entity).state:set("owner", owner, false) + xVehicle.owner = owner + MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE `plate` = ?", { owner, xVehicle.plate }) + + return true + end, + delete = function(self) + local xVehicle = Core.vehicles[self.plate] + if not xVehicle then + return + end + + local entity = NetworkGetEntityFromNetworkId(xVehicle.netId) + if entity >= 0 and Entity(entity).state.owner == xVehicle.owner then + DeleteEntity(xVehicle.entity) + end + + MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 1 WHERE `plate` = ?", { xVehicle.plate }) + TriggerEvent("esx:deletedExtendedVehicle", self) + + Core.vehicles[self.plate] = nil + end, +} diff --git a/[core]/es_extended/server/common.lua b/[core]/es_extended/server/common.lua index edbc3952..2e6845c4 100644 --- a/[core]/es_extended/server/common.lua +++ b/[core]/es_extended/server/common.lua @@ -11,6 +11,8 @@ Core.PlayerFunctionOverrides = {} Core.DatabaseConnected = false Core.playersByIdentifier = {} +---@type table +Core.vehicles = {} Core.vehicleTypesByModel = {} RegisterNetEvent("esx:onPlayerSpawn", function() From 3d16c4fecdeb811e6ebab9846cbadb406c3a4543 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:02:45 +0100 Subject: [PATCH 003/132] feat(es_extended/server/functions): add exposed vehicle class functions --- [core]/es_extended/server/functions.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 884ef4a5..4d61617f 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -615,3 +615,23 @@ function Core.IsPlayerAdmin(playerId) local xPlayer = ESX.Players[playerId] return (xPlayer and Config.AdminGroups[xPlayer.group] and true) or false end + +---@param owner string +---@param plate string +---@param coords vector4 +---@return VehicleClass? +function ESX.CreateExtendedVehicle(owner, plate, coords) + return Core.vehicleClass.new(owner, plate, coords) +end + +---@param plate string +---@return VehicleClass? +function ESX.GetExtendedVehicleFromPlate(plate) + assert(type(plate) == "string", "Expected 'plate' to be a string") + + if Core.vehicles[plate] then + local obj = table.clone(Core.vehicleClass) + obj.plate = plate + return obj + end +end \ No newline at end of file From 5c44b5c8551c578e3da08c3da5350219ebcded4e Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:25:38 +0100 Subject: [PATCH 004/132] refactor(es_extended/server/functions): move ESX.GetExtendedVehicleFromPlate logic into vehicle class --- [core]/es_extended/server/classes/vehicle.lua | 21 ++++++++++++------- [core]/es_extended/server/functions.lua | 6 +----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 2f5d87f3..469cedb6 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -19,6 +19,7 @@ ---@field plate string ---@field isValid fun(self:VehicleClass):boolean ---@field new fun(owner:string, plate:string, coords:vector4): VehicleClass? +---@field getFromPlate fun(plate:string):VehicleClass? ---@field getPlate fun(self:VehicleClass):string? ---@field getNetId fun(self:VehicleClass):number? ---@field getEntity fun(self:VehicleClass):number? @@ -36,13 +37,9 @@ Core.vehicleClass = { assert(type(plate) == "string", "Expected 'plate' to be a string") assert(type(coords) == "vector4", "Expected 'coords' to be a vector4") - if Core.vehicles[plate] then - local obj = table.clone(Core.vehicleClass) - obj.plate = plate - - if obj:isValid() then - return obj - end + local xVehicle = Core.vehicleClass.getFromPlate(plate) + if xVehicle then + return xVehicle end local dbVehicle = MySQL.single.await("SELECT * FROM `owned_vehicles` WHERE `stored` = true AND `owner` = ? AND `plate` = ?", { owner, plate }) --[[@as DbVehicle]] @@ -84,6 +81,16 @@ Core.vehicleClass = { return obj end, + getFromPlate = function(plate) + if Core.vehicles[plate] then + local obj = table.clone(Core.vehicleClass) + obj.plate = plate + + if obj:isValid() then + return obj + end + end + end, isValid = function(self) local xVehicle = Core.vehicles[self.plate] if not xVehicle then diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 4d61617f..90c5bc4f 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -629,9 +629,5 @@ end function ESX.GetExtendedVehicleFromPlate(plate) assert(type(plate) == "string", "Expected 'plate' to be a string") - if Core.vehicles[plate] then - local obj = table.clone(Core.vehicleClass) - obj.plate = plate - return obj - end + return Core.vehicleClass.getFromPlate(plate) end \ No newline at end of file From 520446a6acfeec1635a2784c0017e5390cc33c48 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:33:30 +0100 Subject: [PATCH 005/132] refactor(es_extended/server/classes/vehicle): only select relevant data from db --- [core]/es_extended/server/classes/vehicle.lua | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 469cedb6..e23084cc 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -1,13 +1,3 @@ ----@class DbVehicle ----@field owner string ----@field plate string ----@field vehicle string ----@field type string ----@field job? string ----@field stored boolean ----@field parking? string ----@field pound? string - ---@class VehicleData ---@field plate string ---@field netId number @@ -42,12 +32,12 @@ Core.vehicleClass = { return xVehicle end - local dbVehicle = MySQL.single.await("SELECT * FROM `owned_vehicles` WHERE `stored` = true AND `owner` = ? AND `plate` = ?", { owner, plate }) --[[@as DbVehicle]] - if not dbVehicle then + local vehicleProps = MySQL.scalar.await("SELECT `vehicle` FROM `owned_vehicles` WHERE `stored` = true AND `owner` = ? AND `plate` = ? LIMIT 1", { owner, plate }) + if not vehicleProps then return end + vehicleProps = json.decode(vehicleProps.vehicle) - local vehicleProps = json.decode(dbVehicle.vehicle) if type(vehicleProps.model) ~= "number" then model = joaat(model) end From 68508b96f0cf83c5fdb9bfbf156f1deb70fd3d65 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:38:15 +0100 Subject: [PATCH 006/132] fix(es_extended/server/classes/vehicle): remove unused function getProps --- [core]/es_extended/server/classes/vehicle.lua | 9 --------- 1 file changed, 9 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index e23084cc..c71723bd 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -14,7 +14,6 @@ ---@field getNetId fun(self:VehicleClass):number? ---@field getEntity fun(self:VehicleClass):number? ---@field getModelHash fun(self:VehicleClass):number? ----@field getProps fun(self:VehicleClass):table? ---@field getOwner fun(self:VehicleClass):string? ---@field setPlate fun(self:VehicleClass, plate:string):boolean ---@field setProps fun(self:VehicleClass, props:table):boolean @@ -51,7 +50,6 @@ Core.vehicleClass = { if entity <= 0 then return end - Entity(entity).state:set("owner", owner, false) local vehicle = { @@ -131,13 +129,6 @@ Core.vehicleClass = { return Core.vehicles[self.plate].modelHash end, - getProps = function(self) - if not self:isValid() then - return - end - - return Core.vehicles[self.plate].props - end, getOwner = function(self) if not self:isValid() then return From 04ef04e2078f425f9a8000e1a3c15e1f5253be4c Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 01:20:28 +0100 Subject: [PATCH 007/132] fix(es_extended/server/classes/vehicle): properly parse vehicleProps --- [core]/es_extended/server/classes/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index c71723bd..b4f0240d 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -35,7 +35,7 @@ Core.vehicleClass = { if not vehicleProps then return end - vehicleProps = json.decode(vehicleProps.vehicle) + vehicleProps = json.decode(vehicleProps) if type(vehicleProps.model) ~= "number" then model = joaat(model) From 4e6e37e7089658c63d425cff81acfb9b447917e6 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 01:25:18 +0100 Subject: [PATCH 008/132] fix(es_extended/server/classes/vehicle): fix vehicle model extraction --- [core]/es_extended/server/classes/vehicle.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index b4f0240d..edc1c240 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -38,10 +38,10 @@ Core.vehicleClass = { vehicleProps = json.decode(vehicleProps) if type(vehicleProps.model) ~= "number" then - model = joaat(model) + vehicleProps.model = joaat(vehicleProps.model) end - local netId = ESX.OneSync.SpawnVehicle(model, coords.xyz, coords.w, vehicleProps) + local netId = ESX.OneSync.SpawnVehicle(vehicleProps.model, coords.xyz, coords.w, vehicleProps) if not netId then return end @@ -56,7 +56,7 @@ Core.vehicleClass = { plate = plate, entity = entity, netId = netId, - modelHash = model, + modelHash = vehicleProps.model, owner = owner, } Core.vehicles[plate] = vehicle From 9d7c2edac040666b62af5ed9c1d67b0b9ef3d973 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:00:46 +0100 Subject: [PATCH 009/132] Merge pull request #1553 from Mycroft-Studios/better-vehicle-spawning fix(es_extended/server/onesync): better vehicle spawning method --- [core]/es_extended/server/modules/onesync.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index cb914a17..de4f4eb8 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -107,10 +107,10 @@ function ESX.OneSync.SpawnVehicle(model, coords, heading, properties, cb) local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) local tries = 0 - while not createdVehicle or createdVehicle == 0 or not GetEntityCoords(createdVehicle) do + while not createdVehicle or createdVehicle == 0 or NetworkGetEntityOwner(createdVehicle) == -1 do Wait(200) tries = tries + 1 - if tries > 20 then + if tries > 40 then if promise then return promise:reject(("Could not spawn vehicle - ^5%s^7!"):format(model)) end @@ -122,6 +122,7 @@ function ESX.OneSync.SpawnVehicle(model, coords, heading, properties, cb) SetEntityOrphanMode(createdVehicle, 2) local networkId = NetworkGetNetworkIdFromEntity(createdVehicle) Entity(createdVehicle).state:set("VehicleProperties", vehicleProperties, true) + if promise then promise:resolve(networkId) elseif cb then From 7341b37850f5756369f05b5df4ce4d6aa3435b77 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 02:01:01 +0100 Subject: [PATCH 010/132] fix(es_extended/server/classes/vehicle): fix plate race condition It takes a while until the vehicle plate is actually set. To combat this, we will store the plate in the entity statebag instead. --- [core]/es_extended/server/classes/vehicle.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index edc1c240..cbc9daa6 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -51,6 +51,7 @@ Core.vehicleClass = { return end Entity(entity).state:set("owner", owner, false) + Entity(entity).state:set("plate", plate, false) local vehicle = { plate = plate, @@ -86,13 +87,7 @@ Core.vehicleClass = { end local entity = NetworkGetEntityFromNetworkId(xVehicle.netId) - if entity <= 0 or Entity(entity).state.owner ~= xVehicle.owner then - self:delete() - return false - end - - local plate = ESX.Math.Trim(GetVehicleNumberPlateText(entity)) - if plate ~= xVehicle.plate then + if entity <= 0 or Entity(entity).state.owner ~= xVehicle.owner or Entity(entity).state.plate ~= xVehicle.plate then self:delete() return false end @@ -143,6 +138,7 @@ Core.vehicleClass = { assert(type(plate) == "string", "Expected 'plate' to be a string") local xVehicle = Core.vehicles[self.plate] + Entity(xVehicle.entity).state:set("plate", plate, false) SetVehicleNumberPlateText(xVehicle.entity, plate) xVehicle.plate = plate MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) From 2babbdda687690aeb9922958218f3410ab61cefc Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 02:15:39 +0100 Subject: [PATCH 011/132] fix(es_extended/server/classes/vehicle): fix plate not updating in db --- [core]/es_extended/server/classes/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index cbc9daa6..68ee4f28 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -140,8 +140,8 @@ Core.vehicleClass = { local xVehicle = Core.vehicles[self.plate] Entity(xVehicle.entity).state:set("plate", plate, false) SetVehicleNumberPlateText(xVehicle.entity, plate) - xVehicle.plate = plate MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) + xVehicle.plate = plate return true end, From 734c5ec3d558669b3baa5d0d2f7cb46ca062499b Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 02:21:17 +0100 Subject: [PATCH 012/132] fix(es_extended/server/classes/vehicle): confirm updates to db before reflecting changes in veh obj --- [core]/es_extended/server/classes/vehicle.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 68ee4f28..f12c2366 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -138,9 +138,12 @@ Core.vehicleClass = { assert(type(plate) == "string", "Expected 'plate' to be a string") local xVehicle = Core.vehicles[self.plate] + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) + if affectedRows <= 0 then + return false + end Entity(xVehicle.entity).state:set("plate", plate, false) SetVehicleNumberPlateText(xVehicle.entity, plate) - MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) xVehicle.plate = plate return true @@ -152,8 +155,11 @@ Core.vehicleClass = { assert(type(props) == "table", "Expected 'props' to be a table") local xVehicle = Core.vehicles[self.plate] + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(props), xVehicle.plate, xVehicle.owner) + if affectedRows <= 0 then + return false + end Entity(xVehicle.entity).state:set("VehicleProperties", props, true) - MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(props), xVehicle.plate, xVehicle.owner) return true end, @@ -168,9 +174,12 @@ Core.vehicleClass = { return true end + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { owner, xVehicle.owner, xVehicle.plate }) + if affectedRows <= 0 then + return false + end Entity(xVehicle.entity).state:set("owner", owner, false) xVehicle.owner = owner - MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE `plate` = ?", { owner, xVehicle.plate }) return true end, From 88ad28775fef06ba26a0e12d8be5ee2d0347a5dd Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 02:25:53 +0100 Subject: [PATCH 013/132] refactor(es_extended/server/classes/vehicle): better variable name & added spacing --- [core]/es_extended/server/classes/vehicle.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index f12c2366..486c096e 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -53,14 +53,14 @@ Core.vehicleClass = { Entity(entity).state:set("owner", owner, false) Entity(entity).state:set("plate", plate, false) - local vehicle = { + local vehicleData = { plate = plate, entity = entity, netId = netId, modelHash = vehicleProps.model, owner = owner, } - Core.vehicles[plate] = vehicle + Core.vehicles[plate] = vehicleData MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 0 WHERE `owner` = ? AND `plate` = ?", { owner, plate }) @@ -142,6 +142,7 @@ Core.vehicleClass = { if affectedRows <= 0 then return false end + Entity(xVehicle.entity).state:set("plate", plate, false) SetVehicleNumberPlateText(xVehicle.entity, plate) xVehicle.plate = plate @@ -159,6 +160,7 @@ Core.vehicleClass = { if affectedRows <= 0 then return false end + Entity(xVehicle.entity).state:set("VehicleProperties", props, true) return true @@ -178,6 +180,7 @@ Core.vehicleClass = { if affectedRows <= 0 then return false end + Entity(xVehicle.entity).state:set("owner", owner, false) xVehicle.owner = owner From 5656505e006bd2529ae28f51d2326b1c7c7ea6bb Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 12:48:39 +0100 Subject: [PATCH 014/132] refactor(es_extended/server/functions): move GetExtendedVehicleFromPlate plate validation to vehicle class --- [core]/es_extended/server/classes/vehicle.lua | 2 ++ [core]/es_extended/server/functions.lua | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 486c096e..3cd8562d 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -71,6 +71,8 @@ Core.vehicleClass = { return obj end, getFromPlate = function(plate) + assert(type(plate) == "string", "Expected 'plate' to be a string") + if Core.vehicles[plate] then local obj = table.clone(Core.vehicleClass) obj.plate = plate diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 90c5bc4f..f4894ff7 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -627,7 +627,5 @@ end ---@param plate string ---@return VehicleClass? function ESX.GetExtendedVehicleFromPlate(plate) - assert(type(plate) == "string", "Expected 'plate' to be a string") - return Core.vehicleClass.getFromPlate(plate) end \ No newline at end of file From ea668afb3ab94ea7d091f393f786b259274d6bb7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:15:38 +0100 Subject: [PATCH 015/132] fix(es_extended/server/classes/vehicle): properly object to new vehicle plate --- [core]/es_extended/server/classes/vehicle.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 3cd8562d..22147663 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -147,7 +147,14 @@ Core.vehicleClass = { Entity(xVehicle.entity).state:set("plate", plate, false) SetVehicleNumberPlateText(xVehicle.entity, plate) + + local oldPlate = xVehicle.plate xVehicle.plate = plate + Core.vehicles[plate] = table.clone(xVehicle) + Core.vehicles[self.plate] = nil + + TriggerEvent("esx:changedExtendedVehiclePlate", xVehicle.plate, oldPlate) + Wait(0) return true end, From 203dc9af0252dbd961f059c72edb9a6256458e7a Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:33:56 +0100 Subject: [PATCH 016/132] fix(es_extended/server/classes/vehicle): invalidate vehicle on failed db update --- [core]/es_extended/server/classes/vehicle.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 22147663..fea762d3 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -142,6 +142,7 @@ Core.vehicleClass = { local xVehicle = Core.vehicles[self.plate] local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) if affectedRows <= 0 then + self:delete() return false end @@ -167,6 +168,7 @@ Core.vehicleClass = { local xVehicle = Core.vehicles[self.plate] local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(props), xVehicle.plate, xVehicle.owner) if affectedRows <= 0 then + self:delete() return false end @@ -187,6 +189,7 @@ Core.vehicleClass = { local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { owner, xVehicle.owner, xVehicle.plate }) if affectedRows <= 0 then + self:delete() return false end From 0fe26d2455fbb897185e01763275ddae54ba76e9 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:35:41 +0100 Subject: [PATCH 017/132] fix(es_extended/server/classes/vehicle): add owner check in db query --- [core]/es_extended/server/classes/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index fea762d3..6c676971 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -209,7 +209,7 @@ Core.vehicleClass = { DeleteEntity(xVehicle.entity) end - MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 1 WHERE `plate` = ?", { xVehicle.plate }) + MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 1 WHERE `plate` = ? AND `owner` = ?", { xVehicle.plate, xVehicle.owner }) TriggerEvent("esx:deletedExtendedVehicle", self) Core.vehicles[self.plate] = nil From efc91176401739d7ce3ac9f9c16137a74a5e60c2 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:38:06 +0100 Subject: [PATCH 018/132] feat(es_extended/server/classes/vehicle): add support for setting garage/impound on delete --- [core]/es_extended/server/classes/vehicle.lua | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 6c676971..7222bf9f 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -18,7 +18,7 @@ ---@field setPlate fun(self:VehicleClass, plate:string):boolean ---@field setProps fun(self:VehicleClass, props:table):boolean ---@field setOwner fun(self:VehicleClass, owner:string):boolean ----@field delete fun(self:VehicleClass):nil +---@field delete fun(self:VehicleClass, garageName:string?, impound:boolean?):nil Core.vehicleClass = { plate = "", new = function(owner, plate, coords) @@ -198,7 +198,14 @@ Core.vehicleClass = { return true end, - delete = function(self) + delete = function(self, garageName, impound) + if type(garageName) ~= "string" then + garageName = nil + end + if type(impound) ~= "boolean" then + impound = false + end + local xVehicle = Core.vehicles[self.plate] if not xVehicle then return @@ -209,7 +216,19 @@ Core.vehicleClass = { DeleteEntity(xVehicle.entity) end - MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 1 WHERE `plate` = ? AND `owner` = ?", { xVehicle.plate, xVehicle.owner }) + local query = "UPDATE `owned_vehicles` SET `stored` = true WHERE `plate` = ? AND `owner` = ?" + local queryParams = { xVehicle.plate, xVehicle.owner } + if garageName then + if impound then + query = "UPDATE `owned_vehicles` SET `stored` = true, `parking` = NULL, `pound` = ? WHERE `plate` = ? AND `owner` = ?" + else + query = "UPDATE `owned_vehicles` SET `stored` = true, `pound` = NULL, `parking` = ? WHERE `plate` = ? AND `owner` = ?" + end + + queryParams = { garageName, xVehicle.plate, xVehicle.owner } + end + + MySQL.update.await(query, queryParams) TriggerEvent("esx:deletedExtendedVehicle", self) Core.vehicles[self.plate] = nil From 9c7d7ea26c7a756bd70ec499c466789a2de1384d Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:46:36 +0100 Subject: [PATCH 019/132] refactor(es_extended/server/classes/vehicle): better variable naming --- [core]/es_extended/server/classes/vehicle.lua | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 7222bf9f..f900d4d8 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -15,10 +15,10 @@ ---@field getEntity fun(self:VehicleClass):number? ---@field getModelHash fun(self:VehicleClass):number? ---@field getOwner fun(self:VehicleClass):string? ----@field setPlate fun(self:VehicleClass, plate:string):boolean +---@field setPlate fun(self:VehicleClass, newPlate:string):boolean ---@field setProps fun(self:VehicleClass, props:table):boolean ----@field setOwner fun(self:VehicleClass, owner:string):boolean ----@field delete fun(self:VehicleClass, garageName:string?, impound:boolean?):nil +---@field setOwner fun(self:VehicleClass, newOwner:string):boolean +---@field delete fun(self:VehicleClass, garageName:string?, isImpound:boolean?):nil Core.vehicleClass = { plate = "", new = function(owner, plate, coords) @@ -133,26 +133,26 @@ Core.vehicleClass = { return Core.vehicles[self.plate].owner end, - setPlate = function(self, plate) + setPlate = function(self, newPlate) if not self:isValid() then return false end - assert(type(plate) == "string", "Expected 'plate' to be a string") + assert(type(newPlate) == "string", "Expected 'plate' to be a string") local xVehicle = Core.vehicles[self.plate] - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { plate, xVehicle.plate, xVehicle.owner }) + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { newPlate, xVehicle.plate, xVehicle.owner }) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("plate", plate, false) - SetVehicleNumberPlateText(xVehicle.entity, plate) + Entity(xVehicle.entity).state:set("plate", newPlate, false) + SetVehicleNumberPlateText(xVehicle.entity, newPlate) local oldPlate = xVehicle.plate - xVehicle.plate = plate - Core.vehicles[plate] = table.clone(xVehicle) - Core.vehicles[self.plate] = nil + xVehicle.plate = newPlate + Core.vehicles[newPlate] = table.clone(xVehicle) + Core.vehicles[oldPlate] = nil TriggerEvent("esx:changedExtendedVehiclePlate", xVehicle.plate, oldPlate) Wait(0) @@ -176,34 +176,34 @@ Core.vehicleClass = { return true end, - setOwner = function(self, owner) + setOwner = function(self, newOwner) if not self:isValid() then return false end - assert(type(owner) == "string", "Expected 'owner' to be a string") + assert(type(newOwner) == "string", "Expected 'owner' to be a string") local xVehicle = Core.vehicles[self.plate] - if xVehicle.owner == owner then + if xVehicle.owner == newOwner then return true end - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { owner, xVehicle.owner, xVehicle.plate }) + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { newOwner, xVehicle.owner, xVehicle.plate }) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("owner", owner, false) - xVehicle.owner = owner + Entity(xVehicle.entity).state:set("owner", newOwner, false) + xVehicle.owner = newOwner return true end, - delete = function(self, garageName, impound) + delete = function(self, garageName, isImpound) if type(garageName) ~= "string" then garageName = nil end - if type(impound) ~= "boolean" then - impound = false + if type(isImpound) ~= "boolean" then + isImpound = false end local xVehicle = Core.vehicles[self.plate] @@ -219,7 +219,7 @@ Core.vehicleClass = { local query = "UPDATE `owned_vehicles` SET `stored` = true WHERE `plate` = ? AND `owner` = ?" local queryParams = { xVehicle.plate, xVehicle.owner } if garageName then - if impound then + if isImpound then query = "UPDATE `owned_vehicles` SET `stored` = true, `parking` = NULL, `pound` = ? WHERE `plate` = ? AND `owner` = ?" else query = "UPDATE `owned_vehicles` SET `stored` = true, `pound` = NULL, `parking` = ? WHERE `plate` = ? AND `owner` = ?" From ac7aa3f0b18669b95332b241c37f4f1619c5bbaa Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 22 Dec 2024 15:01:02 +0100 Subject: [PATCH 020/132] refactor(es_extended/server/classes/vehicle): rename classes --- [core]/es_extended/server/classes/vehicle.lua | 36 +++++++++---------- [core]/es_extended/server/common.lua | 2 +- [core]/es_extended/server/functions.lua | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index f900d4d8..025b86da 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -1,24 +1,24 @@ ----@class VehicleData +---@class CVehicleData ---@field plate string ---@field netId number ---@field entity number ---@field modelHash number ---@field owner string ----@class VehicleClass +---@class CExtendedVehicle ---@field plate string ----@field isValid fun(self:VehicleClass):boolean ----@field new fun(owner:string, plate:string, coords:vector4): VehicleClass? ----@field getFromPlate fun(plate:string):VehicleClass? ----@field getPlate fun(self:VehicleClass):string? ----@field getNetId fun(self:VehicleClass):number? ----@field getEntity fun(self:VehicleClass):number? ----@field getModelHash fun(self:VehicleClass):number? ----@field getOwner fun(self:VehicleClass):string? ----@field setPlate fun(self:VehicleClass, newPlate:string):boolean ----@field setProps fun(self:VehicleClass, props:table):boolean ----@field setOwner fun(self:VehicleClass, newOwner:string):boolean ----@field delete fun(self:VehicleClass, garageName:string?, isImpound:boolean?):nil +---@field isValid fun(self:CExtendedVehicle):boolean +---@field new fun(owner:string, plate:string, coords:vector4): CExtendedVehicle? +---@field getFromPlate fun(plate:string):CExtendedVehicle? +---@field getPlate fun(self:CExtendedVehicle):string? +---@field getNetId fun(self:CExtendedVehicle):number? +---@field getEntity fun(self:CExtendedVehicle):number? +---@field getModelHash fun(self:CExtendedVehicle):number? +---@field getOwner fun(self:CExtendedVehicle):string? +---@field setPlate fun(self:CExtendedVehicle, newPlate:string):boolean +---@field setProps fun(self:CExtendedVehicle, newProps:table):boolean +---@field setOwner fun(self:CExtendedVehicle, newOwner:string):boolean +---@field delete fun(self:CExtendedVehicle, garageName:string?, isImpound:boolean?):nil Core.vehicleClass = { plate = "", new = function(owner, plate, coords) @@ -159,20 +159,20 @@ Core.vehicleClass = { return true end, - setProps = function(self, props) + setProps = function(self, newProps) if not self:isValid() then return false end - assert(type(props) == "table", "Expected 'props' to be a table") + assert(type(newProps) == "table", "Expected 'props' to be a table") local xVehicle = Core.vehicles[self.plate] - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(props), xVehicle.plate, xVehicle.owner) + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(newProps), xVehicle.plate, xVehicle.owner) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("VehicleProperties", props, true) + Entity(xVehicle.entity).state:set("VehicleProperties", newProps, true) return true end, diff --git a/[core]/es_extended/server/common.lua b/[core]/es_extended/server/common.lua index 2e6845c4..9c95c571 100644 --- a/[core]/es_extended/server/common.lua +++ b/[core]/es_extended/server/common.lua @@ -11,7 +11,7 @@ Core.PlayerFunctionOverrides = {} Core.DatabaseConnected = false Core.playersByIdentifier = {} ----@type table +---@type table Core.vehicles = {} Core.vehicleTypesByModel = {} diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index f4894ff7..ae052f72 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -619,13 +619,13 @@ end ---@param owner string ---@param plate string ---@param coords vector4 ----@return VehicleClass? +---@return CExtendedVehicle? function ESX.CreateExtendedVehicle(owner, plate, coords) return Core.vehicleClass.new(owner, plate, coords) end ---@param plate string ----@return VehicleClass? +---@return CExtendedVehicle? function ESX.GetExtendedVehicleFromPlate(plate) return Core.vehicleClass.getFromPlate(plate) end \ No newline at end of file From 9635d4774782bff0688f46cf3481fb3846cb37d9 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 22 Dec 2024 15:13:39 +0100 Subject: [PATCH 021/132] fix(es_extended/server/classes/vehicle): properly annotate vehicleData type --- [core]/es_extended/server/classes/vehicle.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 025b86da..1d498cd7 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -53,6 +53,7 @@ Core.vehicleClass = { Entity(entity).state:set("owner", owner, false) Entity(entity).state:set("plate", plate, false) + ---@type CVehicleData local vehicleData = { plate = plate, entity = entity, From 738e0ea0fe59d378c42ddad0690b26901d990456 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 22 Dec 2024 15:17:41 +0100 Subject: [PATCH 022/132] refactor(es_extended/server/classes/vehicle): rename xVehicle to vehicleData where appropriate Avoids confusion. --- [core]/es_extended/server/classes/vehicle.lua | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index 1d498cd7..ac9feca8 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -84,18 +84,18 @@ Core.vehicleClass = { end end, isValid = function(self) - local xVehicle = Core.vehicles[self.plate] - if not xVehicle then + local vehicleData = Core.vehicles[self.plate] + if not vehicleData then return false end - local entity = NetworkGetEntityFromNetworkId(xVehicle.netId) - if entity <= 0 or Entity(entity).state.owner ~= xVehicle.owner or Entity(entity).state.plate ~= xVehicle.plate then + local entity = NetworkGetEntityFromNetworkId(vehicleData.netId) + if entity <= 0 or Entity(entity).state.owner ~= vehicleData.owner or Entity(entity).state.plate ~= vehicleData.plate then self:delete() return false end - xVehicle.entity = entity + vehicleData.entity = entity return true end, @@ -140,22 +140,22 @@ Core.vehicleClass = { end assert(type(newPlate) == "string", "Expected 'plate' to be a string") - local xVehicle = Core.vehicles[self.plate] - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { newPlate, xVehicle.plate, xVehicle.owner }) + local vehicleData = Core.vehicles[self.plate] + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `plate` = ? WHERE `plate` = ? AND `owner` = ?", { newPlate, vehicleData.plate, vehicleData.owner }) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("plate", newPlate, false) - SetVehicleNumberPlateText(xVehicle.entity, newPlate) + Entity(vehicleData.entity).state:set("plate", newPlate, false) + SetVehicleNumberPlateText(vehicleData.entity, newPlate) - local oldPlate = xVehicle.plate - xVehicle.plate = newPlate - Core.vehicles[newPlate] = table.clone(xVehicle) + local oldPlate = vehicleData.plate + vehicleData.plate = newPlate + Core.vehicles[newPlate] = table.clone(vehicleData) Core.vehicles[oldPlate] = nil - TriggerEvent("esx:changedExtendedVehiclePlate", xVehicle.plate, oldPlate) + TriggerEvent("esx:changedExtendedVehiclePlate", vehicleData.plate, oldPlate) Wait(0) return true @@ -166,14 +166,14 @@ Core.vehicleClass = { end assert(type(newProps) == "table", "Expected 'props' to be a table") - local xVehicle = Core.vehicles[self.plate] - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(newProps), xVehicle.plate, xVehicle.owner) + local vehicleData = Core.vehicles[self.plate] + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `vehicle` = ? WHERE `plate` = ? AND `owner` = ?", json.encode(newProps), vehicleData.plate, vehicleData.owner) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("VehicleProperties", newProps, true) + Entity(vehicleData.entity).state:set("VehicleProperties", newProps, true) return true end, @@ -183,19 +183,19 @@ Core.vehicleClass = { end assert(type(newOwner) == "string", "Expected 'owner' to be a string") - local xVehicle = Core.vehicles[self.plate] - if xVehicle.owner == newOwner then + local vehicleData = Core.vehicles[self.plate] + if vehicleData.owner == newOwner then return true end - local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { newOwner, xVehicle.owner, xVehicle.plate }) + local affectedRows = MySQL.update.await("UPDATE `owned_vehicles` SET `owner` = ? WHERE owner = ? AND `plate` = ?", { newOwner, vehicleData.owner, vehicleData.plate }) if affectedRows <= 0 then self:delete() return false end - Entity(xVehicle.entity).state:set("owner", newOwner, false) - xVehicle.owner = newOwner + Entity(vehicleData.entity).state:set("owner", newOwner, false) + vehicleData.owner = newOwner return true end, @@ -207,18 +207,18 @@ Core.vehicleClass = { isImpound = false end - local xVehicle = Core.vehicles[self.plate] - if not xVehicle then + local vehicleData = Core.vehicles[self.plate] + if not vehicleData then return end - local entity = NetworkGetEntityFromNetworkId(xVehicle.netId) - if entity >= 0 and Entity(entity).state.owner == xVehicle.owner then - DeleteEntity(xVehicle.entity) + local entity = NetworkGetEntityFromNetworkId(vehicleData.netId) + if entity >= 0 and Entity(entity).state.owner == vehicleData.owner then + DeleteEntity(vehicleData.entity) end local query = "UPDATE `owned_vehicles` SET `stored` = true WHERE `plate` = ? AND `owner` = ?" - local queryParams = { xVehicle.plate, xVehicle.owner } + local queryParams = { vehicleData.plate, vehicleData.owner } if garageName then if isImpound then query = "UPDATE `owned_vehicles` SET `stored` = true, `parking` = NULL, `pound` = ? WHERE `plate` = ? AND `owner` = ?" @@ -226,7 +226,7 @@ Core.vehicleClass = { query = "UPDATE `owned_vehicles` SET `stored` = true, `pound` = NULL, `parking` = ? WHERE `plate` = ? AND `owner` = ?" end - queryParams = { garageName, xVehicle.plate, xVehicle.owner } + queryParams = { garageName, vehicleData.plate, vehicleData.owner } end MySQL.update.await(query, queryParams) From a812285e3c251f7e0865186d73cb1b32726e9c37 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 22 Dec 2024 15:39:16 +0100 Subject: [PATCH 023/132] refactor(es_extended/server/classes/vehicle): refactory query --- [core]/es_extended/server/classes/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/classes/vehicle.lua b/[core]/es_extended/server/classes/vehicle.lua index ac9feca8..b85c8f65 100644 --- a/[core]/es_extended/server/classes/vehicle.lua +++ b/[core]/es_extended/server/classes/vehicle.lua @@ -63,7 +63,7 @@ Core.vehicleClass = { } Core.vehicles[plate] = vehicleData - MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = 0 WHERE `owner` = ? AND `plate` = ?", { owner, plate }) + MySQL.update.await("UPDATE `owned_vehicles` SET `stored` = false WHERE `owner` = ? AND `plate` = ?", { owner, plate }) local obj = table.clone(Core.vehicleClass) obj.plate = plate From 968c55880491e1f4b35a56dbf756bd7b673a28d0 Mon Sep 17 00:00:00 2001 From: Kasey FItton Date: Wed, 8 Jan 2025 12:31:50 +0000 Subject: [PATCH 024/132] chore: update copyright year --- [core]/cron/LICENSE | 4 +- [core]/cron/README.md | 2 +- [core]/es_extended/LICENSE | 4 +- [core]/es_extended/README.md | 2 +- [core]/esx_chat_theme/README.md | 2 +- [core]/esx_context/LICENSE | 4 +- [core]/esx_context/README.md | 2 +- [core]/esx_identity/LICENSE | 4 +- [core]/esx_identity/README.md | 2 +- [core]/esx_loadingscreen/LICENSE | 4 +- [core]/esx_loadingscreen/README.md | 2 +- [core]/esx_menu_default/LICENSE | 4 +- [core]/esx_menu_default/README.md | 2 +- [core]/esx_menu_dialog/LICENSE | 4 +- [core]/esx_menu_dialog/README.md | 4 +- [core]/esx_menu_list/LICENSE | 4 +- [core]/esx_menu_list/README.md | 4 +- [core]/esx_multicharacter/LICENSE.md | 4 +- [core]/esx_multicharacter/readme.md | 2 +- [core]/esx_notify/LICENSE | 4 +- [core]/esx_notify/readme.md | 2 +- [core]/esx_progressbar/LICENSE | 1348 +++++++++++++------------- [core]/esx_progressbar/readme.md | 30 +- [core]/esx_skin/LICENSE | 4 +- [core]/esx_skin/README.md | 2 +- [core]/esx_textui/LICENSE | 4 +- [core]/esx_textui/readme.md | 2 +- [core]/skinchanger/LICENSE | 4 +- [core]/skinchanger/README.md | 2 +- 29 files changed, 731 insertions(+), 731 deletions(-) diff --git a/[core]/cron/LICENSE b/[core]/cron/LICENSE index 9ef14b9d..4defadc1 100644 --- a/[core]/cron/LICENSE +++ b/[core]/cron/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. cron - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - cron Copyright (C) 2015-2024 Jérémie N'gadi + cron Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/cron/README.md b/[core]/cron/README.md index df2e5eeb..77507c5a 100644 --- a/[core]/cron/README.md +++ b/[core]/cron/README.md @@ -28,7 +28,7 @@ TriggerEvent('cron:runAt', 18, 30, CronTask) cron - run tasks at specific intervals! -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. diff --git a/[core]/es_extended/LICENSE b/[core]/es_extended/LICENSE index 5f5e070b..d5b048d4 100644 --- a/[core]/es_extended/LICENSE +++ b/[core]/es_extended/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. es_extended - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - es_extended Copyright (C) 2015-2024 Jérémie N'gadi + es_extended Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/es_extended/README.md b/[core]/es_extended/README.md index 32ae43b8..5c13688a 100644 --- a/[core]/es_extended/README.md +++ b/[core]/es_extended/README.md @@ -4,7 +4,7 @@ es_extended -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. diff --git a/[core]/esx_chat_theme/README.md b/[core]/esx_chat_theme/README.md index 1d149d4a..a87e8af7 100644 --- a/[core]/esx_chat_theme/README.md +++ b/[core]/esx_chat_theme/README.md @@ -6,7 +6,7 @@ A ESX-Based Chat-theme for your server esx_chat_theme - ESX Chat Theme -Copyright (C) 2024 Jérémie N'gadi +Copyright (C) 2025 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. diff --git a/[core]/esx_context/LICENSE b/[core]/esx_context/LICENSE index dae70ee4..d73da6ba 100644 --- a/[core]/esx_context/LICENSE +++ b/[core]/esx_context/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_context - Copyright (C) 2022-2024 ESX Framework + Copyright (C) 2022-2025 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_context Copyright (C) 2022-2024 ESX Framework + esx_context Copyright (C) 2022-2025 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_context/README.md b/[core]/esx_context/README.md index 6b6a53de..9acc04e9 100644 --- a/[core]/esx_context/README.md +++ b/[core]/esx_context/README.md @@ -6,7 +6,7 @@ A elegant, easy to use Context Menu system to make User Interactions clean and h esx_context -Copyright (C) 2022-2024 ESX Framework +Copyright (C) 2022-2025 ESX Framework 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. diff --git a/[core]/esx_identity/LICENSE b/[core]/esx_identity/LICENSE index e57012ae..0466d652 100644 --- a/[core]/esx_identity/LICENSE +++ b/[core]/esx_identity/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_identity - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_identity Copyright (C) 2015-2024 Jérémie N'gadi + esx_identity Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_identity/README.md b/[core]/esx_identity/README.md index c0637081..2b9618c9 100644 --- a/[core]/esx_identity/README.md +++ b/[core]/esx_identity/README.md @@ -21,7 +21,7 @@ A Core Resource that Allows the player to Pick their characters, Name, Gender, H esx_identity - Make your Character a Person! -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. diff --git a/[core]/esx_loadingscreen/LICENSE b/[core]/esx_loadingscreen/LICENSE index 79832f23..80785a16 100644 --- a/[core]/esx_loadingscreen/LICENSE +++ b/[core]/esx_loadingscreen/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_loadingscreen - Copyright (C) 2020-2024 ESX Framework + Copyright (C) 2020-2025 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_loadingscreen Copyright (C) 2020-2024 ESX Framework + esx_loadingscreen Copyright (C) 2020-2025 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_loadingscreen/README.md b/[core]/esx_loadingscreen/README.md index 366c5ada..0e39378e 100644 --- a/[core]/esx_loadingscreen/README.md +++ b/[core]/esx_loadingscreen/README.md @@ -6,7 +6,7 @@ A simple but beautiful Loading Screen for your server! esx_loadingscreen - Loading in style! -Copyright (C) 2020-2024 ESX Framework +Copyright (C) 2020-2025 ESX Framework 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. diff --git a/[core]/esx_menu_default/LICENSE b/[core]/esx_menu_default/LICENSE index 8eef124b..e4c61364 100644 --- a/[core]/esx_menu_default/LICENSE +++ b/[core]/esx_menu_default/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_default - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_default Copyright (C) 2015-2024 Jérémie N'gadi + esx_menu_default Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_default/README.md b/[core]/esx_menu_default/README.md index 29e07d1f..8a6bd80f 100644 --- a/[core]/esx_menu_default/README.md +++ b/[core]/esx_menu_default/README.md @@ -9,7 +9,7 @@ A default List type menu for ESX. esx_menu_default - Default Menu! -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. diff --git a/[core]/esx_menu_dialog/LICENSE b/[core]/esx_menu_dialog/LICENSE index 7b3fd1a9..625326fe 100644 --- a/[core]/esx_menu_dialog/LICENSE +++ b/[core]/esx_menu_dialog/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_dialog - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_dialog Copyright (C) 2015-2024 Jérémie N'gadi + esx_menu_dialog Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_dialog/README.md b/[core]/esx_menu_dialog/README.md index 038f95f7..6817fcd8 100644 --- a/[core]/esx_menu_dialog/README.md +++ b/[core]/esx_menu_dialog/README.md @@ -12,10 +12,10 @@ start esx_menu_dialog ### License esx_menu_dialog - input dialog for ESX -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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 http://www.gnu.org/licenses/. \ No newline at end of file +You should have received a copy Of the GNU General Public License along with this program. If Not, see http://www.gnu.org/licenses/. diff --git a/[core]/esx_menu_list/LICENSE b/[core]/esx_menu_list/LICENSE index b56c9231..455eebc8 100644 --- a/[core]/esx_menu_list/LICENSE +++ b/[core]/esx_menu_list/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_list - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_list Copyright (C) 2015-2024 Jérémie N'gadi + esx_menu_list Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_list/README.md b/[core]/esx_menu_list/README.md index 9b829283..9fd8fc26 100644 --- a/[core]/esx_menu_list/README.md +++ b/[core]/esx_menu_list/README.md @@ -5,10 +5,10 @@ Advanced menu inputs for ESX ### License esx_menu_list - advanced menu inputs for ESX -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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 http://www.gnu.org/licenses/. \ No newline at end of file +You should have received a copy Of the GNU General Public License along with this program. If Not, see http://www.gnu.org/licenses/. diff --git a/[core]/esx_multicharacter/LICENSE.md b/[core]/esx_multicharacter/LICENSE.md index e23434ea..45ddb022 100644 --- a/[core]/esx_multicharacter/LICENSE.md +++ b/[core]/esx_multicharacter/LICENSE.md @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_multicharacter - Copyright (C) 2022-2024 ESX Framework, Linden, KASH + Copyright (C) 2022-2025 ESX Framework, Linden, KASH 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_multicharacter Copyright (C) 2022-2024 ESX Framework, Linden, KASH + esx_multicharacter Copyright (C) 2022-2025 ESX Framework, Linden, KASH This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_multicharacter/readme.md b/[core]/esx_multicharacter/readme.md index 2517728e..eb2ffa6b 100644 --- a/[core]/esx_multicharacter/readme.md +++ b/[core]/esx_multicharacter/readme.md @@ -21,7 +21,7 @@ A Simplistic system, that allows Players to have multiple Characters, which can Official Multi-Character system for ESX Legacy -Copyright © 2022-2024 Linden, ESX-Framework and KASH +Copyright © 2022-2025 Linden, ESX-Framework and KASH 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 diff --git a/[core]/esx_notify/LICENSE b/[core]/esx_notify/LICENSE index a7568793..28d1db85 100644 --- a/[core]/esx_notify/LICENSE +++ b/[core]/esx_notify/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_notify - Copyright (C) 2022-2024 ESX Framework + Copyright (C) 2022-2025 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_notify Copyright (C) 2022-2024 ESX Framework + esx_notify Copyright (C) 2022-2025 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_notify/readme.md b/[core]/esx_notify/readme.md index f6bc4c9d..621b89f7 100644 --- a/[core]/esx_notify/readme.md +++ b/[core]/esx_notify/readme.md @@ -55,7 +55,7 @@ ESX.ShowNotification("I i ~r~love~s~ donuts", "success", 3000) esx_notify- Notify! -Copyright (C) 2022-2024 ESX-Framework +Copyright (C) 2022-2025 ESX-Framework 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. diff --git a/[core]/esx_progressbar/LICENSE b/[core]/esx_progressbar/LICENSE index e3013ae2..28d1db85 100644 --- a/[core]/esx_progressbar/LICENSE +++ b/[core]/esx_progressbar/LICENSE @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - esx_notify - Copyright (C) 2022-2024 ESX Framework - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - esx_notify Copyright (C) 2022-2024 ESX Framework - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + esx_notify + Copyright (C) 2022-2025 ESX Framework + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + esx_notify Copyright (C) 2022-2025 ESX Framework + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[core]/esx_progressbar/readme.md b/[core]/esx_progressbar/readme.md index a3daddfe..91ec2123 100644 --- a/[core]/esx_progressbar/readme.md +++ b/[core]/esx_progressbar/readme.md @@ -3,12 +3,12 @@ * ESX Function ```lua ESX.Progressbar("test", 25000,{ - FreezePlayer = false, + FreezePlayer = false, animation ={ type = "anim", - dict = "mini@prostitutes@sexlow_veh", - lib ="low_car_sex_to_prop_p2_player" - }, + dict = "mini@prostitutes@sexlow_veh", + lib ="low_car_sex_to_prop_p2_player" + }, onFinish = function() --Code here end}) @@ -16,13 +16,13 @@ ``` * Export - + ```lua exports["esx_progressbar"]:Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "anim", - dict = "anim@mp_player_intmenu@key_fob@", + dict = "anim@mp_player_intmenu@key_fob@", lib ="fob_click" }, onFinish = function() @@ -31,13 +31,13 @@ ``` * Cancel - + ```lua ESX.Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "anim", - dict = "anim@mp_player_intmenu@key_fob@", + dict = "anim@mp_player_intmenu@key_fob@", lib ="fob_click" }, onFinish = function() @@ -49,13 +49,13 @@ ``` * Scenario - + ```lua ESX.Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "Scenario", - Scenario = "PROP_HUMAN_BUM_BIN", + Scenario = "PROP_HUMAN_BUM_BIN", }, onFinish = function() --Code here @@ -69,10 +69,10 @@ esx_progressbar -Copyright (C) 2022-2024 ESX-Framework +Copyright (C) 2022-2025 ESX-Framework 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 . \ No newline at end of file +You should have received a copy Of the GNU General Public License along with this program. If Not, see . diff --git a/[core]/esx_skin/LICENSE b/[core]/esx_skin/LICENSE index 78332fdb..39517c08 100644 --- a/[core]/esx_skin/LICENSE +++ b/[core]/esx_skin/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_skin - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_skin Copyright (C) 2015-2024 Jérémie N'gadi + esx_skin Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_skin/README.md b/[core]/esx_skin/README.md index e004178c..57c091b3 100644 --- a/[core]/esx_skin/README.md +++ b/[core]/esx_skin/README.md @@ -32,7 +32,7 @@ start esx_skin ### License esx_skin - skin selector for ESX -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. diff --git a/[core]/esx_textui/LICENSE b/[core]/esx_textui/LICENSE index cf001768..77dc202c 100644 --- a/[core]/esx_textui/LICENSE +++ b/[core]/esx_textui/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_textui - Copyright (C) 2015-2024 ESX Framework + Copyright (C) 2015-2025 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_textui Copyright (C) 2022-2024 ESX Framework + esx_textui Copyright (C) 2022-2025 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_textui/readme.md b/[core]/esx_textui/readme.md index 922e11a8..bdd3bda8 100644 --- a/[core]/esx_textui/readme.md +++ b/[core]/esx_textui/readme.md @@ -43,7 +43,7 @@ ESX.TextUI("i ~r~love~s~ donuts", "error") esx_textui - Persistent Notifications -Copyright (C) 2022-2024 ESX Framework +Copyright (C) 2022-2025 ESX Framework 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. diff --git a/[core]/skinchanger/LICENSE b/[core]/skinchanger/LICENSE index 89a184fc..bcb2e5ff 100644 --- a/[core]/skinchanger/LICENSE +++ b/[core]/skinchanger/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. skinchanger - Copyright (C) 2015-2024 Jérémie N'gadi + Copyright (C) 2015-2025 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - skinchanger Copyright (C) 2015-2024 Jérémie N'gadi + skinchanger Copyright (C) 2015-2025 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/skinchanger/README.md b/[core]/skinchanger/README.md index 7b1b3dd6..141c344f 100644 --- a/[core]/skinchanger/README.md +++ b/[core]/skinchanger/README.md @@ -73,7 +73,7 @@ end) skinchanger - Own your skin! -Copyright (C) 2015-2024 Jérémie N'gadi +Copyright (C) 2015-2025 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. From 97aaf58a021ba4dd2ce2fa6daf17f43a94eb5b4a Mon Sep 17 00:00:00 2001 From: Arctos2win <116841243+Arctos2win@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:17:39 +0100 Subject: [PATCH 025/132] Revert "chore: update copyright year" --- .github/pull_request_template.md | 22 - [core]/cron/LICENSE | 4 +- [core]/cron/README.md | 2 +- [core]/cron/fxmanifest.lua | 2 +- [core]/es_extended/LICENSE | 4 +- [core]/es_extended/README.md | 2 +- .../es_extended/client/modules/scaleform.lua | 84 +- [core]/es_extended/fxmanifest.lua | 2 +- [core]/esx_chat_theme/README.md | 2 +- [core]/esx_chat_theme/fxmanifest.lua | 2 +- [core]/esx_context/LICENSE | 4 +- [core]/esx_context/README.md | 2 +- [core]/esx_context/fxmanifest.lua | 2 +- [core]/esx_identity/LICENSE | 4 +- [core]/esx_identity/README.md | 2 +- [core]/esx_identity/fxmanifest.lua | 2 +- [core]/esx_loadingscreen/LICENSE | 4 +- [core]/esx_loadingscreen/README.md | 2 +- [core]/esx_loadingscreen/fxmanifest.lua | 2 +- [core]/esx_menu_default/LICENSE | 4 +- [core]/esx_menu_default/README.md | 2 +- [core]/esx_menu_default/fxmanifest.lua | 2 +- [core]/esx_menu_dialog/LICENSE | 4 +- [core]/esx_menu_dialog/README.md | 4 +- [core]/esx_menu_dialog/fxmanifest.lua | 2 +- [core]/esx_menu_list/LICENSE | 4 +- [core]/esx_menu_list/README.md | 4 +- [core]/esx_menu_list/fxmanifest.lua | 2 +- [core]/esx_multicharacter/LICENSE.md | 4 +- [core]/esx_multicharacter/fxmanifest.lua | 2 +- [core]/esx_multicharacter/readme.md | 2 +- [core]/esx_notify/LICENSE | 4 +- [core]/esx_notify/fxmanifest.lua | 2 +- [core]/esx_notify/readme.md | 2 +- [core]/esx_progressbar/LICENSE | 1348 ++++++++--------- [core]/esx_progressbar/fxmanifest.lua | 2 +- [core]/esx_progressbar/readme.md | 30 +- [core]/esx_skin/LICENSE | 4 +- [core]/esx_skin/README.md | 2 +- [core]/esx_skin/fxmanifest.lua | 2 +- [core]/esx_textui/LICENSE | 4 +- [core]/esx_textui/fxmanifest.lua | 2 +- [core]/esx_textui/readme.md | 2 +- [core]/skinchanger/LICENSE | 4 +- [core]/skinchanger/README.md | 2 +- [core]/skinchanger/fxmanifest.lua | 2 +- 46 files changed, 789 insertions(+), 809 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 30db5c2b..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,22 +0,0 @@ -### Description - - ---- -### Motivation - - ---- - -### **Implementation Details** - ---- - -### Usage Example - ---- - -### PR Checklist -- [] My commit messages and PR title follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard. -- [] My changes have been tested locally and function as expected. -- [] My PR does not introduce any breaking changes. -- [] I have provided a clear explanation of what my PR does, including the reasoning behind the changes and any relevant context. diff --git a/[core]/cron/LICENSE b/[core]/cron/LICENSE index 4defadc1..9ef14b9d 100644 --- a/[core]/cron/LICENSE +++ b/[core]/cron/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. cron - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - cron Copyright (C) 2015-2025 Jérémie N'gadi + cron Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/cron/README.md b/[core]/cron/README.md index 77507c5a..df2e5eeb 100644 --- a/[core]/cron/README.md +++ b/[core]/cron/README.md @@ -28,7 +28,7 @@ TriggerEvent('cron:runAt', 18, 30, CronTask) cron - run tasks at specific intervals! -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/cron/fxmanifest.lua b/[core]/cron/fxmanifest.lua index db502695..200cf33f 100644 --- a/[core]/cron/fxmanifest.lua +++ b/[core]/cron/fxmanifest.lua @@ -4,6 +4,6 @@ game 'gta5' author 'ESX-Framework' description 'Allows resources to Run tasks at specific intervals.' lua54 'yes' -version '1.12.3' +version '1.12.2' server_script 'server/main.lua' diff --git a/[core]/es_extended/LICENSE b/[core]/es_extended/LICENSE index d5b048d4..5f5e070b 100644 --- a/[core]/es_extended/LICENSE +++ b/[core]/es_extended/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. es_extended - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - es_extended Copyright (C) 2015-2025 Jérémie N'gadi + es_extended Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/es_extended/README.md b/[core]/es_extended/README.md index 5c13688a..32ae43b8 100644 --- a/[core]/es_extended/README.md +++ b/[core]/es_extended/README.md @@ -4,7 +4,7 @@ es_extended -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/es_extended/client/modules/scaleform.lua b/[core]/es_extended/client/modules/scaleform.lua index 66479685..31d0897e 100644 --- a/[core]/es_extended/client/modules/scaleform.lua +++ b/[core]/es_extended/client/modules/scaleform.lua @@ -2,12 +2,7 @@ ESX.Scaleform = {} ESX.Scaleform.Utils = {} function ESX.Scaleform.ShowFreemodeMessage(title, msg, sec) - local scaleform = ESX.Scaleform.Utils.RequestScaleformMovie("MP_BIG_MESSAGE_FREEMODE") - - BeginScaleformMovieMethod(scaleform, "SHOW_SHARD_WASTED_MP_MESSAGE") - ScaleformMovieMethodAddParamTextureNameString(title) - ScaleformMovieMethodAddParamTextureNameString(msg) - EndScaleformMovieMethod() + local scaleform = ESX.Scaleform.Utils.RunMethod("MP_BIG_MESSAGE_FREEMODE", "SHOW_SHARD_WASTED_MP_MESSAGE", false, title, msg) while sec > 0 do Wait(0) @@ -20,25 +15,9 @@ function ESX.Scaleform.ShowFreemodeMessage(title, msg, sec) end function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) - local scaleform = ESX.Scaleform.Utils.RequestScaleformMovie("BREAKING_NEWS") - - BeginScaleformMovieMethod(scaleform, "SET_TEXT") - ScaleformMovieMethodAddParamTextureNameString(msg) - ScaleformMovieMethodAddParamTextureNameString(bottom) - EndScaleformMovieMethod() - - BeginScaleformMovieMethod(scaleform, "SET_SCROLL_TEXT") - ScaleformMovieMethodAddParamInt(0) -- top ticker - ScaleformMovieMethodAddParamInt(0) -- Since this is the first string, start at 0 - ScaleformMovieMethodAddParamTextureNameString(title) - - EndScaleformMovieMethod() - - BeginScaleformMovieMethod(scaleform, "DISPLAY_SCROLL_TEXT") - ScaleformMovieMethodAddParamInt(0) -- Top ticker - ScaleformMovieMethodAddParamInt(0) -- Index of string - - EndScaleformMovieMethod() + local scaleform = ESX.Scaleform.Utils.RunMethod("BREAKING_NEWS", "SET_TEXT", false, msg, bottom) + ESX.Scaleform.Utils.RunMethod(scaleform, "SET_SCROLL_TEXT", false, 0, 0, title) + ESX.Scaleform.Utils.RunMethod(scaleform, "DISPLAY_SCROLL_TEXT", false, 0, 0) while sec > 0 do Wait(0) @@ -51,17 +30,7 @@ function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) end function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) - local scaleform = ESX.Scaleform.Utils.RequestScaleformMovie("POPUP_WARNING") - - BeginScaleformMovieMethod(scaleform, "SHOW_POPUP_WARNING") - - ScaleformMovieMethodAddParamFloat(500.0) -- black background - ScaleformMovieMethodAddParamTextureNameString(title) - ScaleformMovieMethodAddParamTextureNameString(msg) - ScaleformMovieMethodAddParamTextureNameString(bottom) - ScaleformMovieMethodAddParamBool(true) - - EndScaleformMovieMethod() + local scaleform = ESX.Scaleform.Utils.RunMethod("POPUP_WARNING", "SHOW_POPUP_WARNING", false, 500.0, title, msg, bottom, true) while sec > 0 do Wait(0) @@ -74,11 +43,7 @@ function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) end function ESX.Scaleform.ShowTrafficMovie(sec) - local scaleform = ESX.Scaleform.Utils.RequestScaleformMovie("TRAFFIC_CAM") - - BeginScaleformMovieMethod(scaleform, "PLAY_CAM_MOVIE") - - EndScaleformMovieMethod() + local scaleform = ESX.Scaleform.Utils.RunMethod("TRAFFIC_CAM", "PLAY_CAM_MOVIE", false) while sec > 0 do Wait(0) @@ -99,3 +64,40 @@ function ESX.Scaleform.Utils.RequestScaleformMovie(movie) return scaleform end + +--- Executes a method on a scaleform movie with optional arguments and return value. +--- The caller is responsible for disposing of the scaleform using `SetScaleformMovieAsNoLongerNeeded`. +---@param scaleform number|string # Scaleform handle or name to request the scaleform movie +---@param methodName string # The method name to call on the scaleform +---@param returnValue? boolean # Whether to return the value from the method +---@param ... number|string|boolean # Arguments to pass to the method +---@return number, number? # The scaleform handle, and the return value if `returnValue` is true +function ESX.Scaleform.Utils.RunMethod(scaleform, methodName, returnValue, ...) + scaleform = type(scaleform) == "number" and scaleform or ESX.Scaleform.Utils.RequestScaleformMovie(scaleform) + BeginScaleformMovieMethod(scaleform, methodName) + + local args = { ... } + for i, arg in ipairs(args) do + local typeArg = type(arg) + + if typeArg == "number" then + if math.type(arg) == "float" then + ScaleformMovieMethodAddParamFloat(arg) + else + ScaleformMovieMethodAddParamInt(arg) + end + elseif typeArg == "string" then + ScaleformMovieMethodAddParamTextureNameString(arg) + elseif typeArg == "boolean" then + ScaleformMovieMethodAddParamBool(arg) + end + end + + if returnValue then + return scaleform, EndScaleformMovieMethodReturnValue() + end + + EndScaleformMovieMethod() + + return scaleform +end diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index a579d4ee..c43e9574 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'cerulean' game 'gta5' description 'The Core resource that provides the functionalities for all other resources.' lua54 'yes' -version '1.12.3' +version '1.12.2' shared_scripts { 'locale.lua', diff --git a/[core]/esx_chat_theme/README.md b/[core]/esx_chat_theme/README.md index a87e8af7..1d149d4a 100644 --- a/[core]/esx_chat_theme/README.md +++ b/[core]/esx_chat_theme/README.md @@ -6,7 +6,7 @@ A ESX-Based Chat-theme for your server esx_chat_theme - ESX Chat Theme -Copyright (C) 2025 Jérémie N'gadi +Copyright (C) 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. diff --git a/[core]/esx_chat_theme/fxmanifest.lua b/[core]/esx_chat_theme/fxmanifest.lua index 704c76b3..e29afa25 100644 --- a/[core]/esx_chat_theme/fxmanifest.lua +++ b/[core]/esx_chat_theme/fxmanifest.lua @@ -1,4 +1,4 @@ -version '1.12.3' +version '1.12.2' author 'ESX-Framework' description 'A ESX Stylised theme for the chat resource.' diff --git a/[core]/esx_context/LICENSE b/[core]/esx_context/LICENSE index d73da6ba..dae70ee4 100644 --- a/[core]/esx_context/LICENSE +++ b/[core]/esx_context/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_context - Copyright (C) 2022-2025 ESX Framework + Copyright (C) 2022-2024 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_context Copyright (C) 2022-2025 ESX Framework + esx_context Copyright (C) 2022-2024 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_context/README.md b/[core]/esx_context/README.md index 9acc04e9..6b6a53de 100644 --- a/[core]/esx_context/README.md +++ b/[core]/esx_context/README.md @@ -6,7 +6,7 @@ A elegant, easy to use Context Menu system to make User Interactions clean and h esx_context -Copyright (C) 2022-2025 ESX Framework +Copyright (C) 2022-2024 ESX Framework 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. diff --git a/[core]/esx_context/fxmanifest.lua b/[core]/esx_context/fxmanifest.lua index 4be9c618..0db66d92 100644 --- a/[core]/esx_context/fxmanifest.lua +++ b/[core]/esx_context/fxmanifest.lua @@ -4,7 +4,7 @@ game 'gta5' author 'ESX-Framework & Brayden' description 'A simplistic context menu for ESX.' lua54 'yes' -version '1.12.3' +version '1.12.2' ui_page 'index.html' diff --git a/[core]/esx_identity/LICENSE b/[core]/esx_identity/LICENSE index 0466d652..e57012ae 100644 --- a/[core]/esx_identity/LICENSE +++ b/[core]/esx_identity/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_identity - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_identity Copyright (C) 2015-2025 Jérémie N'gadi + esx_identity Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_identity/README.md b/[core]/esx_identity/README.md index 2b9618c9..c0637081 100644 --- a/[core]/esx_identity/README.md +++ b/[core]/esx_identity/README.md @@ -21,7 +21,7 @@ A Core Resource that Allows the player to Pick their characters, Name, Gender, H esx_identity - Make your Character a Person! -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/esx_identity/fxmanifest.lua b/[core]/esx_identity/fxmanifest.lua index 1ff5e2de..027d1168 100644 --- a/[core]/esx_identity/fxmanifest.lua +++ b/[core]/esx_identity/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' description 'Allows the player to Pick their characters: Name, Gender, Height and Date-of-birth.' lua54 'yes' -version '1.12.3' +version '1.12.2' shared_scripts { '@es_extended/imports.lua', diff --git a/[core]/esx_loadingscreen/LICENSE b/[core]/esx_loadingscreen/LICENSE index 80785a16..79832f23 100644 --- a/[core]/esx_loadingscreen/LICENSE +++ b/[core]/esx_loadingscreen/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_loadingscreen - Copyright (C) 2020-2025 ESX Framework + Copyright (C) 2020-2024 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_loadingscreen Copyright (C) 2020-2025 ESX Framework + esx_loadingscreen Copyright (C) 2020-2024 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_loadingscreen/README.md b/[core]/esx_loadingscreen/README.md index 0e39378e..366c5ada 100644 --- a/[core]/esx_loadingscreen/README.md +++ b/[core]/esx_loadingscreen/README.md @@ -6,7 +6,7 @@ A simple but beautiful Loading Screen for your server! esx_loadingscreen - Loading in style! -Copyright (C) 2020-2025 ESX Framework +Copyright (C) 2020-2024 ESX Framework 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. diff --git a/[core]/esx_loadingscreen/fxmanifest.lua b/[core]/esx_loadingscreen/fxmanifest.lua index 9c5eba20..35acfdb9 100644 --- a/[core]/esx_loadingscreen/fxmanifest.lua +++ b/[core]/esx_loadingscreen/fxmanifest.lua @@ -3,7 +3,7 @@ game 'common' fx_version 'cerulean' author 'ESX-Framework' description 'Allows resources to Run tasks at specific intervals.' -version '1.12.3' +version '1.12.2' lua54 'yes' loadscreen 'index.html' diff --git a/[core]/esx_menu_default/LICENSE b/[core]/esx_menu_default/LICENSE index e4c61364..8eef124b 100644 --- a/[core]/esx_menu_default/LICENSE +++ b/[core]/esx_menu_default/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_default - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_default Copyright (C) 2015-2025 Jérémie N'gadi + esx_menu_default Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_default/README.md b/[core]/esx_menu_default/README.md index 8a6bd80f..29e07d1f 100644 --- a/[core]/esx_menu_default/README.md +++ b/[core]/esx_menu_default/README.md @@ -9,7 +9,7 @@ A default List type menu for ESX. esx_menu_default - Default Menu! -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/esx_menu_default/fxmanifest.lua b/[core]/esx_menu_default/fxmanifest.lua index fc171998..851fbcd6 100644 --- a/[core]/esx_menu_default/fxmanifest.lua +++ b/[core]/esx_menu_default/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' description 'A basic menu system for ESX Legacy.' lua54 'yes' -version '1.12.3' +version '1.12.2' client_scripts { '@es_extended/imports.lua', 'client/main.lua' } diff --git a/[core]/esx_menu_dialog/LICENSE b/[core]/esx_menu_dialog/LICENSE index 625326fe..7b3fd1a9 100644 --- a/[core]/esx_menu_dialog/LICENSE +++ b/[core]/esx_menu_dialog/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_dialog - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_dialog Copyright (C) 2015-2025 Jérémie N'gadi + esx_menu_dialog Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_dialog/README.md b/[core]/esx_menu_dialog/README.md index 6817fcd8..038f95f7 100644 --- a/[core]/esx_menu_dialog/README.md +++ b/[core]/esx_menu_dialog/README.md @@ -12,10 +12,10 @@ start esx_menu_dialog ### License esx_menu_dialog - input dialog for ESX -Copyright (C) 2015-2025 Jérémie N'gadi +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 http://www.gnu.org/licenses/. +You should have received a copy Of the GNU General Public License along with this program. If Not, see http://www.gnu.org/licenses/. \ No newline at end of file diff --git a/[core]/esx_menu_dialog/fxmanifest.lua b/[core]/esx_menu_dialog/fxmanifest.lua index 09350575..a932977a 100644 --- a/[core]/esx_menu_dialog/fxmanifest.lua +++ b/[core]/esx_menu_dialog/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' description 'A basic input dialog for ESX Legacy.' lua54 'yes' -version '1.12.3' +version '1.12.2' client_scripts { '@es_extended/imports.lua', diff --git a/[core]/esx_menu_list/LICENSE b/[core]/esx_menu_list/LICENSE index 455eebc8..b56c9231 100644 --- a/[core]/esx_menu_list/LICENSE +++ b/[core]/esx_menu_list/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_menu_list - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_menu_list Copyright (C) 2015-2025 Jérémie N'gadi + esx_menu_list Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_menu_list/README.md b/[core]/esx_menu_list/README.md index 9fd8fc26..9b829283 100644 --- a/[core]/esx_menu_list/README.md +++ b/[core]/esx_menu_list/README.md @@ -5,10 +5,10 @@ Advanced menu inputs for ESX ### License esx_menu_list - advanced menu inputs for ESX -Copyright (C) 2015-2025 Jérémie N'gadi +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 http://www.gnu.org/licenses/. +You should have received a copy Of the GNU General Public License along with this program. If Not, see http://www.gnu.org/licenses/. \ No newline at end of file diff --git a/[core]/esx_menu_list/fxmanifest.lua b/[core]/esx_menu_list/fxmanifest.lua index f0f4b534..606c36ad 100644 --- a/[core]/esx_menu_list/fxmanifest.lua +++ b/[core]/esx_menu_list/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' description 'A basic table-based menu system for ESX Legacy.' lua54 'yes' -version '1.12.3' +version '1.12.2' client_scripts { diff --git a/[core]/esx_multicharacter/LICENSE.md b/[core]/esx_multicharacter/LICENSE.md index 45ddb022..e23434ea 100644 --- a/[core]/esx_multicharacter/LICENSE.md +++ b/[core]/esx_multicharacter/LICENSE.md @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_multicharacter - Copyright (C) 2022-2025 ESX Framework, Linden, KASH + Copyright (C) 2022-2024 ESX Framework, Linden, KASH 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_multicharacter Copyright (C) 2022-2025 ESX Framework, Linden, KASH + esx_multicharacter Copyright (C) 2022-2024 ESX Framework, Linden, KASH This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_multicharacter/fxmanifest.lua b/[core]/esx_multicharacter/fxmanifest.lua index 670945b9..81c76c37 100644 --- a/[core]/esx_multicharacter/fxmanifest.lua +++ b/[core]/esx_multicharacter/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'cerulean' game 'gta5' author 'ESX-Framework - Linden - KASH' description 'Allows players to have multiple characters on the same account.' -version '1.12.3' +version '1.12.2' lua54 'yes' dependencies { 'es_extended', 'esx_context', 'esx_identity', 'esx_skin' } diff --git a/[core]/esx_multicharacter/readme.md b/[core]/esx_multicharacter/readme.md index eb2ffa6b..2517728e 100644 --- a/[core]/esx_multicharacter/readme.md +++ b/[core]/esx_multicharacter/readme.md @@ -21,7 +21,7 @@ A Simplistic system, that allows Players to have multiple Characters, which can Official Multi-Character system for ESX Legacy -Copyright © 2022-2025 Linden, ESX-Framework and KASH +Copyright © 2022-2024 Linden, ESX-Framework and KASH 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 diff --git a/[core]/esx_notify/LICENSE b/[core]/esx_notify/LICENSE index 28d1db85..a7568793 100644 --- a/[core]/esx_notify/LICENSE +++ b/[core]/esx_notify/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_notify - Copyright (C) 2022-2025 ESX Framework + Copyright (C) 2022-2024 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_notify Copyright (C) 2022-2025 ESX Framework + esx_notify Copyright (C) 2022-2024 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_notify/fxmanifest.lua b/[core]/esx_notify/fxmanifest.lua index dd554681..d019b983 100644 --- a/[core]/esx_notify/fxmanifest.lua +++ b/[core]/esx_notify/fxmanifest.lua @@ -2,7 +2,7 @@ fx_version 'adamant' lua54 'yes' game 'gta5' -version '1.12.3' +version '1.12.2' author 'ESX-Framework' description 'A beautiful and simple NUI notification system for ESX' diff --git a/[core]/esx_notify/readme.md b/[core]/esx_notify/readme.md index 621b89f7..f6bc4c9d 100644 --- a/[core]/esx_notify/readme.md +++ b/[core]/esx_notify/readme.md @@ -55,7 +55,7 @@ ESX.ShowNotification("I i ~r~love~s~ donuts", "success", 3000) esx_notify- Notify! -Copyright (C) 2022-2025 ESX-Framework +Copyright (C) 2022-2024 ESX-Framework 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. diff --git a/[core]/esx_progressbar/LICENSE b/[core]/esx_progressbar/LICENSE index 28d1db85..e3013ae2 100644 --- a/[core]/esx_progressbar/LICENSE +++ b/[core]/esx_progressbar/LICENSE @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - esx_notify - Copyright (C) 2022-2025 ESX Framework - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - esx_notify Copyright (C) 2022-2025 ESX Framework - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + esx_notify + Copyright (C) 2022-2024 ESX Framework + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + esx_notify Copyright (C) 2022-2024 ESX Framework + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[core]/esx_progressbar/fxmanifest.lua b/[core]/esx_progressbar/fxmanifest.lua index 2180b57f..54caeda1 100644 --- a/[core]/esx_progressbar/fxmanifest.lua +++ b/[core]/esx_progressbar/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' author 'ESX-Framework' description 'A beautiful and simple NUI progress bar for ESX' -version '1.12.3' +version '1.12.2' lua54 'yes' client_scripts { 'Progress.lua' } diff --git a/[core]/esx_progressbar/readme.md b/[core]/esx_progressbar/readme.md index 91ec2123..a3daddfe 100644 --- a/[core]/esx_progressbar/readme.md +++ b/[core]/esx_progressbar/readme.md @@ -3,12 +3,12 @@ * ESX Function ```lua ESX.Progressbar("test", 25000,{ - FreezePlayer = false, + FreezePlayer = false, animation ={ type = "anim", - dict = "mini@prostitutes@sexlow_veh", - lib ="low_car_sex_to_prop_p2_player" - }, + dict = "mini@prostitutes@sexlow_veh", + lib ="low_car_sex_to_prop_p2_player" + }, onFinish = function() --Code here end}) @@ -16,13 +16,13 @@ ``` * Export - + ```lua exports["esx_progressbar"]:Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "anim", - dict = "anim@mp_player_intmenu@key_fob@", + dict = "anim@mp_player_intmenu@key_fob@", lib ="fob_click" }, onFinish = function() @@ -31,13 +31,13 @@ ``` * Cancel - + ```lua ESX.Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "anim", - dict = "anim@mp_player_intmenu@key_fob@", + dict = "anim@mp_player_intmenu@key_fob@", lib ="fob_click" }, onFinish = function() @@ -49,13 +49,13 @@ ``` * Scenario - + ```lua ESX.Progressbar("Unlocking Storage", 3000,{ - FreezePlayer = true, + FreezePlayer = true, animation ={ type = "Scenario", - Scenario = "PROP_HUMAN_BUM_BIN", + Scenario = "PROP_HUMAN_BUM_BIN", }, onFinish = function() --Code here @@ -69,10 +69,10 @@ esx_progressbar -Copyright (C) 2022-2025 ESX-Framework +Copyright (C) 2022-2024 ESX-Framework 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 . +You should have received a copy Of the GNU General Public License along with this program. If Not, see . \ No newline at end of file diff --git a/[core]/esx_skin/LICENSE b/[core]/esx_skin/LICENSE index 39517c08..78332fdb 100644 --- a/[core]/esx_skin/LICENSE +++ b/[core]/esx_skin/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_skin - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_skin Copyright (C) 2015-2025 Jérémie N'gadi + esx_skin Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_skin/README.md b/[core]/esx_skin/README.md index 57c091b3..e004178c 100644 --- a/[core]/esx_skin/README.md +++ b/[core]/esx_skin/README.md @@ -32,7 +32,7 @@ start esx_skin ### License esx_skin - skin selector for ESX -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/esx_skin/fxmanifest.lua b/[core]/esx_skin/fxmanifest.lua index 25569210..0485a171 100644 --- a/[core]/esx_skin/fxmanifest.lua +++ b/[core]/esx_skin/fxmanifest.lua @@ -2,7 +2,7 @@ fx_version 'adamant' game 'gta5' description 'Allows players to customise their character\'s appearance' -version '1.12.3' +version '1.12.2' lua54 'yes' shared_scripts { diff --git a/[core]/esx_textui/LICENSE b/[core]/esx_textui/LICENSE index 77dc202c..cf001768 100644 --- a/[core]/esx_textui/LICENSE +++ b/[core]/esx_textui/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. esx_textui - Copyright (C) 2015-2025 ESX Framework + Copyright (C) 2015-2024 ESX Framework 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - esx_textui Copyright (C) 2022-2025 ESX Framework + esx_textui Copyright (C) 2022-2024 ESX Framework This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/esx_textui/fxmanifest.lua b/[core]/esx_textui/fxmanifest.lua index 0fdd5605..4dc027b8 100644 --- a/[core]/esx_textui/fxmanifest.lua +++ b/[core]/esx_textui/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' author 'ESX-Framework' description 'A beautiful and simple Persistent Notification system for ESX.' -version '1.12.3' +version '1.12.2' lua54 'yes' client_scripts { 'TextUI.lua' } diff --git a/[core]/esx_textui/readme.md b/[core]/esx_textui/readme.md index bdd3bda8..922e11a8 100644 --- a/[core]/esx_textui/readme.md +++ b/[core]/esx_textui/readme.md @@ -43,7 +43,7 @@ ESX.TextUI("i ~r~love~s~ donuts", "error") esx_textui - Persistent Notifications -Copyright (C) 2022-2025 ESX Framework +Copyright (C) 2022-2024 ESX Framework 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. diff --git a/[core]/skinchanger/LICENSE b/[core]/skinchanger/LICENSE index bcb2e5ff..89a184fc 100644 --- a/[core]/skinchanger/LICENSE +++ b/[core]/skinchanger/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. skinchanger - Copyright (C) 2015-2025 Jérémie N'gadi + 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 @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - skinchanger Copyright (C) 2015-2025 Jérémie N'gadi + skinchanger Copyright (C) 2015-2024 Jérémie N'gadi This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/[core]/skinchanger/README.md b/[core]/skinchanger/README.md index 141c344f..7b1b3dd6 100644 --- a/[core]/skinchanger/README.md +++ b/[core]/skinchanger/README.md @@ -73,7 +73,7 @@ end) skinchanger - Own your skin! -Copyright (C) 2015-2025 Jérémie N'gadi +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. diff --git a/[core]/skinchanger/fxmanifest.lua b/[core]/skinchanger/fxmanifest.lua index 288bd751..43cefff9 100644 --- a/[core]/skinchanger/fxmanifest.lua +++ b/[core]/skinchanger/fxmanifest.lua @@ -2,7 +2,7 @@ fx_version 'adamant' game 'gta5' description 'Saves/loads character appearances for ESX Legacy.' -version '1.12.3' +version '1.12.2' lua54 'yes' client_scripts { From 9019935bf5c75c7f3226528dc03c0f42c39e3f04 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:44:49 +0100 Subject: [PATCH 026/132] fix(es_extended/client/modules/adjustments): `SetHudComponentPosition` expects float --- [core]/es_extended/client/modules/adjustments.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index b4d4fe8a..00412fcb 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -4,7 +4,7 @@ function Adjustments:RemoveHudComponents() for i = 1, #Config.RemoveHudComponents do if Config.RemoveHudComponents[i] then SetHudComponentSize(i, 0.0, 0.0) - SetHudComponentPosition(i, 900, 900) + SetHudComponentPosition(i, 900.0, 900.0) end end end From c744cf9bac1379bb33db96a3fe9aa3e789a829f7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Fri, 10 Jan 2025 22:20:09 +0100 Subject: [PATCH 027/132] fix(es_extended/client/modules/death): fix death thread on first spawn --- [core]/es_extended/client/modules/death.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/[core]/es_extended/client/modules/death.lua b/[core]/es_extended/client/modules/death.lua index ba3fb846..346c3ce4 100644 --- a/[core]/es_extended/client/modules/death.lua +++ b/[core]/es_extended/client/modules/death.lua @@ -62,6 +62,8 @@ end AddEventHandler("esx:onPlayerSpawn", function() Citizen.CreateThreadNow(function() + while not ESX.PlayerLoaded do Wait(0) end + while ESX.PlayerLoaded and not ESX.PlayerData.dead do if DoesEntityExist(ESX.PlayerData.ped) and (IsPedDeadOrDying(ESX.PlayerData.ped, true) or IsPedFatallyInjured(ESX.PlayerData.ped)) then Death:Died() From bde89e7dbfe6e9b81be61a74050870a578744bff Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 18 Jan 2025 21:41:36 +0100 Subject: [PATCH 028/132] refactor(es_extended/locale): refactor Translate fun --- [core]/es_extended/locale.lua | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/[core]/es_extended/locale.lua b/[core]/es_extended/locale.lua index db77eba7..7d940e4f 100644 --- a/[core]/es_extended/locale.lua +++ b/[core]/es_extended/locale.lua @@ -3,21 +3,23 @@ Locales = {} function Translate(str, ...) -- Translate string if not str then error(("Resource ^5%s^1 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) - return "Given translate function parameter is nil!" end - if Locales[Config.Locale] then - if Locales[Config.Locale][str] then - return string.format(Locales[Config.Locale][str], ...) - elseif Config.Locale ~= "en" and Locales["en"] and Locales["en"][str] then - return string.format(Locales["en"][str], ...) - else - return "Translation [" .. Config.Locale .. "][" .. str .. "] does not exist" + + local translations = Locales[Config.Locale] + if not translations then + -- Fall back to English translation if the current locale is not found + if Locales.en and Locales.en[str] then + return Locales.en[str]:format(...) end - elseif Config.Locale ~= "en" and Locales["en"] and Locales["en"][str] then - return string.format(Locales["en"][str], ...) - else - return "Locale [" .. Config.Locale .. "] does not exist" + + return ("Locale [%s] does not exist"):format(Config.Locale) end + + if translations[str] then + return translations[str]:format(...) + end + + return ("Translation [%s][%s] does not exist"):format(Config.Locale, str) end function TranslateCap(str, ...) -- Translate string first char uppercase From 86af2ef0edd8e6d23845048ae703cdb8f419d5ae Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 19 Jan 2025 12:54:14 +0100 Subject: [PATCH 029/132] feat(es_extended/locale): add support for lazy loading locales --- [core]/es_extended/fxmanifest.lua | 2 +- [core]/es_extended/locale.lua | 20 +++++++++++++++----- [core]/es_extended/locales/cs.lua | 8 ++++---- [core]/es_extended/locales/de.lua | 2 +- [core]/es_extended/locales/el.lua | 2 +- [core]/es_extended/locales/en.lua | 2 +- [core]/es_extended/locales/es.lua | 2 +- [core]/es_extended/locales/fi.lua | 2 +- [core]/es_extended/locales/fr.lua | 2 +- [core]/es_extended/locales/he.lua | 2 +- [core]/es_extended/locales/hu.lua | 8 ++++---- [core]/es_extended/locales/id.lua | 2 +- [core]/es_extended/locales/it.lua | 2 +- [core]/es_extended/locales/nl.lua | 2 +- [core]/es_extended/locales/pl.lua | 8 ++++---- [core]/es_extended/locales/sl.lua | 8 ++++---- [core]/es_extended/locales/sr.lua | 8 ++++---- [core]/es_extended/locales/sv.lua | 2 +- [core]/es_extended/locales/tr.lua | 16 ++++++++-------- [core]/es_extended/locales/zh-cn.lua | 8 ++++---- [core]/es_extended/shared/config/main.lua | 9 ++++----- [core]/esx_identity/locales/cs.lua | 2 +- [core]/esx_identity/locales/da.lua | 2 +- [core]/esx_identity/locales/de.lua | 4 ++-- [core]/esx_identity/locales/en.lua | 2 +- [core]/esx_identity/locales/es.lua | 2 +- [core]/esx_identity/locales/fi.lua | 2 +- [core]/esx_identity/locales/fr.lua | 2 +- [core]/esx_identity/locales/he.lua | 2 +- [core]/esx_identity/locales/hu.lua | 2 +- [core]/esx_identity/locales/it.lua | 2 +- [core]/esx_identity/locales/nl.lua | 2 +- [core]/esx_identity/locales/pl.lua | 2 +- [core]/esx_identity/locales/pt.lua | 2 +- [core]/esx_identity/locales/sl.lua | 2 +- [core]/esx_identity/locales/sr.lua | 2 +- [core]/esx_identity/locales/sv.lua | 2 +- [core]/esx_skin/locales/da.lua | 2 +- [core]/esx_skin/locales/de.lua | 12 ++++++------ [core]/esx_skin/locales/en.lua | 2 +- [core]/esx_skin/locales/es.lua | 2 +- [core]/esx_skin/locales/fi.lua | 2 +- [core]/esx_skin/locales/fr.lua | 2 +- [core]/esx_skin/locales/he.lua | 2 +- [core]/esx_skin/locales/hu.lua | 2 +- [core]/esx_skin/locales/it.lua | 2 +- [core]/esx_skin/locales/nl.lua | 2 +- [core]/esx_skin/locales/pl.lua | 2 +- [core]/esx_skin/locales/pt.lua | 2 +- [core]/esx_skin/locales/sl.lua | 2 +- [core]/esx_skin/locales/sr.lua | 2 +- [core]/esx_skin/locales/sv.lua | 2 +- [core]/esx_skin/locales/zh-cn.lua | 2 +- 53 files changed, 101 insertions(+), 92 deletions(-) diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index 6bef9655..5bcca096 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -7,7 +7,6 @@ version '1.12.2' shared_scripts { 'locale.lua', - 'locales/*.lua', 'shared/config/main.lua', 'shared/config/weapons.lua', @@ -63,6 +62,7 @@ ui_page { files { 'imports.lua', + 'locales/*.lua', 'locale.js', 'html/ui.html', diff --git a/[core]/es_extended/locale.lua b/[core]/es_extended/locale.lua index 7d940e4f..3f059b62 100644 --- a/[core]/es_extended/locale.lua +++ b/[core]/es_extended/locale.lua @@ -5,14 +5,24 @@ function Translate(str, ...) -- Translate string error(("Resource ^5%s^1 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) end + --- Load the locale file if it hasn't been loaded yet + if Locales[Config.Locale] == nil then + local success, result = pcall(function() + return assert(load(LoadResourceFile(GetCurrentResourceName(), ("locales/%s.lua"):format(Config.Locale))))() + end) + + Locales[Config.Locale] = success and result or false + end + local translations = Locales[Config.Locale] if not translations then - -- Fall back to English translation if the current locale is not found - if Locales.en and Locales.en[str] then - return Locales.en[str]:format(...) + if Config.Locale == "en" then + return "Locale [en] does not exist" end - return ("Locale [%s] does not exist"):format(Config.Locale) + -- Fall back to English translation if the current locale is not found + Config.Locale = "en" + return Translate(str, ...) end if translations[str] then @@ -28,4 +38,4 @@ end _ = Translate -- luacheck: ignore _U -_U = TranslateCap +_U = TranslateCap \ No newline at end of file diff --git a/[core]/es_extended/locales/cs.lua b/[core]/es_extended/locales/cs.lua index a9b278fd..435cd875 100644 --- a/[core]/es_extended/locales/cs.lua +++ b/[core]/es_extended/locales/cs.lua @@ -1,4 +1,4 @@ -Locales["cs"] = { +return { -- Inventory ["inventory"] = "Inventář ( Váha %s / %s )", ["use"] = "Použít", @@ -223,10 +223,10 @@ Locales["cs"] = { ["weapon_tactilerifle"] = "Service Carbine", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Thrown ["weapon_ball"] = "Míček", diff --git a/[core]/es_extended/locales/de.lua b/[core]/es_extended/locales/de.lua index 8951fdf0..3aac3dd2 100644 --- a/[core]/es_extended/locales/de.lua +++ b/[core]/es_extended/locales/de.lua @@ -1,4 +1,4 @@ -Locales["de"] = { +return { -- Inventory ["inventory"] = "Inventar ( Gewicht %s / %s )", ["use"] = "Benutzen", diff --git a/[core]/es_extended/locales/el.lua b/[core]/es_extended/locales/el.lua index ba1d1ab0..4fe64ddc 100644 --- a/[core]/es_extended/locales/el.lua +++ b/[core]/es_extended/locales/el.lua @@ -1,4 +1,4 @@ -Locales["el"] = { +return { -- Inventory ["inventory"] = "Αποθήκη ( Βάρος %s / %s )", ["use"] = "Χρήση", diff --git a/[core]/es_extended/locales/en.lua b/[core]/es_extended/locales/en.lua index 81431aec..194d2912 100644 --- a/[core]/es_extended/locales/en.lua +++ b/[core]/es_extended/locales/en.lua @@ -1,4 +1,4 @@ -Locales["en"] = { +return { -- Inventory ["inventory"] = "Inventory ( Weight %s / %s )", ["use"] = "Use", diff --git a/[core]/es_extended/locales/es.lua b/[core]/es_extended/locales/es.lua index 9d03a65d..b5bb38d3 100644 --- a/[core]/es_extended/locales/es.lua +++ b/[core]/es_extended/locales/es.lua @@ -1,4 +1,4 @@ -Locales["es"] = { +return { -- Inventory ["inventory"] = "Inventario %s / %s", ["use"] = "Usar", diff --git a/[core]/es_extended/locales/fi.lua b/[core]/es_extended/locales/fi.lua index 4d71ff48..a1ebffd2 100644 --- a/[core]/es_extended/locales/fi.lua +++ b/[core]/es_extended/locales/fi.lua @@ -1,4 +1,4 @@ -Locales["fi"] = { +return { -- Inventory ["inventory"] = "Reppu %s / %s", ["use"] = "Käytä", diff --git a/[core]/es_extended/locales/fr.lua b/[core]/es_extended/locales/fr.lua index a0a132b4..95f5f796 100644 --- a/[core]/es_extended/locales/fr.lua +++ b/[core]/es_extended/locales/fr.lua @@ -1,4 +1,4 @@ -Locales["fr"] = { +return { -- Inventory ["inventory"] = "Inventaire ( Poids %s / %s )", ["use"] = "Utiliser", diff --git a/[core]/es_extended/locales/he.lua b/[core]/es_extended/locales/he.lua index d6216573..4c8440ff 100644 --- a/[core]/es_extended/locales/he.lua +++ b/[core]/es_extended/locales/he.lua @@ -1,4 +1,4 @@ -Locales["he"] = { +return { -- Inventory ["inventory"] = "מלאי ( משקל %s / %s )", ["use"] = "השתמש", diff --git a/[core]/es_extended/locales/hu.lua b/[core]/es_extended/locales/hu.lua index 5d3b230b..e805ccf1 100644 --- a/[core]/es_extended/locales/hu.lua +++ b/[core]/es_extended/locales/hu.lua @@ -1,4 +1,4 @@ -Locales["hu"] = { +return { -- Inventory ["inventory"] = "Inventory ( Súly %s / %s )", ["use"] = "Használ", @@ -229,10 +229,10 @@ Locales["hu"] = { ["weapon_tactilerifle"] = "Service Carbine", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Thrown ["weapon_ball"] = "Baseball", diff --git a/[core]/es_extended/locales/id.lua b/[core]/es_extended/locales/id.lua index bbd2587c..1dfa8487 100644 --- a/[core]/es_extended/locales/id.lua +++ b/[core]/es_extended/locales/id.lua @@ -1,4 +1,4 @@ -Locales["id"] = { +return { -- Inventory ["inventory"] = "Inventaris ( Berat %s / %s )", ["use"] = "Gunakan", diff --git a/[core]/es_extended/locales/it.lua b/[core]/es_extended/locales/it.lua index 8a7caf59..9de0edb5 100644 --- a/[core]/es_extended/locales/it.lua +++ b/[core]/es_extended/locales/it.lua @@ -1,4 +1,4 @@ -Locales["it"] = { +return { -- Inventory ["inventory"] = "Inventario ( Peso %s / %s )", ["use"] = "Usa", diff --git a/[core]/es_extended/locales/nl.lua b/[core]/es_extended/locales/nl.lua index 2dee1716..494e0c3f 100644 --- a/[core]/es_extended/locales/nl.lua +++ b/[core]/es_extended/locales/nl.lua @@ -1,4 +1,4 @@ -Locales["nl"] = { +return { -- Inventory ["inventory"] = "Inventaris ( Gewicht %s / %s )", ["use"] = "Gebruik", diff --git a/[core]/es_extended/locales/pl.lua b/[core]/es_extended/locales/pl.lua index a9dceb4f..7535ffdd 100644 --- a/[core]/es_extended/locales/pl.lua +++ b/[core]/es_extended/locales/pl.lua @@ -1,4 +1,4 @@ -Locales["pl"] = { +return { -- Inventory ["inventory"] = "ekwipunek %s / %s", ["use"] = "użyj", @@ -202,10 +202,10 @@ Locales["pl"] = { ["component_luxary_finish"] = "luksusowe wykończenie broni", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Weapon Ammo ["ammo_rounds"] = "nabój/oi", diff --git a/[core]/es_extended/locales/sl.lua b/[core]/es_extended/locales/sl.lua index da0c9de4..3808ab28 100644 --- a/[core]/es_extended/locales/sl.lua +++ b/[core]/es_extended/locales/sl.lua @@ -1,4 +1,4 @@ -Locales["sl"] = { +return { -- Inventory ["inventory"] = "Shramba ( Teza %s / %s )", ["use"] = "Uporabi", @@ -229,10 +229,10 @@ Locales["sl"] = { ["weapon_tactilerifle"] = "Service Carbine", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Thrown ["weapon_ball"] = "Baseball", diff --git a/[core]/es_extended/locales/sr.lua b/[core]/es_extended/locales/sr.lua index 9ef51703..fb76e019 100644 --- a/[core]/es_extended/locales/sr.lua +++ b/[core]/es_extended/locales/sr.lua @@ -1,4 +1,4 @@ -Locales["sr"] = { +return { -- Inventory ["inventory"] = "Inventar ( Težina %s / %s )", ["use"] = "Koristi", @@ -229,10 +229,10 @@ Locales["sr"] = { ["weapon_tactilerifle"] = "Service Carbine", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Thrown ["weapon_ball"] = "Baseball", diff --git a/[core]/es_extended/locales/sv.lua b/[core]/es_extended/locales/sv.lua index e528baca..14e45b40 100644 --- a/[core]/es_extended/locales/sv.lua +++ b/[core]/es_extended/locales/sv.lua @@ -1,4 +1,4 @@ -Locales["sv"] = { +return { -- Inventory ["inventory"] = "Inventory ( Vikt %s / %s )", ["use"] = "Använd", diff --git a/[core]/es_extended/locales/tr.lua b/[core]/es_extended/locales/tr.lua index f12d6495..6c0f9edb 100644 --- a/[core]/es_extended/locales/tr.lua +++ b/[core]/es_extended/locales/tr.lua @@ -1,4 +1,4 @@ -Locales["tr"] = { +return { -- Inventory ["inventory"] = "Envanter ( Ağırlık %s / %s )", ["use"] = "Kullan", @@ -36,10 +36,10 @@ Locales["tr"] = { ["threw_weapon_already"] = "Bu Silaha Zaten Sahipsiniz", ["threw_cannot_pickup"] = "Envanter Dolu, Alınamaz!", ["threw_pickup_prompt"] = "Almak İçin E'ye Basın", - + -- Key mapping ["keymap_showinventory"] = "Envanteri Göster", - + -- Salary related ["received_salary"] = "Maaşınız Ödendi: $%s", ["received_help"] = "Yardım Çekiniz Ödendi: $%s", @@ -49,11 +49,11 @@ Locales["tr"] = { ["account_bank"] = "Banka", ["account_black_money"] = "Kirli Para", ["account_money"] = "Nakit", - + ["act_imp"] = "İşlem Yapılamaz", ["in_vehicle"] = "İşlem Yapılamaz, Oyuncu Araçta", ["not_in_vehicle"] = "İşlem Yapılamaz, Oyuncu Araçta Değil", - + -- Commands ["command_bring"] = "Oyuncuyu Yanınıza Getir", ["command_car"] = "Araç Spawn Et", @@ -119,14 +119,14 @@ Locales["tr"] = { ["command_giveammo_ammo"] = "Mermi Miktarı", ["tpm_nowaypoint"] = "Hiçbir Yol İşareti Ayarlanmadı.", ["tpm_success"] = "Başarıyla Teleport Edildi", - + ["noclip_message"] = "Noclip %s Yapıldı", ["enabled"] = "~g~Aktif Edildi~s~", ["disabled"] = "~r~Pasif Edildi~s~", - + -- Locale settings ["locale_digit_grouping_symbol"] = ",", - ["locale_currency"] = "£%s", + ["locale_currency"] = "£%s", -- Silahlar diff --git a/[core]/es_extended/locales/zh-cn.lua b/[core]/es_extended/locales/zh-cn.lua index 5971cc35..b49819e7 100644 --- a/[core]/es_extended/locales/zh-cn.lua +++ b/[core]/es_extended/locales/zh-cn.lua @@ -1,4 +1,4 @@ -Locales["zh-cn"] = { +return { -- Inventory ["inventory"] = "背包 %s / %s", ["use"] = "使用", @@ -229,10 +229,10 @@ Locales["zh-cn"] = { ["weapon_tactilerifle"] = "制式卡宾步枪", -- Drug Wars DLC - ["weapon_candycane"] = "Candy Cane", -- not translated + ["weapon_candycane"] = "Candy Cane", -- not translated ["weapon_acidpackage"] = "Acid Package", -- not translated - ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated - ["weapon_railgunxm3"] = "Railgun", -- not translated + ["weapon_pistolxm3"] = "WM 29 Pistol", -- not translated + ["weapon_railgunxm3"] = "Railgun", -- not translated -- Thrown ["weapon_ball"] = "棒球", diff --git a/[core]/es_extended/shared/config/main.lua b/[core]/es_extended/shared/config/main.lua index 5d37450a..108d292d 100644 --- a/[core]/es_extended/shared/config/main.lua +++ b/[core]/es_extended/shared/config/main.lua @@ -1,5 +1,9 @@ Config = {} +local txAdminLocale = GetConvar("txAdmin-locale", "en") +local esxLocale = GetConvar("esx:locale", "invalid") +Config.Locale = (esxLocale ~= "invalid") and esxLocale or (txAdminLocale ~= "custom" and txAdminLocale) or "en" + -- for ox inventory, this will automatically be adjusted, do not change! for other inventories, change to "resource_name" Config.CustomInventory = false @@ -60,8 +64,3 @@ if GetResourceState("ox_inventory") ~= "missing" then end Config.EnableDefaultInventory = Config.CustomInventory == false -- Display the default Inventory ( F2 ) - -local txAdminLocale = GetConvar("txAdmin-locale", "en") -local esxLocale = GetConvar("esx:locale", "invalid") - -Config.Locale = (esxLocale ~= "invalid") and esxLocale or (txAdminLocale ~= "custom" and txAdminLocale) or "en" diff --git a/[core]/esx_identity/locales/cs.lua b/[core]/esx_identity/locales/cs.lua index bb17bed2..1482b154 100644 --- a/[core]/esx_identity/locales/cs.lua +++ b/[core]/esx_identity/locales/cs.lua @@ -1,4 +1,4 @@ -Locales["cs"] = { +return { ["show_registration"] = "zobrazit registracni menu", ["show_active_character"] = "zobrazit aktivni postavy", ["delete_character"] = "smazat svou stavajici postavu a vytvorit novou", diff --git a/[core]/esx_identity/locales/da.lua b/[core]/esx_identity/locales/da.lua index 21c03808..f5c0c1d2 100644 --- a/[core]/esx_identity/locales/da.lua +++ b/[core]/esx_identity/locales/da.lua @@ -1,4 +1,4 @@ -Locales["da"] = { +return { ["show_active_character"] = "Vis aktiv karakter", ["active_character"] = "Aktiv karakter: %s", ["error_active_character"] = "Der opstod en fejl under indhentning af dine data.", diff --git a/[core]/esx_identity/locales/de.lua b/[core]/esx_identity/locales/de.lua index 4c6de824..b2285b00 100644 --- a/[core]/esx_identity/locales/de.lua +++ b/[core]/esx_identity/locales/de.lua @@ -1,4 +1,4 @@ -Locales["de"] = { +return { ["show_active_character"] = "Aktiven Charakter anzeigen", ["active_character"] = "Aktiver Charakter: %s", ["error_active_character"] = "Beim Abrufen deiner Daten ist ein Fehler aufgetreten", @@ -35,4 +35,4 @@ Locales["de"] = { ["invalid_dob_format"] = "Ungültiges Format (Geburtstag): Bitte versuche es erneut.", ["invalid_sex_format"] = "Ungültiges Format (Geschlecht): Bitte versuche es erneut.", ["invalid_height_format"] = "Ungültiges Format (Körpergröße): Bitte versuche es erneut.", -} \ No newline at end of file +} diff --git a/[core]/esx_identity/locales/en.lua b/[core]/esx_identity/locales/en.lua index ab3d922c..fa4737e3 100644 --- a/[core]/esx_identity/locales/en.lua +++ b/[core]/esx_identity/locales/en.lua @@ -1,4 +1,4 @@ -Locales["en"] = { +return { ["show_active_character"] = "Show Active Character", ["active_character"] = "Active Character: %s", ["error_active_character"] = "There was an error obtaining your data.", diff --git a/[core]/esx_identity/locales/es.lua b/[core]/esx_identity/locales/es.lua index d6385eac..3961d823 100644 --- a/[core]/esx_identity/locales/es.lua +++ b/[core]/esx_identity/locales/es.lua @@ -1,4 +1,4 @@ -Locales["es"] = { +return { ["show_active_character"] = "Mostrar personaje actual", ["active_character"] = "Personaje actual: %s", ["error_active_character"] = "Se produjo un error al recuperar tu nombre. Por favor contacta con un administrador", diff --git a/[core]/esx_identity/locales/fi.lua b/[core]/esx_identity/locales/fi.lua index c9d9ccb3..f2c87935 100644 --- a/[core]/esx_identity/locales/fi.lua +++ b/[core]/esx_identity/locales/fi.lua @@ -1,4 +1,4 @@ -Locales["fi"] = { +return { ["show_active_character"] = "Näytä nykyinen hahmosi", ["active_character"] = "Nykyinen hahmosi: %s", ["error_active_character"] = "Hahmosi hakemisessa ilmeni ongelma. Ota yhteyttä ylläpitoon.", diff --git a/[core]/esx_identity/locales/fr.lua b/[core]/esx_identity/locales/fr.lua index 36b9d866..4a0e007b 100644 --- a/[core]/esx_identity/locales/fr.lua +++ b/[core]/esx_identity/locales/fr.lua @@ -1,4 +1,4 @@ -Locales["fr"] = { +return { ["show_active_character"] = "Afficher le personnage actif", ["active_character"] = "Personnage actif: %s", ["error_active_character"] = "Une erreur s'est produite lors de l'obtention de vos données.", diff --git a/[core]/esx_identity/locales/he.lua b/[core]/esx_identity/locales/he.lua index 18c8a514..1205e4ec 100644 --- a/[core]/esx_identity/locales/he.lua +++ b/[core]/esx_identity/locales/he.lua @@ -1,4 +1,4 @@ -Locales["he"] = { +return { ["show_active_character"] = "הצג דמות פעילה", ["active_character"] = "דמות פעילה: %s", ["error_active_character"] = "אירעה שגיאה בקבלת הנתונים שלך.", diff --git a/[core]/esx_identity/locales/hu.lua b/[core]/esx_identity/locales/hu.lua index 0af9173d..3c08cd3c 100644 --- a/[core]/esx_identity/locales/hu.lua +++ b/[core]/esx_identity/locales/hu.lua @@ -1,4 +1,4 @@ -Locales["hu"] = { +return { ["show_active_character"] = "Aktív karakterek mutatása", ["active_character"] = "Aktív karakter: %s", ["error_active_character"] = "A név nem megfelelő", diff --git a/[core]/esx_identity/locales/it.lua b/[core]/esx_identity/locales/it.lua index 41ce44e3..6bdca479 100644 --- a/[core]/esx_identity/locales/it.lua +++ b/[core]/esx_identity/locales/it.lua @@ -1,4 +1,4 @@ -Locales["it"] = { +return { ["show_active_character"] = "Mostra Personaggio Attivo", ["active_character"] = "Personaggio Attivo: %s", ["error_active_character"] = "Errore nel recuperare i tuoi dati.", diff --git a/[core]/esx_identity/locales/nl.lua b/[core]/esx_identity/locales/nl.lua index 6cd862c6..a5bd0082 100644 --- a/[core]/esx_identity/locales/nl.lua +++ b/[core]/esx_identity/locales/nl.lua @@ -1,4 +1,4 @@ -Locales["nl"] = { +return { ["show_active_character"] = "Actieve karakter laten zien", ["active_character"] = "Actief karakter: %s", ["error_active_character"] = "Er is een probleem opgetreden tijdens het verzamelen van uw data.", diff --git a/[core]/esx_identity/locales/pl.lua b/[core]/esx_identity/locales/pl.lua index d10a7968..7f6551cb 100644 --- a/[core]/esx_identity/locales/pl.lua +++ b/[core]/esx_identity/locales/pl.lua @@ -1,4 +1,4 @@ -Locales["pl"] = { +return { ["show_active_character"] = "Pokaż aktywną postać", ["active_character"] = "Aktywna postać: %s", ["error_active_character"] = "Podczas pobierania Twojego imienia wystąpił błąd. Proszę skontaktować się z administratorem.", diff --git a/[core]/esx_identity/locales/pt.lua b/[core]/esx_identity/locales/pt.lua index 52088df8..1d78f10d 100644 --- a/[core]/esx_identity/locales/pt.lua +++ b/[core]/esx_identity/locales/pt.lua @@ -1,4 +1,4 @@ -Locales["pt"] = { +return { ["show_active_character"] = "Mostrar personagem ativa", ["active_character"] = "Personagem ativa: %s", ["error_active_character"] = "Houve um erro a obter os teus dados.", diff --git a/[core]/esx_identity/locales/sl.lua b/[core]/esx_identity/locales/sl.lua index 1d93aa7b..cf4ad1e9 100644 --- a/[core]/esx_identity/locales/sl.lua +++ b/[core]/esx_identity/locales/sl.lua @@ -1,4 +1,4 @@ -Locales["sl"] = { +return { ["show_active_character"] = "Prikaži aktivnega lika", ["active_character"] = "Aktivni lik: %s", ["error_active_character"] = "Prišlo je do napake pri pridobivanju podatkov.", diff --git a/[core]/esx_identity/locales/sr.lua b/[core]/esx_identity/locales/sr.lua index 8cb37a22..f34819e7 100644 --- a/[core]/esx_identity/locales/sr.lua +++ b/[core]/esx_identity/locales/sr.lua @@ -1,4 +1,4 @@ -Locales["sr"] = { +return { ["show_active_character"] = "Prikazi karaktere", ["active_character"] = "Karakteri: %s", ["error_active_character"] = "Imamo problem sa ucitavanjem karaktera.", diff --git a/[core]/esx_identity/locales/sv.lua b/[core]/esx_identity/locales/sv.lua index 6c291d34..c298c85b 100644 --- a/[core]/esx_identity/locales/sv.lua +++ b/[core]/esx_identity/locales/sv.lua @@ -1,4 +1,4 @@ -Locales["sv"] = { +return { ["show_active_character"] = "Visa aktiv karaktär", ["active_character"] = "Aktiv karaktär: %s", ["error_active_character"] = "Det blev ett fel med att ta din data.", diff --git a/[core]/esx_skin/locales/da.lua b/[core]/esx_skin/locales/da.lua index 4aee2f79..b799e3f8 100644 --- a/[core]/esx_skin/locales/da.lua +++ b/[core]/esx_skin/locales/da.lua @@ -1,4 +1,4 @@ -Locales["da"] = { +return { ["skin_menu"] = "Udseende Menu", ["use_rotate_view"] = "brug Q og E for at dreje kameraet.", ["skin"] = "skift udseende", diff --git a/[core]/esx_skin/locales/de.lua b/[core]/esx_skin/locales/de.lua index d92041a1..efc46293 100644 --- a/[core]/esx_skin/locales/de.lua +++ b/[core]/esx_skin/locales/de.lua @@ -1,6 +1,6 @@ -Locales["de"] = { - ["skin_menu"] = "Skin Menü", - ["use_rotate_view"] = "Drücke Q oder E um deine Ansicht zu ändern.", - ["skin"] = "Aussehen ändern", - ["saveskin"] = "Aussehen abspeichern", - } +return { + ["skin_menu"] = "Skin Menü", + ["use_rotate_view"] = "Drücke Q oder E um deine Ansicht zu ändern.", + ["skin"] = "Aussehen ändern", + ["saveskin"] = "Aussehen abspeichern", +} diff --git a/[core]/esx_skin/locales/en.lua b/[core]/esx_skin/locales/en.lua index afc9581a..a7a52162 100644 --- a/[core]/esx_skin/locales/en.lua +++ b/[core]/esx_skin/locales/en.lua @@ -1,4 +1,4 @@ -Locales["en"] = { +return { ["skin_menu"] = "Skin Menu", ["use_rotate_view"] = "use Q and E to rotate the view.", ["skin"] = "change skin", diff --git a/[core]/esx_skin/locales/es.lua b/[core]/esx_skin/locales/es.lua index 735c78fe..a34840ac 100644 --- a/[core]/esx_skin/locales/es.lua +++ b/[core]/esx_skin/locales/es.lua @@ -1,4 +1,4 @@ -Locales["es"] = { +return { ["skin_menu"] = "Menú de apariencia", ["use_rotate_view"] = "Utiliza Q y E para rotar la vista.", ["skin"] = "Cambiar aspecto", diff --git a/[core]/esx_skin/locales/fi.lua b/[core]/esx_skin/locales/fi.lua index 7747dcc0..b6b342a1 100644 --- a/[core]/esx_skin/locales/fi.lua +++ b/[core]/esx_skin/locales/fi.lua @@ -1,4 +1,4 @@ -Locales["fi"] = { +return { ["skin_menu"] = "Ulkonäkö", ["use_rotate_view"] = "Paina Q tai E liikutaaksesi kameraa.", ["skin"] = "Muokkaa ulkonäköä", diff --git a/[core]/esx_skin/locales/fr.lua b/[core]/esx_skin/locales/fr.lua index 44d3d4b2..7f5a5f85 100644 --- a/[core]/esx_skin/locales/fr.lua +++ b/[core]/esx_skin/locales/fr.lua @@ -1,4 +1,4 @@ -Locales["fr"] = { +return { ["skin_menu"] = "menu de Skin", ["use_rotate_view"] = "utilisez Q et E pour tourner la vue.", ["skin"] = "changer de skin", diff --git a/[core]/esx_skin/locales/he.lua b/[core]/esx_skin/locales/he.lua index 6f750e07..6b24b452 100644 --- a/[core]/esx_skin/locales/he.lua +++ b/[core]/esx_skin/locales/he.lua @@ -1,4 +1,4 @@ -Locales["he"] = { +return { ["skin_menu"] = "תפריט עור", ["use_rotate_view"] = "השתמש Q ו E כדי לסובב את התצוגה.", ["skin"] = "שנה עור", diff --git a/[core]/esx_skin/locales/hu.lua b/[core]/esx_skin/locales/hu.lua index a83ea999..aa44c251 100644 --- a/[core]/esx_skin/locales/hu.lua +++ b/[core]/esx_skin/locales/hu.lua @@ -1,4 +1,4 @@ -Locales["hu"] = { +return { ["skin_menu"] = "Kinézet Menü", ["use_rotate_view"] = "Használd Q vagy E gombokat a forgatáshoz", ["skin"] = "kinézet változtatása", diff --git a/[core]/esx_skin/locales/it.lua b/[core]/esx_skin/locales/it.lua index c6605e97..7d20e777 100644 --- a/[core]/esx_skin/locales/it.lua +++ b/[core]/esx_skin/locales/it.lua @@ -1,4 +1,4 @@ -Locales["it"] = { +return { ["skin_menu"] = "Menu Skin", ["use_rotate_view"] = "usa Q e E per ruotare la visuale.", ["skin"] = "cambia skin", diff --git a/[core]/esx_skin/locales/nl.lua b/[core]/esx_skin/locales/nl.lua index 1ec2b1d2..bf8c0543 100644 --- a/[core]/esx_skin/locales/nl.lua +++ b/[core]/esx_skin/locales/nl.lua @@ -1,4 +1,4 @@ -Locales["nl"] = { +return { ["skin_menu"] = "Kleding Menu", ["use_rotate_view"] = "gebruik Q en E om de camera te draaien.", ["skin"] = "verander outfit", diff --git a/[core]/esx_skin/locales/pl.lua b/[core]/esx_skin/locales/pl.lua index c12fe139..0c3a33a0 100644 --- a/[core]/esx_skin/locales/pl.lua +++ b/[core]/esx_skin/locales/pl.lua @@ -1,4 +1,4 @@ -Locales["pl"] = { +return { ["skin_menu"] = "menu wyglądu", ["use_rotate_view"] = "użyj Q i E aby obrócić ekran.", ["skin"] = "zmień wygląd", diff --git a/[core]/esx_skin/locales/pt.lua b/[core]/esx_skin/locales/pt.lua index 0109a66a..f22e2fae 100644 --- a/[core]/esx_skin/locales/pt.lua +++ b/[core]/esx_skin/locales/pt.lua @@ -1,4 +1,4 @@ -Locales["pt"] = { +return { ["skin_menu"] = "Menu de Skin", ["use_rotate_view"] = "Usa Q e E para rodar a câmara.", ["skin"] = "alterar skin", diff --git a/[core]/esx_skin/locales/sl.lua b/[core]/esx_skin/locales/sl.lua index d2f3571c..04ea3c3b 100644 --- a/[core]/esx_skin/locales/sl.lua +++ b/[core]/esx_skin/locales/sl.lua @@ -1,4 +1,4 @@ -Locales["sl"] = { +return { ["skin_menu"] = "Oblacilni menu.", ["use_rotate_view"] = "Pritisni Q in E da se obracas s pogledom.", ["skin"] = "Zamenjaj Skin.", diff --git a/[core]/esx_skin/locales/sr.lua b/[core]/esx_skin/locales/sr.lua index 9d6ee01d..03c55e47 100644 --- a/[core]/esx_skin/locales/sr.lua +++ b/[core]/esx_skin/locales/sr.lua @@ -1,4 +1,4 @@ -Locales["sr"] = { +return { ["skin_menu"] = "Skin Meni", ["use_rotate_view"] = "koristi Q i E da rotiras kameru.", ["skin"] = "promeni skin", diff --git a/[core]/esx_skin/locales/sv.lua b/[core]/esx_skin/locales/sv.lua index 1e14369a..bf7b4d2d 100644 --- a/[core]/esx_skin/locales/sv.lua +++ b/[core]/esx_skin/locales/sv.lua @@ -1,4 +1,4 @@ -Locales["sv"] = { +return { ["skin_menu"] = "Skin Meny", ["use_rotate_view"] = "Använd Q och E för att rotera.", ["skin"] = "Ända utseende", diff --git a/[core]/esx_skin/locales/zh-cn.lua b/[core]/esx_skin/locales/zh-cn.lua index c0402d43..52cc2339 100644 --- a/[core]/esx_skin/locales/zh-cn.lua +++ b/[core]/esx_skin/locales/zh-cn.lua @@ -1,4 +1,4 @@ -Locales["zh-cn"] = { +return { ["skin_menu"] = "皮肤选单", ["use_rotate_view"] = "使用 Q 和 E 旋转镜头视角.", ["skin"] = "更换皮肤数据", From b5edb1a746200d5252a22b8c14a7effdc43f96b0 Mon Sep 17 00:00:00 2001 From: fnbar <66674667+fnbar0@users.noreply.github.com> Date: Sun, 19 Jan 2025 15:35:42 +0100 Subject: [PATCH 030/132] Make scaleforms show duration frame independent --- .../es_extended/client/modules/scaleform.lua | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/[core]/es_extended/client/modules/scaleform.lua b/[core]/es_extended/client/modules/scaleform.lua index 31d0897e..1f807acb 100644 --- a/[core]/es_extended/client/modules/scaleform.lua +++ b/[core]/es_extended/client/modules/scaleform.lua @@ -3,11 +3,10 @@ ESX.Scaleform.Utils = {} function ESX.Scaleform.ShowFreemodeMessage(title, msg, sec) local scaleform = ESX.Scaleform.Utils.RunMethod("MP_BIG_MESSAGE_FREEMODE", "SHOW_SHARD_WASTED_MP_MESSAGE", false, title, msg) - - while sec > 0 do + + local startTimer = GetGameTimer() + while GetGameTimer() - startTimer < sec * 1000 do Wait(0) - sec = sec - 0.01 - DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -19,10 +18,9 @@ function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) ESX.Scaleform.Utils.RunMethod(scaleform, "SET_SCROLL_TEXT", false, 0, 0, title) ESX.Scaleform.Utils.RunMethod(scaleform, "DISPLAY_SCROLL_TEXT", false, 0, 0) - while sec > 0 do + local startTimer = GetGameTimer() + while GetGameTimer() - startTimer < sec * 1000 do Wait(0) - sec = sec - 0.01 - DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -32,10 +30,9 @@ end function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) local scaleform = ESX.Scaleform.Utils.RunMethod("POPUP_WARNING", "SHOW_POPUP_WARNING", false, 500.0, title, msg, bottom, true) - while sec > 0 do + local startTimer = GetGameTimer() + while GetGameTimer() - startTimer < sec * 1000 do Wait(0) - sec = sec - 0.01 - DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -45,10 +42,9 @@ end function ESX.Scaleform.ShowTrafficMovie(sec) local scaleform = ESX.Scaleform.Utils.RunMethod("TRAFFIC_CAM", "PLAY_CAM_MOVIE", false) - while sec > 0 do + local startTimer = GetGameTimer() + while GetGameTimer() - startTimer < sec * 1000 do Wait(0) - sec = sec - 0.01 - DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end From bdb74d05e900951ea429ea214a0889334f1236f2 Mon Sep 17 00:00:00 2001 From: fnbar <66674667+fnbar0@users.noreply.github.com> Date: Sun, 19 Jan 2025 16:39:55 +0100 Subject: [PATCH 031/132] Remove unnecessary calculations in loop --- [core]/es_extended/client/modules/scaleform.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/[core]/es_extended/client/modules/scaleform.lua b/[core]/es_extended/client/modules/scaleform.lua index 1f807acb..16d4752d 100644 --- a/[core]/es_extended/client/modules/scaleform.lua +++ b/[core]/es_extended/client/modules/scaleform.lua @@ -4,8 +4,8 @@ ESX.Scaleform.Utils = {} function ESX.Scaleform.ShowFreemodeMessage(title, msg, sec) local scaleform = ESX.Scaleform.Utils.RunMethod("MP_BIG_MESSAGE_FREEMODE", "SHOW_SHARD_WASTED_MP_MESSAGE", false, title, msg) - local startTimer = GetGameTimer() - while GetGameTimer() - startTimer < sec * 1000 do + local endTime = GetGameTimer() + (sec * 1000) + while GetGameTimer() < endTime do Wait(0) DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -18,8 +18,8 @@ function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) ESX.Scaleform.Utils.RunMethod(scaleform, "SET_SCROLL_TEXT", false, 0, 0, title) ESX.Scaleform.Utils.RunMethod(scaleform, "DISPLAY_SCROLL_TEXT", false, 0, 0) - local startTimer = GetGameTimer() - while GetGameTimer() - startTimer < sec * 1000 do + local endTime = GetGameTimer() + (sec * 1000) + while GetGameTimer() < endTime do Wait(0) DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -30,8 +30,8 @@ end function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) local scaleform = ESX.Scaleform.Utils.RunMethod("POPUP_WARNING", "SHOW_POPUP_WARNING", false, 500.0, title, msg, bottom, true) - local startTimer = GetGameTimer() - while GetGameTimer() - startTimer < sec * 1000 do + local endTime = GetGameTimer() + (sec * 1000) + while GetGameTimer() < endTime do Wait(0) DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end @@ -42,8 +42,8 @@ end function ESX.Scaleform.ShowTrafficMovie(sec) local scaleform = ESX.Scaleform.Utils.RunMethod("TRAFFIC_CAM", "PLAY_CAM_MOVIE", false) - local startTimer = GetGameTimer() - while GetGameTimer() - startTimer < sec * 1000 do + local endTime = GetGameTimer() + (sec * 1000) + while GetGameTimer() < endTime do Wait(0) DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) end From df287b041b1ed84e24e1ae4dc095aed3dc9c9592 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:12:21 +0100 Subject: [PATCH 032/132] feat(es_extended): add static player methods --- [core]/es_extended/imports.lua | 13 +++++++++++++ [core]/es_extended/server/classes/player.lua | 10 ++++++++++ [core]/es_extended/server/functions.lua | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/[core]/es_extended/imports.lua b/[core]/es_extended/imports.lua index 719d0623..bf5b9bee 100644 --- a/[core]/es_extended/imports.lua +++ b/[core]/es_extended/imports.lua @@ -42,6 +42,19 @@ if not IsDuplicityVersion() then -- Only register this event for the client return error(('\n^1Error loading module (%s)'):format(external[i])) end end +else + ESX.Player = setmetatable({}, { + __call = function(_, src) + if not ESX.IsPlayerLoaded(src) then return end + return setmetatable({src = src}, { + __index = function(self, method) + return function(...) + return exports.es_extended:RunStaticPlayerMethod(self.src, method, ...) + end + end + }) + end + }) end if GetResourceState("ox_lib") == "missing" then diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index b5e66e7b..d82cd695 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -944,3 +944,13 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, return self end + +local function runStaticPlayerMethod(src, method, ...) + local xPlayer = ESX.Players[src] + if not xPlayer then + return + end + + return xPlayer[method](...) +end +exports("RunStaticPlayerMethod", runStaticPlayerMethod) \ No newline at end of file diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 0ab74f2f..6a27581a 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -358,6 +358,12 @@ function ESX.GetPlayerFromIdentifier(identifier) return Core.playersByIdentifier[identifier] end +---@param source number +---@return boolean +function ESX.IsPlayerLoaded(source) + return ESX.Players[source] ~= nil +end + ---@param playerId number | string ---@return string function ESX.GetIdentifier(playerId) From 4c9a7d29180d084f4dfb55748d1adbb14d6ee9f2 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:24:20 +0100 Subject: [PATCH 033/132] feat(es_extended/imports): add support for resolving player identifier --- [core]/es_extended/imports.lua | 8 +++++++- [core]/es_extended/server/functions.lua | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/imports.lua b/[core]/es_extended/imports.lua index bf5b9bee..3587951c 100644 --- a/[core]/es_extended/imports.lua +++ b/[core]/es_extended/imports.lua @@ -45,7 +45,13 @@ if not IsDuplicityVersion() then -- Only register this event for the client else ESX.Player = setmetatable({}, { __call = function(_, src) - if not ESX.IsPlayerLoaded(src) then return end + if type(src) ~= "number" then + src = ESX.GetPlayerIdFromIdentifier(src) + if not src then return end + elseif not ESX.IsPlayerLoaded(src) then + return + end + return setmetatable({src = src}, { __index = function(self, method) return function(...) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 6a27581a..d7900e46 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -358,6 +358,12 @@ function ESX.GetPlayerFromIdentifier(identifier) return Core.playersByIdentifier[identifier] end +---@param identifier string +---@return number playerId +function ESX.GetPlayerIdFromIdentifier(identifier) + return Core.playersByIdentifier[identifier]?.source +end + ---@param source number ---@return boolean function ESX.IsPlayerLoaded(source) From 54e0da8b33a05225b5da178b43a5556c67ac2599 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:24:45 +0100 Subject: [PATCH 034/132] refactor(es_extended/server/classes/player): add error msg for invalid functions --- [core]/es_extended/server/classes/player.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index d82cd695..ed99efb6 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -951,6 +951,10 @@ local function runStaticPlayerMethod(src, method, ...) return end + if not ESX.IsFunctionReference(xPlayer[method]) then + error(("Attempted to call invalid method on playerId %s: %s"):format(src, method)) + end + return xPlayer[method](...) end exports("RunStaticPlayerMethod", runStaticPlayerMethod) \ No newline at end of file From cd96d75ed9c0900a97d1421752fc009e1cacc9d7 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:28:10 +0100 Subject: [PATCH 035/132] refactor(es_extended/imports): fix styling --- [core]/es_extended/imports.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/imports.lua b/[core]/es_extended/imports.lua index 3587951c..9bd1d4ff 100644 --- a/[core]/es_extended/imports.lua +++ b/[core]/es_extended/imports.lua @@ -47,7 +47,9 @@ else __call = function(_, src) if type(src) ~= "number" then src = ESX.GetPlayerIdFromIdentifier(src) - if not src then return end + if not src then + return + end elseif not ESX.IsPlayerLoaded(src) then return end From 7d9a64a524427c00f5865175e2a600d025dac094 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:30:28 +0100 Subject: [PATCH 036/132] feat(es_extended/server/classes/player): add getter for player source --- [core]/es_extended/server/classes/player.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index ed99efb6..376316c6 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -455,6 +455,11 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, return self.weight end + ---@return number + function self.getSource() + return self.source + end + ---@return number function self.getMaxWeight() return self.maxWeight From 1ad52fa3e2d0196e1d798341e8915678f4071084 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:33:11 +0100 Subject: [PATCH 037/132] feat(es_extended/server/classes/player): add isAdmin getter --- [core]/es_extended/server/classes/player.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index 376316c6..ec2e04d3 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -91,6 +91,11 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, return self.paycheckEnabled end + ---@return boolean + function self.isAdmin() + return Core.IsPlayerAdmin(self.source) + end + ---@param coordinates vector4 | vector3 | table ---@return nil function self.setCoords(coordinates) From 66bf71c254411cec0fa439dfa7ab34d9231da81e Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:35:21 +0100 Subject: [PATCH 038/132] feat(es_extended/server/classes/player): add getter for playerId --- [core]/es_extended/server/classes/player.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index ec2e04d3..b4682df7 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -464,6 +464,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.getSource() return self.source end + self.getPlayerId = self.getSource ---@return number function self.getMaxWeight() From db5128fcb6184e95a2f8f5752220cfb894ef4356 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:25:20 +0100 Subject: [PATCH 039/132] fix(es_extended/server/functions): fix admin logs --- [core]/es_extended/server/functions.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index d7900e46..8366c069 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -445,6 +445,11 @@ end ---@param fields table ---@return nil function ESX.DiscordLogFields(name, title, color, fields) + for i = 1, #fields do + local field = fields[i] + field.value = tostring(field.value) + end + local webHook = Config.DiscordLogs.Webhooks[name] or Config.DiscordLogs.Webhooks.default local embedData = { { From 34a5c00b5b241b8bf1298517d9e56e44c0c7a1fe Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:03:24 +0100 Subject: [PATCH 040/132] fix(esx_multicharacter/server/modules/database): fix conn string parser --- .../esx_multicharacter/server/modules/database.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/database.lua b/[core]/esx_multicharacter/server/modules/database.lua index 0f3d7a3e..4bf4ee33 100644 --- a/[core]/esx_multicharacter/server/modules/database.lua +++ b/[core]/esx_multicharacter/server/modules/database.lua @@ -14,12 +14,12 @@ function Database:GetConnection() self.name = connectionString:sub(connectionString:find("/") + 1, -1):gsub("[%?]+[%w%p]*$", "") self.found = true else - local connectionExtracted = { string.strsplit(";", connectionString) } - - for i = 1, #connectionExtracted do - local v = connectionExtracted[i] - if v:match("database") then - self.name = connectionString:sub(connectionString:find("/") + 1, -1):gsub("[%?]+[%w%p]*$", "") + local confPairs = { string.strsplit(";", connectionString) } + for i = 1, #confPairs do + local confPair = confPairs[i] + local key, value = confPair:match("^%s*(.-)%s*=%s*(.-)%s*$") + if key == "database" then + self.name = value self.found = true break end From 6b4def278e8141fcc7eafde66b38bddf4707ecb0 Mon Sep 17 00:00:00 2001 From: Kasey Fitton Date: Fri, 7 Feb 2025 11:51:51 +0000 Subject: [PATCH 041/132] tweak(es_extended/shared/main): remove deprication message --- [core]/es_extended/shared/main.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/[core]/es_extended/shared/main.lua b/[core]/es_extended/shared/main.lua index 5d659daa..8c76a0ac 100644 --- a/[core]/es_extended/shared/main.lua +++ b/[core]/es_extended/shared/main.lua @@ -9,7 +9,6 @@ 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://docs.esx-legacy.com/tutorials/tutorials-esx/sharedevent to find out why."):format(invokingResource)) end) -- backwards compatibility (DO NOT TOUCH !) From 901f16fb7ba61d5e9389ad8f5d1dee7fc97b6321 Mon Sep 17 00:00:00 2001 From: Kasey Fitton Date: Fri, 7 Feb 2025 13:52:31 +0000 Subject: [PATCH 042/132] fix(es_extended/shared/main): remove unused variable --- [core]/es_extended/shared/main.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/[core]/es_extended/shared/main.lua b/[core]/es_extended/shared/main.lua index 8c76a0ac..432cf03d 100644 --- a/[core]/es_extended/shared/main.lua +++ b/[core]/es_extended/shared/main.lua @@ -8,7 +8,6 @@ AddEventHandler("esx:getSharedObject", function(cb) if ESX.IsFunctionReference(cb) then cb(ESX) end - local invokingResource = GetInvokingResource() end) -- backwards compatibility (DO NOT TOUCH !) From 262ae32a8b0c984a5ec5b87a5bccf5fd0090d166 Mon Sep 17 00:00:00 2001 From: t1ger-scripts <67964693+t1ger-scripts@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:35:01 +0100 Subject: [PATCH 043/132] fix: missing attributes for unemployed Added missing attributes for unemployed job if no Jobs found --- [core]/es_extended/server/functions.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 8366c069..d268681e 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -512,7 +512,7 @@ function ESX.RefreshJobs() if not Jobs then -- Fallback data, if no jobs exist - ESX.Jobs["unemployed"] = { label = "Unemployed", grades = { ["0"] = { grade = 0, label = "Unemployed", salary = 200, skin_male = {}, skin_female = {} } } } + ESX.Jobs["unemployed"] = { name = "unemployed", label = "Unemployed", grades = { ["0"] = { grade = 0, name = "unemployed", label = "Unemployed", salary = 200, skin_male = {}, skin_female = {} } } } else ESX.Jobs = Jobs end @@ -650,4 +650,4 @@ end ---@return CExtendedVehicle? function ESX.GetExtendedVehicleFromPlate(plate) return Core.vehicleClass.getFromPlate(plate) -end \ No newline at end of file +end From 6d7831b0d0cad47ae6a7714fed20d3ea4f214c0c Mon Sep 17 00:00:00 2001 From: t1ger-scripts <67964693+t1ger-scripts@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:56:59 +0100 Subject: [PATCH 044/132] fix(es_extended/server/functions): whitelisted attribute for unemployed Added the missing whitelisted attribute for defaulted unemployed job. --- [core]/es_extended/server/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index d268681e..dd7c1408 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -512,7 +512,7 @@ function ESX.RefreshJobs() if not Jobs then -- Fallback data, if no jobs exist - ESX.Jobs["unemployed"] = { name = "unemployed", label = "Unemployed", grades = { ["0"] = { grade = 0, name = "unemployed", label = "Unemployed", salary = 200, skin_male = {}, skin_female = {} } } } + ESX.Jobs["unemployed"] = { name = "unemployed", label = "Unemployed", whitelisted = false, grades = { ["0"] = { grade = 0, name = "unemployed", label = "Unemployed", salary = 200, skin_male = {}, skin_female = {} } } } else ESX.Jobs = Jobs end From 364dfc03c6f1185362eaa5b03cc6061abc3221ae Mon Sep 17 00:00:00 2001 From: t1ger-scripts <67964693+t1ger-scripts@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:02:44 +0100 Subject: [PATCH 045/132] fix(server/modules/createJob): job grades not inserting The queries assume that the parsed grades are numerically indexed. But removing ipairs method, and using pairs method, allows to loop grades that are indexed with strings as well. Just like we are using pairs method inside generatenewJobTable --- [core]/es_extended/server/modules/createJob.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/modules/createJob.lua b/[core]/es_extended/server/modules/createJob.lua index c4c09eff..e6f47782 100644 --- a/[core]/es_extended/server/modules/createJob.lua +++ b/[core]/es_extended/server/modules/createJob.lua @@ -72,10 +72,10 @@ function ESX.CreateJob(name, label, grades) { query = 'INSERT INTO jobs (name, label) VALUES (?, ?)', values = { name, label } } } - for _, grade in ipairs(grades) do + for _, grade in pairs(grades) do queries[#queries + 1] = { query = 'INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES (?, ?, ?, ?, ?, ?, ?)', - values = { name, grade.grade, grade.name, grade.label, grade.salary, '{}', '{}' } + values = { name, grade.grade, grade.name, grade.label, grade.salary, json.encode(grade.skin_male) or '{}', json.encode(grade.skin_female) or '{}' } } end From 5ddd20080222cd809451084aa46af506aa050195 Mon Sep 17 00:00:00 2001 From: t1ger-scripts <67964693+t1ger-scripts@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:36:53 +0100 Subject: [PATCH 046/132] fix(server/modules/createJob): skin nil check before json encode Added checks for skin_male and skin_female before json encoding to prevent 'NULL' entries in database --- [core]/es_extended/server/modules/createJob.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/modules/createJob.lua b/[core]/es_extended/server/modules/createJob.lua index e6f47782..d2079512 100644 --- a/[core]/es_extended/server/modules/createJob.lua +++ b/[core]/es_extended/server/modules/createJob.lua @@ -75,7 +75,7 @@ function ESX.CreateJob(name, label, grades) for _, grade in pairs(grades) do queries[#queries + 1] = { query = 'INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES (?, ?, ?, ?, ?, ?, ?)', - values = { name, grade.grade, grade.name, grade.label, grade.salary, json.encode(grade.skin_male) or '{}', json.encode(grade.skin_female) or '{}' } + values = { name, grade.grade, grade.name, grade.label, grade.salary, grade.skin_male and json.encode(grade.skin_male) or '{}', grade.skin_female and json.encode(grade.skin_female) or '{}' } } end From b66909c33d3248e4a5c48a02ac6211091e37f677 Mon Sep 17 00:00:00 2001 From: t1ger-scripts <67964693+t1ger-scripts@users.noreply.github.com> Date: Tue, 18 Feb 2025 22:28:02 +0100 Subject: [PATCH 047/132] feat(modules/createJob): triggerevent for created jobs Added a TriggerEvent when a job is created successfully, allowing developers to use an event listener and update ESX.Jobs, instead of importing the whole ESX object. --- [core]/es_extended/server/modules/createJob.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/[core]/es_extended/server/modules/createJob.lua b/[core]/es_extended/server/modules/createJob.lua index d2079512..35d6daab 100644 --- a/[core]/es_extended/server/modules/createJob.lua +++ b/[core]/es_extended/server/modules/createJob.lua @@ -90,5 +90,7 @@ function ESX.CreateJob(name, label, grades) notify("SUCCESS", currentResourceName, 'Job created successfully: `%s`', name) + TriggerEvent('esx:jobCreated', name, ESX.Jobs[name]) + return success end From 3dae29d7dc49f14c29248949152dc47d641ac8aa Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:44:09 -0500 Subject: [PATCH 048/132] Fix ox crash --- [core]/es_extended/server/modules/commands.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 628e85ea..a040f44d 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -204,9 +204,13 @@ ESX.RegisterCommand( "setaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.setAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Set Account Money /setaccountmoney Triggered!", "pink", { @@ -234,9 +238,13 @@ ESX.RegisterCommand( "giveaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Account Money /giveaccountmoney Triggered!", "pink", { From d9fe2d8051c82c4769b7b66502f969afd8be9d4a Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:45:27 -0500 Subject: [PATCH 049/132] Fix oc crash and the impossibility to give anymore money --- [core]/es_extended/server/modules/commands.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index a040f44d..44cd4699 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -272,9 +272,13 @@ ESX.RegisterCommand( "removeaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_removeaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.removeAccountMoney(args.account, args.amount, "Government Tax") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Remove Account Money /removeaccountmoney Triggered!", "pink", { From 5e91bad5c4b61b37675c4752f39fa8da1e7c3b52 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:47:32 -0500 Subject: [PATCH 050/132] feat(commands): add maximum amount validation for giveitem, giveweapon, and giveammo commands --- [core]/es_extended/server/modules/commands.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 44cd4699..98c9fff6 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -307,6 +307,10 @@ if not Config.CustomInventory then "giveitem", "admin", function(xPlayer, args) + local MAX_AMOUNT = 1.79769e+308 + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addInventoryItem(args.item, args.count) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Item /giveitem Triggered!", "pink", { @@ -334,9 +338,13 @@ if not Config.CustomInventory then "giveweapon", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveweapon_hasalready")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addWeapon(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Weapon /giveweapon Triggered!", "pink", { @@ -364,9 +372,13 @@ if not Config.CustomInventory then "giveammo", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveammo_noweapon_found")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addWeaponAmmo(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Ammunition /giveammo Triggered!", "pink", { From 221e23b2b7289701f10ff85f297447728c4ce4d5 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:44:09 -0500 Subject: [PATCH 051/132] feat(commands): prevent Ox from breaking due to money overflow --- [core]/es_extended/server/modules/commands.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 628e85ea..a040f44d 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -204,9 +204,13 @@ ESX.RegisterCommand( "setaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.setAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Set Account Money /setaccountmoney Triggered!", "pink", { @@ -234,9 +238,13 @@ ESX.RegisterCommand( "giveaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Account Money /giveaccountmoney Triggered!", "pink", { From 91e783c9e96eca907151858e805b4b5337b81b82 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:45:27 -0500 Subject: [PATCH 052/132] feat(commands): prevent money overflow from blocking further transactions --- [core]/es_extended/server/modules/commands.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index a040f44d..44cd4699 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -272,9 +272,13 @@ ESX.RegisterCommand( "removeaccountmoney", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_removeaccountmoney_invalid")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.removeAccountMoney(args.account, args.amount, "Government Tax") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Remove Account Money /removeaccountmoney Triggered!", "pink", { From a0389d7e511caf3e5c861685f743639968a3b3d3 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 15:47:32 -0500 Subject: [PATCH 053/132] feat(commands): add max amount validation for giveitem, giveweapon, and giveammo --- [core]/es_extended/server/modules/commands.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 44cd4699..98c9fff6 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -307,6 +307,10 @@ if not Config.CustomInventory then "giveitem", "admin", function(xPlayer, args) + local MAX_AMOUNT = 1.79769e+308 + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addInventoryItem(args.item, args.count) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Item /giveitem Triggered!", "pink", { @@ -334,9 +338,13 @@ if not Config.CustomInventory then "giveweapon", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveweapon_hasalready")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addWeapon(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Weapon /giveweapon Triggered!", "pink", { @@ -364,9 +372,13 @@ if not Config.CustomInventory then "giveammo", "admin", function(xPlayer, args, showError) + local MAX_AMOUNT = 1.79769e+308 if not args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveammo_noweapon_found")) end + if args.amount > MAX_AMOUNT then + return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + end args.playerId.addWeaponAmmo(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Ammunition /giveammo Triggered!", "pink", { From b60eb3b43cac288f1a0894c05609bc8c181d5fdb Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 17:05:23 -0500 Subject: [PATCH 054/132] feat(commands): rename amount to count for giveitem and update ammo validation for giveweapon and giveammo --- [core]/es_extended/server/modules/commands.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 98c9fff6..5216b2f3 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -307,9 +307,9 @@ if not Config.CustomInventory then "giveitem", "admin", function(xPlayer, args) - local MAX_AMOUNT = 1.79769e+308 - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + local MAX_COUNT = 1.79769e+308 + if args.count > MAX_COUNT then + return showError(("Count must be between 1 and %s"):format(MAX_COUNT)) end args.playerId.addInventoryItem(args.item, args.count) if Config.AdminLogging then @@ -338,12 +338,12 @@ if not Config.CustomInventory then "giveweapon", "admin", function(xPlayer, args, showError) - local MAX_AMOUNT = 1.79769e+308 + local MAX_AMMO = 1.79769e+308 if args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveweapon_hasalready")) end - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + if args.ammo > MAX_AMMO then + return showError(("Ammo must be between 1 and %s"):format(MAX_AMMO)) end args.playerId.addWeapon(args.weapon, args.ammo) if Config.AdminLogging then @@ -372,12 +372,12 @@ if not Config.CustomInventory then "giveammo", "admin", function(xPlayer, args, showError) - local MAX_AMOUNT = 1.79769e+308 + local MAX_AMMO = 1.79769e+308 if not args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveammo_noweapon_found")) end - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) + if args.ammo > MAX_AMMO then + return showError(("Ammo must be between 1 and %s"):format(MAX_AMMO)) end args.playerId.addWeaponAmmo(args.weapon, args.ammo) if Config.AdminLogging then From f2cde312514d47ab3891a2473c8b46657b47023b Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 21:55:24 -0500 Subject: [PATCH 055/132] feat(commands): remove maximum amount validation for account money and item commands --- .../es_extended/server/modules/commands.lua | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 5216b2f3..628e85ea 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -204,13 +204,9 @@ ESX.RegisterCommand( "setaccountmoney", "admin", function(xPlayer, args, showError) - local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) - end args.playerId.setAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Set Account Money /setaccountmoney Triggered!", "pink", { @@ -238,13 +234,9 @@ ESX.RegisterCommand( "giveaccountmoney", "admin", function(xPlayer, args, showError) - local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) end - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) - end args.playerId.addAccountMoney(args.account, args.amount, "Government Grant") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Account Money /giveaccountmoney Triggered!", "pink", { @@ -272,13 +264,9 @@ ESX.RegisterCommand( "removeaccountmoney", "admin", function(xPlayer, args, showError) - local MAX_AMOUNT = 1.79769e+308 if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_removeaccountmoney_invalid")) end - if args.amount > MAX_AMOUNT then - return showError(("Amount must be between 1 and %s"):format(MAX_AMOUNT)) - end args.playerId.removeAccountMoney(args.account, args.amount, "Government Tax") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Remove Account Money /removeaccountmoney Triggered!", "pink", { @@ -307,10 +295,6 @@ if not Config.CustomInventory then "giveitem", "admin", function(xPlayer, args) - local MAX_COUNT = 1.79769e+308 - if args.count > MAX_COUNT then - return showError(("Count must be between 1 and %s"):format(MAX_COUNT)) - end args.playerId.addInventoryItem(args.item, args.count) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Item /giveitem Triggered!", "pink", { @@ -338,13 +322,9 @@ if not Config.CustomInventory then "giveweapon", "admin", function(xPlayer, args, showError) - local MAX_AMMO = 1.79769e+308 if args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveweapon_hasalready")) end - if args.ammo > MAX_AMMO then - return showError(("Ammo must be between 1 and %s"):format(MAX_AMMO)) - end args.playerId.addWeapon(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Weapon /giveweapon Triggered!", "pink", { @@ -372,13 +352,9 @@ if not Config.CustomInventory then "giveammo", "admin", function(xPlayer, args, showError) - local MAX_AMMO = 1.79769e+308 if not args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveammo_noweapon_found")) end - if args.ammo > MAX_AMMO then - return showError(("Ammo must be between 1 and %s"):format(MAX_AMMO)) - end args.playerId.addWeaponAmmo(args.weapon, args.ammo) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Give Ammunition /giveammo Triggered!", "pink", { From 7877d9e40a5a9f72d7f898a5a4ab9f751e723256 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 21:58:23 -0500 Subject: [PATCH 056/132] feat(inventory): implement maximum amount cap for account money transactions --- [core]/es_extended/server/classes/overrides/oxinventory.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/[core]/es_extended/server/classes/overrides/oxinventory.lua b/[core]/es_extended/server/classes/overrides/oxinventory.lua index c3be26a8..64bdd550 100644 --- a/[core]/es_extended/server/classes/overrides/oxinventory.lua +++ b/[core]/es_extended/server/classes/overrides/oxinventory.lua @@ -1,4 +1,5 @@ local Inventory +local MAX_AMOUNT = 1.79769e+308 if Config.CustomInventory ~= "ox" then return end @@ -45,6 +46,7 @@ Core.PlayerFunctionOverrides.OxInventory = { setAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 0 then return end local account = self.getAccount(accountName) @@ -64,6 +66,7 @@ Core.PlayerFunctionOverrides.OxInventory = { addAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 1 then return end local account = self.getAccount(accountName) @@ -82,6 +85,7 @@ Core.PlayerFunctionOverrides.OxInventory = { removeAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 1 then return end local account = self.getAccount(accountName) From 90ba45075ef1e0e36e6367716fbb4b50d7a173e6 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 22:01:15 -0500 Subject: [PATCH 057/132] feat(player): enforce maximum amount limit for account money transactions --- [core]/es_extended/server/classes/player.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index b4682df7..a454cbfc 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -31,6 +31,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, loadout, name, coords, metadata) ---@class xPlayer local self = {} + local MAX_AMOUNT = 1.79769e+308 self.accounts = accounts self.coords = coords @@ -313,6 +314,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money >= 0 then local account = self.getAccount(accountName) @@ -340,6 +342,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money > 0 then local account = self.getAccount(accountName) if account then @@ -366,6 +369,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end + money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money > 0 then local account = self.getAccount(accountName) From 6693a7f5034d4e86e60fb23a6d1386003767b74d Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Tue, 25 Feb 2025 22:59:31 -0500 Subject: [PATCH 058/132] feat(player): update inventory item addition to enforce maximum count limit --- [core]/es_extended/server/classes/player.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index a454cbfc..49e5cfa4 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -407,15 +407,14 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, ---@return nil function self.addInventoryItem(itemName, count) local item = self.getInventoryItem(itemName) + if not item then return end - if item then - count = ESX.Math.Round(count) - item.count = item.count + count - self.weight = self.weight + (item.weight * count) + count += item.count + item.count = (count <= MAX_AMOUNT and count) or MAX_AMOUNT + self.weight += (item.weight * count) - TriggerEvent("esx:onAddInventoryItem", self.source, item.name, item.count) - self.triggerEvent("esx:addInventoryItem", item.name, item.count) - end + TriggerEvent("esx:onAddInventoryItem", self.source, item.name, item.count) + self.triggerEvent("esx:addInventoryItem", item.name, item.count) end ---@param itemName string From 9083a7152645632cb2e84616e2a300d5aa3dabfb Mon Sep 17 00:00:00 2001 From: iSentrie Date: Sun, 2 Mar 2025 11:49:04 +0200 Subject: [PATCH 059/132] fix(functions.lua): IsPlayerAdmin group fix Function failed to properly detect is player admin or not, while GetPlayerFromId function was receiving a string instead of number. --- [core]/es_extended/server/functions.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 12417065..2a57fc0f 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -612,11 +612,14 @@ end ---@param playerId string | number ---@return boolean function Core.IsPlayerAdmin(playerId) - playerId = tostring(playerId) - if (IsPlayerAceAllowed(playerId, "command") or GetConvar("sv_lan", "") == "true") then + local playerSrc = tostring(playerId) + + if IsPlayerAceAllowed(playerSrc, "command") or GetConvar("sv_lan", "") == "true" then return true end - local xPlayer = ESX.Players[playerId] - return (xPlayer and Config.AdminGroups[xPlayer.group] and true) or false + local xPlayer = ESX.GetPlayerFromId(tonumber(playerId)) + if not xPlayer then return false end + + return Config.AdminGroups[xPlayer.getGroup()] or false end From 80d934ac599a6573ac45bb3451219aa357a6a361 Mon Sep 17 00:00:00 2001 From: YOMAN1792 Date: Mon, 3 Mar 2025 21:33:05 -0500 Subject: [PATCH 060/132] feat(player): refine inventory item count handling with rounding and maximum limit enforcement. --- [core]/es_extended/server/classes/player.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index 49e5cfa4..e0076c56 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -407,10 +407,9 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, ---@return nil function self.addInventoryItem(itemName, count) local item = self.getInventoryItem(itemName) - if not item then return end count += item.count - item.count = (count <= MAX_AMOUNT and count) or MAX_AMOUNT + item.count = ESX.Math.Round(count) <= MAX_AMOUNT and ESX.Math.Round(count) or MAX_AMOUNT self.weight += (item.weight * count) TriggerEvent("esx:onAddInventoryItem", self.source, item.name, item.count) From 22d191b51edea0a97a0e5a0765d0e84a0dc0025a Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:06:50 +0100 Subject: [PATCH 061/132] Revert "feat(es_extended/server/modules/commands.lua): Add Validation Number" --- .../server/classes/overrides/oxinventory.lua | 4 ---- [core]/es_extended/server/classes/player.lua | 16 +++++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/[core]/es_extended/server/classes/overrides/oxinventory.lua b/[core]/es_extended/server/classes/overrides/oxinventory.lua index 64bdd550..c3be26a8 100644 --- a/[core]/es_extended/server/classes/overrides/oxinventory.lua +++ b/[core]/es_extended/server/classes/overrides/oxinventory.lua @@ -1,5 +1,4 @@ local Inventory -local MAX_AMOUNT = 1.79769e+308 if Config.CustomInventory ~= "ox" then return end @@ -46,7 +45,6 @@ Core.PlayerFunctionOverrides.OxInventory = { setAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 0 then return end local account = self.getAccount(accountName) @@ -66,7 +64,6 @@ Core.PlayerFunctionOverrides.OxInventory = { addAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 1 then return end local account = self.getAccount(accountName) @@ -85,7 +82,6 @@ Core.PlayerFunctionOverrides.OxInventory = { removeAccountMoney = function(self) return function(accountName, money, reason) reason = reason or "unknown" - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money < 1 then return end local account = self.getAccount(accountName) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index e0076c56..b4682df7 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -31,7 +31,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, loadout, name, coords, metadata) ---@class xPlayer local self = {} - local MAX_AMOUNT = 1.79769e+308 self.accounts = accounts self.coords = coords @@ -314,7 +313,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money >= 0 then local account = self.getAccount(accountName) @@ -342,7 +340,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money > 0 then local account = self.getAccount(accountName) if account then @@ -369,7 +366,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, error(("Tried To Set Account ^5%s^1 For Player ^5%s^1 To An Invalid Number -> ^5%s^1"):format(accountName, self.playerId, money)) return end - money = money <= MAX_AMOUNT and money or MAX_AMOUNT if money > 0 then local account = self.getAccount(accountName) @@ -408,12 +404,14 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.addInventoryItem(itemName, count) local item = self.getInventoryItem(itemName) - count += item.count - item.count = ESX.Math.Round(count) <= MAX_AMOUNT and ESX.Math.Round(count) or MAX_AMOUNT - self.weight += (item.weight * count) + if item then + count = ESX.Math.Round(count) + item.count = item.count + count + self.weight = self.weight + (item.weight * count) - TriggerEvent("esx:onAddInventoryItem", self.source, item.name, item.count) - self.triggerEvent("esx:addInventoryItem", item.name, item.count) + TriggerEvent("esx:onAddInventoryItem", self.source, item.name, item.count) + self.triggerEvent("esx:addInventoryItem", item.name, item.count) + end end ---@param itemName string From 638ce3a276af997df59567562c2d4fefbb337edd Mon Sep 17 00:00:00 2001 From: nexis Date: Thu, 6 Mar 2025 09:41:03 +0100 Subject: [PATCH 062/132] fix(es_extended/server/main): GlobalState jobcount infinitely increasing --- [core]/es_extended/server/main.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index fe0821a0..845bc7e9 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -353,6 +353,11 @@ end) AddEventHandler("esx:playerLogout", function(playerId, cb) local xPlayer = ESX.GetPlayerFromId(playerId) + local job = xPlayer.getJob().name + + Core.JobsPlayerCount[job] = Core.JobsPlayerCount[job] - 1 + GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job] + if xPlayer then TriggerEvent("esx:playerDropped", playerId) From 88a415a2b6383b86a34be76068cca54cd55972cb Mon Sep 17 00:00:00 2001 From: iSentrie Date: Sun, 9 Mar 2025 23:29:52 +0200 Subject: [PATCH 063/132] Update functions.lua --- [core]/es_extended/server/functions.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 2a57fc0f..447a3e51 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -612,13 +612,11 @@ end ---@param playerId string | number ---@return boolean function Core.IsPlayerAdmin(playerId) - local playerSrc = tostring(playerId) - - if IsPlayerAceAllowed(playerSrc, "command") or GetConvar("sv_lan", "") == "true" then + if IsPlayerAceAllowed(playerId, "command") or GetConvar("sv_lan", "") == "true" then return true end - local xPlayer = ESX.GetPlayerFromId(tonumber(playerId)) + local xPlayer = ESX.GetPlayerFromId(playerId) if not xPlayer then return false end return Config.AdminGroups[xPlayer.getGroup()] or false From eb1a2b992371bfa4685eabeced7421f5b2e0cbfd Mon Sep 17 00:00:00 2001 From: iSentrie Date: Sun, 9 Mar 2025 23:39:57 +0200 Subject: [PATCH 064/132] Update functions.lua --- [core]/es_extended/server/functions.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 447a3e51..b571501c 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -609,15 +609,21 @@ function ESX.DoesJobExist(job, grade) return (ESX.Jobs[job] and ESX.Jobs[job].grades[tostring(grade)] ~= nil) or false end ----@param playerId string | number +---@param playerSrc number ---@return boolean -function Core.IsPlayerAdmin(playerId) - if IsPlayerAceAllowed(playerId, "command") or GetConvar("sv_lan", "") == "true" then +function Core.IsPlayerAdmin(playerSrc) + if type(playerSrc) ~= "number" then + return false + end + + if IsPlayerAceAllowed(tostring(playerSrc), "command") or GetConvar("sv_lan", "") == "true" then return true end - local xPlayer = ESX.GetPlayerFromId(playerId) - if not xPlayer then return false end + local xPlayer = ESX.GetPlayerFromId(playerSrc) + if not xPlayer then + return false + end return Config.AdminGroups[xPlayer.getGroup()] or false end From 23915d15088e23459381137c7847106c0c2515e6 Mon Sep 17 00:00:00 2001 From: iSentrie Date: Sun, 9 Mar 2025 23:42:54 +0200 Subject: [PATCH 065/132] Update functions.lua --- [core]/es_extended/server/functions.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index b571501c..ccdfb584 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -621,9 +621,5 @@ function Core.IsPlayerAdmin(playerSrc) end local xPlayer = ESX.GetPlayerFromId(playerSrc) - if not xPlayer then - return false - end - - return Config.AdminGroups[xPlayer.getGroup()] or false + return xPlayer and Config.AdminGroups[xPlayer.getGroup()] or false end From 5bb75de78fb16ffe868e44469f1e91ef90faedc3 Mon Sep 17 00:00:00 2001 From: iSentrie Date: Mon, 10 Mar 2025 00:11:09 +0200 Subject: [PATCH 066/132] Update functions.lua --- [core]/es_extended/server/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 501ff357..5ba47867 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -633,7 +633,7 @@ function Core.IsPlayerAdmin(playerSrc) return false end - if IsPlayerAceAllowed(tostring(playerSrc), "command") or GetConvar("sv_lan", "") == "true" then + if IsPlayerAceAllowed(playerSrc --[[@as string]], "command") or GetConvar("sv_lan", "") == "true" then return true end From 532070b72dd86667c46c8845603bcb14f464477a Mon Sep 17 00:00:00 2001 From: Mirow Date: Tue, 1 Apr 2025 17:56:50 +0200 Subject: [PATCH 067/132] feat(callback): add `AwaitClientCallback` function --- .../es_extended/client/modules/callback.lua | 47 +++++++++---------- .../es_extended/server/modules/callback.lua | 36 +++++++++++++- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/[core]/es_extended/client/modules/callback.lua b/[core]/es_extended/client/modules/callback.lua index 6bd2d758..85facbb7 100644 --- a/[core]/es_extended/client/modules/callback.lua +++ b/[core]/es_extended/client/modules/callback.lua @@ -14,8 +14,25 @@ Callbacks.id = 0 -- MARK: Internal Functions -- ============================================= -function Callbacks:Trigger(event, cb, invoker, ...) +function Callbacks:Register(name, resource, cb) + self.storage[name] = { + resource = resource, + cb = cb + } +end + +function Callbacks:Execute(cb, id, ...) + local success, errorString = pcall(cb, ...) + + if not success then + print(("[^1ERROR^7] Failed to execute Callback with RequestId: ^5%s^7"):format(id)) + error(errorString) + return + end +end + +function Callbacks:Trigger(event, cb, invoker, ...) self.requests[self.id] = { await = type(cb) == "boolean", cb = cb or promise:new() @@ -29,16 +46,6 @@ function Callbacks:Trigger(event, cb, invoker, ...) return table.cb end -function Callbacks:Execute(cb, id, ...) - local success, errorString = pcall(cb, ...) - - if not success then - print(("[^1ERROR^7] Failed to execute Callback with RequestId: ^5%s^7"):format(id)) - error(errorString) - return - end -end - function Callbacks:ServerRecieve(requestId, invoker, ...) if not self.requests[requestId] then return error(("Server Callback with requestId ^5%s^1 Was Called by ^5%s^1 but does not exist."):format(requestId, invoker)) @@ -49,21 +56,13 @@ function Callbacks:ServerRecieve(requestId, invoker, ...) self.requests[requestId] = nil if callback.await then - callback.cb:resolve({...}) + callback.cb:resolve({ ... }) else self:Execute(callback.cb, requestId, ...) end end -function Callbacks:Register(name, resource, cb) - self.storage[name] = { - resource = resource, - cb = cb - } -end - function Callbacks:ClientRecieve(eventName, requestId, invoker, ...) - if not self.storage[eventName] then return error(("Client Callback with requestId ^5%s^1 Was Called by ^5%s^1 but does not exist."):format(eventName, invoker)) end @@ -130,14 +129,14 @@ end -- MARK: Events -- ============================================= -ESX.SecureNetEvent("esx:triggerClientCallback", function(...) - Callbacks:ClientRecieve(...) -end) - ESX.SecureNetEvent("esx:serverCallback", function(...) Callbacks:ServerRecieve(...) end) +ESX.SecureNetEvent("esx:triggerClientCallback", function(...) + Callbacks:ClientRecieve(...) +end) + AddEventHandler("onResourceStop", function(resource) for k, v in pairs(Callbacks.storage) do if v.resource == resource then diff --git a/[core]/es_extended/server/modules/callback.lua b/[core]/es_extended/server/modules/callback.lua index 3301e6a6..fc89a855 100644 --- a/[core]/es_extended/server/modules/callback.lua +++ b/[core]/es_extended/server/modules/callback.lua @@ -33,11 +33,17 @@ function Callbacks:Execute(cb, ...) end function Callbacks:Trigger(player, event, cb, invoker, ...) - self.requests[self.id] = cb + self.requests[self.id] = { + await = type(cb) == "boolean", + cb = cb or promise:new() + } + local table = self.requests[self.id] TriggerClientEvent("esx:triggerClientCallback", player, event, self.id, invoker, ...) self.id += 1 + + return table.cb end function Callbacks:ServerRecieve(player, event, requestId, invoker, ...) @@ -64,8 +70,12 @@ function Callbacks:RecieveClient(requestId, invoker, ...) local callback = self.requests[self.currentId] - self:Execute(callback, ...) self.requests[requestId] = nil + if callback.await then + callback.cb:resolve({ ... }) + else + self:Execute(callback.cb, ...) + end end -- ============================================= @@ -83,6 +93,28 @@ function ESX.TriggerClientCallback(player, eventName, callback, ...) Callbacks:Trigger(player, eventName, callback, invoker, ...) end +---@param player number playerId +---@param eventName string +---@param ... any +---@return any +function ESX.AwaitClientCallback(player, eventName, ...) + local invokingResource = GetInvokingResource() + local invoker = (invokingResource and invokingResource ~= "Unknown") and invokingResource or "es_extended" + + local p = Callbacks:Trigger(player, eventName, false, invoker, ...) + if not p then return end + + SetTimeout(15000, function() + if p.state == "pending" then + p:reject("Server Callback Timed Out") + end + end) + + Citizen.Await(p) + + return table.unpack(p.value) +end + ---@param eventName string ---@param callback function ---@return nil From aba21e2c036caa770cf81fb8e19745f1691253a6 Mon Sep 17 00:00:00 2001 From: Mirow Date: Fri, 4 Apr 2025 16:59:08 +0200 Subject: [PATCH 068/132] feat(paycheck): add event --- .../es_extended/server/modules/paycheck.lua | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/[core]/es_extended/server/modules/paycheck.lua b/[core]/es_extended/server/modules/paycheck.lua index 54881709..cb7de3a9 100644 --- a/[core]/es_extended/server/modules/paycheck.lua +++ b/[core]/es_extended/server/modules/paycheck.lua @@ -9,29 +9,30 @@ function StartPayCheck() local salary = (job == "unemployed" or onDuty) and xPlayer.job.grade_salary or ESX.Math.Round(xPlayer.job.grade_salary * Config.OffDutyPaycheckMultiplier) if xPlayer.paycheckEnabled then + TriggerEvent("esx:paycheckReceived", xPlayer.source, salary, job, jobLabel, onDuty) if salary > 0 then if job == "unemployed" then -- unemployed xPlayer.addAccountMoney("bank", salary, "Welfare Check") TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_help", salary), "CHAR_BANK_MAZE", 9) if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - Unemployment Benefits", "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end - elseif Config.EnableSocietyPayouts then -- possibly a society + elseif Config.EnableSocietyPayouts then -- possibly a society TriggerEvent("esx_society:getSociety", xPlayer.job.name, function(society) - if society ~= nil then -- verified society + if society ~= nil then -- verified society TriggerEvent("esx_addonaccount:getSharedAccount", society.account, function(account) if account.money >= salary then -- does the society money to pay its employees? xPlayer.addAccountMoney("bank", salary, "Paycheck") account.removeMoney(salary) if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - " .. jobLabel, "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end @@ -44,9 +45,9 @@ function StartPayCheck() xPlayer.addAccountMoney("bank", salary, "Paycheck") if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - " .. jobLabel, "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_salary", salary), "CHAR_BANK_MAZE", 9) @@ -56,9 +57,9 @@ function StartPayCheck() xPlayer.addAccountMoney("bank", salary, "Paycheck") if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - Generic", "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_salary", salary), "CHAR_BANK_MAZE", 9) From 9f0bafef3d607f6e3c87e79bc35d03bc394daf97 Mon Sep 17 00:00:00 2001 From: Mirow Date: Fri, 4 Apr 2025 17:02:10 +0200 Subject: [PATCH 069/132] Revert "feat(paycheck): add event" This reverts commit aba21e2c036caa770cf81fb8e19745f1691253a6. --- .../es_extended/server/modules/paycheck.lua | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/[core]/es_extended/server/modules/paycheck.lua b/[core]/es_extended/server/modules/paycheck.lua index cb7de3a9..54881709 100644 --- a/[core]/es_extended/server/modules/paycheck.lua +++ b/[core]/es_extended/server/modules/paycheck.lua @@ -9,30 +9,29 @@ function StartPayCheck() local salary = (job == "unemployed" or onDuty) and xPlayer.job.grade_salary or ESX.Math.Round(xPlayer.job.grade_salary * Config.OffDutyPaycheckMultiplier) if xPlayer.paycheckEnabled then - TriggerEvent("esx:paycheckReceived", xPlayer.source, salary, job, jobLabel, onDuty) if salary > 0 then if job == "unemployed" then -- unemployed xPlayer.addAccountMoney("bank", salary, "Welfare Check") TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_help", salary), "CHAR_BANK_MAZE", 9) if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - Unemployment Benefits", "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end - elseif Config.EnableSocietyPayouts then -- possibly a society + elseif Config.EnableSocietyPayouts then -- possibly a society TriggerEvent("esx_society:getSociety", xPlayer.job.name, function(society) - if society ~= nil then -- verified society + if society ~= nil then -- verified society TriggerEvent("esx_addonaccount:getSharedAccount", society.account, function(account) if account.money >= salary then -- does the society money to pay its employees? xPlayer.addAccountMoney("bank", salary, "Paycheck") account.removeMoney(salary) if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - " .. jobLabel, "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end @@ -45,9 +44,9 @@ function StartPayCheck() xPlayer.addAccountMoney("bank", salary, "Paycheck") if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - " .. jobLabel, "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_salary", salary), "CHAR_BANK_MAZE", 9) @@ -57,9 +56,9 @@ function StartPayCheck() xPlayer.addAccountMoney("bank", salary, "Paycheck") if Config.LogPaycheck then ESX.DiscordLogFields("Paycheck", "Paycheck - Generic", "green", { - { name = "Player", value = xPlayer.name, inline = true }, - { name = "ID", value = xPlayer.source, inline = true }, - { name = "Amount", value = salary, inline = true }, + { name = "Player", value = xPlayer.name, inline = true }, + { name = "ID", value = xPlayer.source, inline = true }, + { name = "Amount", value = salary, inline = true }, }) end TriggerClientEvent("esx:showAdvancedNotification", player, TranslateCap("bank"), TranslateCap("received_paycheck"), TranslateCap("received_salary", salary), "CHAR_BANK_MAZE", 9) From f4f6f22578658517c06b393ba1ce2abec6cc0caf Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:51:44 -0400 Subject: [PATCH 070/132] Refactor name validation: remove numeric check and simplify character validation - Removed the redundant numeric check as it is already handled by the character validation. - Simplified the name validation logic by keeping only the `checkValidCharacter()` function for validating characters. - The `checkValidCharacter()` function checks for allowed characters: Latin, Greek, Cyrillic, Hebrew, Arabic, and CJK. - Updated `checkNameFormat()` to rely solely on the new character validation logic. --- [core]/esx_identity/server/main.lua | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index d0461b92..9d00a43d 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -84,16 +84,35 @@ local function formatDate(str) return date end -local function checkAlphanumeric(str) - return (string.match(str, "%W")) -end +local function checkValidCharacter(str) + for _, code in utf8.codes(str) do -local function checkForNumbers(str) - return (string.match(str, "%d")) + local isBasicLatin = (code >= 0x0041 and code <= 0x005A) or (code >= 0x0061 and code <= 0x007A) + local isSpaceOrDash = (code == 0x0020 or code == 0x002D) + local isLatinExtended = (code >= 0x00C0 and code <= 0x02AF) + local isGreek = (code >= 0x0370 and code <= 0x03FF) + local isCyrillic = (code >= 0x0400 and code <= 0x04FF) + local isHebrew = (code >= 0x05D0 and code <= 0x05EA) + local isArabic = + (code >= 0x0620 and code <= 0x063F) or + (code >= 0x0641 and code <= 0x064A) or + (code >= 0x066E and code <= 0x066F) or + (code >= 0x0671 and code <= 0x06D3) or + (code == 0x06D5) or + (code >= 0x0750 and code <= 0x077F) or + (code >= 0x08A0 and code <= 0x08BD) + local isCJK = (code >= 0x4E00 and code <= 0x9FFF) + + if not (isBasicLatin or isSpaceOrDash or isLatinExtended or isGreek or isCyrillic or isHebrew or isArabic or isCJK) then + return false + end + end + + return true end local function checkNameFormat(name) - if not checkAlphanumeric(name) and not checkForNumbers(name) then + if checkValidCharacter(name) then local stringLength = string.len(name) return stringLength > 0 and stringLength < Config.MaxNameLength end From 57228734722e792289bc22b96d45d16912078b17 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:01:28 +0200 Subject: [PATCH 071/132] feat(es_extended/server/functions): add promise support for ESX.GetVehicleType --- [core]/es_extended/server/functions.lua | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 5ba47867..9b20c92f 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -386,19 +386,38 @@ end ---@param model string|number ---@param player number ----@param cb function +---@param cb function? ---@diagnostic disable-next-line: duplicate-set-field +---@return string? function ESX.GetVehicleType(model, player, cb) + if cb and not ESX.IsFunctionReference(cb) then + error("Invalid callback function") + end + + local promise = not cb and promise.new() + + local function resolve(result) + if promise then + promise:resolve(result) + elseif cb then + cb(result) + end + end + model = type(model) == "string" and joaat(model) or model if Core.vehicleTypesByModel[model] then - return cb(Core.vehicleTypesByModel[model]) + return resolve(Core.vehicleTypesByModel[model]) end ESX.TriggerClientCallback(player, "esx:GetVehicleType", function(vehicleType) Core.vehicleTypesByModel[model] = vehicleType - cb(vehicleType) + resolve(vehicleType) end, model) + + if promise then + return Citizen.Await(promise) + end end ---@param name string From 372c8a224d5e71dc7169a49761679268144a80ff Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:01:48 +0200 Subject: [PATCH 072/132] feat(es_extended/server/modules/onesync): add optional vehicleType to SpawnVehicle --- [core]/es_extended/server/modules/onesync.lua | 78 +++++++++++-------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index de4f4eb8..8c5c76d3 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -78,57 +78,67 @@ function ESX.OneSync.GetClosestPlayer(source, maxDistance, ignore) return getNearbyPlayers(source, true, maxDistance, ignore) end ----@param model number|string +---@param vehicleModel number|string ---@param coords vector3|table ---@param heading number ----@param properties table +---@param vehicleProperties table ---@param cb? fun(netId: number) +---@param vehicleType string? ---@return number? netId -function ESX.OneSync.SpawnVehicle(model, coords, heading, properties, cb) +function ESX.OneSync.SpawnVehicle(vehicleModel, coords, heading, vehicleProperties, cb, vehicleType) if cb and not ESX.IsFunctionReference(cb) then error("Invalid callback function") end - - local vehicleModel = joaat(model) - local vehicleProperties = properties + vehicleModel = joaat(vehicleModel) local promise = not cb and promise.new() + + local function resolve(result) + if promise then + promise:resolve(result) + elseif cb then + cb(result) + end + end + + local function reject(err) + if promise then + promise:reject(err) + end + error(err) + end + CreateThread(function() - local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) - ESX.GetVehicleType(vehicleModel, xPlayer.id, function(vehicleType) - if not vehicleType then - if (promise) then - return promise:reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) - end - error(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) + if not vehicleType then + local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) + if not xPlayer then + return reject("No players found nearby to check vehicle type!") end + vehicleType = ESX.GetVehicleType(vehicleModel, xPlayer.id) + end - local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) - local tries = 0 + if not vehicleType then + return reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(vehicleModel)) + end - while not createdVehicle or createdVehicle == 0 or NetworkGetEntityOwner(createdVehicle) == -1 do - Wait(200) - tries = tries + 1 - if tries > 40 then - if promise then - return promise:reject(("Could not spawn vehicle - ^5%s^7!"):format(model)) - end - error(("Could not spawn vehicle - ^5%s^7!"):format(model)) - end + local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) + local tries = 0 + + while not createdVehicle or createdVehicle == 0 or NetworkGetEntityOwner(createdVehicle) == -1 do + Wait(200) + tries = tries + 1 + if tries > 40 then + return reject(("Could not spawn vehicle - ^5%s^7!"):format(vehicleModel)) end + end - -- luacheck: ignore - SetEntityOrphanMode(createdVehicle, 2) - local networkId = NetworkGetNetworkIdFromEntity(createdVehicle) - Entity(createdVehicle).state:set("VehicleProperties", vehicleProperties, true) + -- luacheck: ignore + SetEntityOrphanMode(createdVehicle, 2) + local networkId = NetworkGetNetworkIdFromEntity(createdVehicle) + Entity(createdVehicle).state:set("VehicleProperties", vehicleProperties, true) - if promise then - promise:resolve(networkId) - elseif cb then - cb(networkId) - end - end) + resolve(networkId) end) if promise then From 7ef8ad78bf7fe714e94432825ed5c92ead86842c Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:38:14 +0200 Subject: [PATCH 073/132] Remove old & Add new config entries for MinAge & MaxAge --- [core]/esx_identity/config.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/esx_identity/config.lua b/[core]/esx_identity/config.lua index 9d46263c..77a15d0d 100644 --- a/[core]/esx_identity/config.lua +++ b/[core]/esx_identity/config.lua @@ -13,8 +13,8 @@ Config.DateFormat = "DD/MM/YYYY" Config.MaxNameLength = 20 -- Max Name Length. Config.MinHeight = 120 -- 120 cm lowest height Config.MaxHeight = 220 -- 220 cm max height. -Config.LowestYear = 1900 -- 112 years old is the oldest you can be. -Config.HighestYear = 2005 -- 18 years old is the youngest you can be. +Config.MinAge = 18 -- 18 years old is the youngest you can be. +Config.MaxAge = 112 -- 112 years old is the oldest you can be. Config.FullCharDelete = true -- Delete all reference to character. Config.EnableDebugging = ESX.GetConfig().EnableDebug -- prints for debugging :) From 4819740b21d3a240c41b4848427668ce5f066778 Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:38:27 +0200 Subject: [PATCH 074/132] Refactor checkDOBFormat function --- [core]/esx_identity/server/main.lua | 58 +++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index d0461b92..d4530f47 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -44,31 +44,51 @@ local function saveIdentityToDatabase(identifier, identity) MySQL.update.await("UPDATE users SET firstname = ?, lastname = ?, dateofbirth = ?, sex = ?, height = ? WHERE identifier = ?", { identity.firstName, identity.lastName, identity.dateOfBirth, identity.sex, identity.height, identifier }) end -local function checkDOBFormat(str) - str = tostring(str) - if not string.match(str, "(%d%d)/(%d%d)/(%d%d%d%d)") then +---@param year number Year +---@return boolean: true if the year is a leap year, false otherwise +local function isLeapYear(year) + return (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0) +end + +---@param dob string Date of Birth in the format DD/MM/YYYY +---@return boolean: true if the date is valid, false otherwise +local function checkDOBFormat(dob) + dob = tostring(dob) + + local dayStr, monthStr, yearStr = dob:match('^(%d%d?)/(%d%d?)/(%d%d%d%d)$') + if not dayStr or not monthStr or not yearStr then return false end - local d, m, y = string.match(str, "(%d+)/(%d+)/(%d+)") - - m = tonumber(m) - d = tonumber(d) - y = tonumber(y) - - if ((d <= 0) or (d > 31)) or ((m <= 0) or (m > 12)) or ((y <= Config.LowestYear) or (y > Config.HighestYear)) then + local day, month, year = tonumber(dayStr), tonumber(monthStr), tonumber(yearStr) + if not day or not month or not year then return false - elseif m == 4 or m == 6 or m == 9 or m == 11 then - return d <= 30 - elseif m == 2 then - if y % 400 == 0 or (y % 100 ~= 0 and y % 4 == 0) then - return d <= 29 - else - return d <= 28 + end + + local currentDate = os.date("*t") + local currentYear = currentDate.year + local minYear = currentYear - Config.MaxAge + local maxYear = currentYear - Config.MinAge + + if year < minYear or year > maxYear or year > currentYear then + return false + end + + if year == maxYear then + if month > currentDate.month or (month == currentDate.month and day > currentDate.day) then + return false end - else - return d <= 31 end + + if month < 1 or month > 12 then return false end + + -- Days in each month (starting from January.) + local daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + if month == 2 and isLeapYear(year) then + daysInMonth[2] = 29 + end + + return day >= 1 and day <= daysInMonth[month] end local function formatDate(str) From f77b105419e0bd6aba91c8554eafea3c22f7a00f Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:04:21 +0200 Subject: [PATCH 075/132] Update main.lua --- [core]/esx_identity/server/main.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index d4530f47..6c111e74 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -83,11 +83,7 @@ local function checkDOBFormat(dob) if month < 1 or month > 12 then return false end -- Days in each month (starting from January.) - local daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } - if month == 2 and isLeapYear(year) then - daysInMonth[2] = 29 - end - + local daysInMonth = { 31, isLeapYear(year) and 29 or 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } return day >= 1 and day <= daysInMonth[month] end From 90120948e9ed1f2457996aee0603bba7d78250ac Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:42:46 +0200 Subject: [PATCH 076/132] Remove Config.MinAge --- [core]/esx_identity/config.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/[core]/esx_identity/config.lua b/[core]/esx_identity/config.lua index 77a15d0d..3da197d0 100644 --- a/[core]/esx_identity/config.lua +++ b/[core]/esx_identity/config.lua @@ -13,8 +13,7 @@ Config.DateFormat = "DD/MM/YYYY" Config.MaxNameLength = 20 -- Max Name Length. Config.MinHeight = 120 -- 120 cm lowest height Config.MaxHeight = 220 -- 220 cm max height. -Config.MinAge = 18 -- 18 years old is the youngest you can be. -Config.MaxAge = 112 -- 112 years old is the oldest you can be. +Config.MaxAge = 100 -- 100 years old is the oldest you can be. Config.FullCharDelete = true -- Delete all reference to character. Config.EnableDebugging = ESX.GetConfig().EnableDebug -- prints for debugging :) From ad6c9a0930e3b8e35680a6eb540885652e67cd65 Mon Sep 17 00:00:00 2001 From: zykem#0643 <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 19:48:12 +0200 Subject: [PATCH 077/132] Format date after validating it --- [core]/esx_identity/server/main.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 6c111e74..1642ab1f 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -68,13 +68,13 @@ local function checkDOBFormat(dob) local currentDate = os.date("*t") local currentYear = currentDate.year local minYear = currentYear - Config.MaxAge - local maxYear = currentYear - Config.MinAge + local maxYear = currentYear - 18 - if year < minYear or year > maxYear or year > currentYear then + if year < minYear or year > maxYear or year > currentYear then return false end - if year == maxYear then + if year == currentYear then if month > currentDate.month or (month == currentDate.month and day > currentDate.day) then return false end @@ -259,7 +259,6 @@ end ESX.RegisterServerCallback("esx_identity:registerIdentity", function(source, cb, data) local xPlayer = ESX.GetPlayerFromId(source) - data.dateofbirth = formatDate(data.dateofbirth) if not checkNameFormat(data.firstname) then TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_firstname_format"), "error") @@ -281,6 +280,7 @@ end TriggerClientEvent("esx:showNotification", source, TranslateCap("invalid_height_format"), "error") return cb(false) end + if xPlayer then if alreadyRegistered[xPlayer.identifier] then xPlayer.showNotification(TranslateCap("already_registered"), "error") @@ -290,7 +290,7 @@ end playerIdentity[xPlayer.identifier] = { firstName = formatName(data.firstname), lastName = formatName(data.lastname), - dateOfBirth = data.dateofbirth, + dateOfBirth = formatDate(data.dateofbirth), sex = data.sex, height = data.height, } From 77be031e8239b4612f166b1b97b0329ecdc03bdb Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:19:49 -0400 Subject: [PATCH 078/132] Add early returns --- [core]/esx_identity/server/main.lua | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 9d00a43d..5dcc4d7a 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -86,28 +86,30 @@ end local function checkValidCharacter(str) for _, code in utf8.codes(str) do + if not ( - local isBasicLatin = (code >= 0x0041 and code <= 0x005A) or (code >= 0x0061 and code <= 0x007A) - local isSpaceOrDash = (code == 0x0020 or code == 0x002D) - local isLatinExtended = (code >= 0x00C0 and code <= 0x02AF) - local isGreek = (code >= 0x0370 and code <= 0x03FF) - local isCyrillic = (code >= 0x0400 and code <= 0x04FF) - local isHebrew = (code >= 0x05D0 and code <= 0x05EA) - local isArabic = - (code >= 0x0620 and code <= 0x063F) or - (code >= 0x0641 and code <= 0x064A) or - (code >= 0x066E and code <= 0x066F) or - (code >= 0x0671 and code <= 0x06D3) or - (code == 0x06D5) or - (code >= 0x0750 and code <= 0x077F) or - (code >= 0x08A0 and code <= 0x08BD) - local isCJK = (code >= 0x4E00 and code <= 0x9FFF) - - if not (isBasicLatin or isSpaceOrDash or isLatinExtended or isGreek or isCyrillic or isHebrew or isArabic or isCJK) then + (code >= 0x0041 and code <= 0x005A) or -- Basic Latin uppercase + (code >= 0x0061 and code <= 0x007A) or -- Basic Latin lowercase + (code == 0x0020 or code == 0x002D) or -- Space or dash + (code >= 0x00C0 and code <= 0x02AF) or -- Latin Extended + (code >= 0x0370 and code <= 0x03FF) or -- Greek + (code >= 0x0400 and code <= 0x04FF) or -- Cyrillic + (code >= 0x05D0 and code <= 0x05EA) or -- Hebrew letters + ( -- Arabic + (code >= 0x0620 and code <= 0x063F) or + (code >= 0x0641 and code <= 0x064A) or + (code >= 0x066E and code <= 0x066F) or + (code >= 0x0671 and code <= 0x06D3) or + (code == 0x06D5) or + (code >= 0x0750 and code <= 0x077F) or + (code >= 0x08A0 and code <= 0x08BD) + ) or + (code >= 0x4E00 and code <= 0x9FFF) -- CJK + ) + then return false end end - return true end From 2ddb9e9fd20a7617bc4acc97ee1446537c3acc4c Mon Sep 17 00:00:00 2001 From: zykem <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:56:31 +0200 Subject: [PATCH 079/132] Remove unnecessary year > currentYear check --- [core]/esx_identity/server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 1642ab1f..098a174e 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -70,7 +70,7 @@ local function checkDOBFormat(dob) local minYear = currentYear - Config.MaxAge local maxYear = currentYear - 18 - if year < minYear or year > maxYear or year > currentYear then + if year < minYear or year > maxYear then return false end From a91e17dee98ccdb9e7e6f0cd53757efd6d71fe65 Mon Sep 17 00:00:00 2001 From: zykem <86602828+Zykem@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:58:04 +0200 Subject: [PATCH 080/132] Remove unnecessary code --- [core]/esx_identity/server/main.lua | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 098a174e..e4fdf104 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -70,16 +70,7 @@ local function checkDOBFormat(dob) local minYear = currentYear - Config.MaxAge local maxYear = currentYear - 18 - if year < minYear or year > maxYear then - return false - end - - if year == currentYear then - if month > currentDate.month or (month == currentDate.month and day > currentDate.day) then - return false - end - end - + if year < minYear or year > maxYear then return false end if month < 1 or month > 12 then return false end -- Days in each month (starting from January.) From 05769c6ac8a1c9db1f67eeef9004622057efc14f Mon Sep 17 00:00:00 2001 From: zykem <86602828+Zykem@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:02:50 +0200 Subject: [PATCH 081/132] Directly using os.date instead of localizing iit --- [core]/esx_identity/server/main.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index e4fdf104..44a99173 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -65,8 +65,7 @@ local function checkDOBFormat(dob) return false end - local currentDate = os.date("*t") - local currentYear = currentDate.year + local currentYear = os.date("*t").year local minYear = currentYear - Config.MaxAge local maxYear = currentYear - 18 From 04dd22b30b9004fe3c3a6c68b8bcc7a4fb7405f6 Mon Sep 17 00:00:00 2001 From: zykem <86602828+Zykem@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:03:36 +0200 Subject: [PATCH 082/132] Update main.lua --- [core]/esx_identity/server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 44a99173..0c8612de 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -55,7 +55,7 @@ end local function checkDOBFormat(dob) dob = tostring(dob) - local dayStr, monthStr, yearStr = dob:match('^(%d%d?)/(%d%d?)/(%d%d%d%d)$') + local dayStr, monthStr, yearStr = dob:match("^(%d%d?)/(%d%d?)/(%d%d%d%d)$") if not dayStr or not monthStr or not yearStr then return false end From 7a1eef95ff0091090966e4f27b19e421b5a1fbc3 Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Mon, 5 May 2025 19:32:19 +0300 Subject: [PATCH 083/132] refactor(esx_notify): redesign, code improvements. --- [core]/esx_notify/Notify.lua | 55 ++++--- [core]/esx_notify/fxmanifest.lua | 2 +- [core]/esx_notify/nui/css/style.css | 221 +++++++++++++++++++------- [core]/esx_notify/nui/index.html | 24 +-- [core]/esx_notify/nui/js/script.js | 237 ++++++++++++++++++++-------- 5 files changed, 383 insertions(+), 156 deletions(-) diff --git a/[core]/esx_notify/Notify.lua b/[core]/esx_notify/Notify.lua index 26dacd06..a0503f6f 100644 --- a/[core]/esx_notify/Notify.lua +++ b/[core]/esx_notify/Notify.lua @@ -3,11 +3,13 @@ local Debug = ESX.GetConfig().EnableDebug ---@param notificatonType string the notification type ---@param length number the length of the notification ---@param message any the message :D -local function Notify(notificatonType, length, message) +---@param title string optional title for the notification +local function Notify(notificatonType, length, message, title) if Debug then - print(("1 %s"):format(tostring(notificatonType))) - print(("2 %s"):format(tostring(length))) - print(("3 %s"):format(message)) + print("1 ".. tostring(notificatonType)) + print("2 "..tostring(length)) + print("3 "..message) + print("4 "..tostring(title)) end if type(notificatonType) ~= "string" then @@ -19,40 +21,49 @@ local function Notify(notificatonType, length, message) end if Debug then - print(("4 %s"):format(tostring(notificatonType))) - print(("5 %s"):format(tostring(length))) - print(("6 %s"):format(message)) + print("5 ".. tostring(notificatonType)) + print("6 "..tostring(length)) + print("7 "..message) + print("8 "..tostring(title)) + end + + if type(message) == "string" then + message = message:gsub("~br~", "
") end SendNuiMessage(json.encode({ - type = notificatonType, - length = length, + type = notificatonType or "info", + length = length or 5000, message = message or "ESX-Notify", + title = title or "New Notification" })) end -exports("Notify", Notify) -ESX.SecureNetEvent("ESX:Notify", Notify) +exports('Notify', Notify) +RegisterNetEvent("ESX:Notify", Notify) if Debug then RegisterCommand("oldnotify", function() - ---@diagnostic disable-next-line - ESX.ShowNotification("No Waypoint Set.", true, false, 140) - end, false) + ESX.ShowNotification('No Waypoint Set.', true, false, 140) + end) RegisterCommand("notify", function() - ESX.ShowNotification("You Recived ~br~ 1x ball~s~!", "success", 3000) - end, false) + ESX.ShowNotification("You Received
1x ball!", "success", 3000) + end) RegisterCommand("notify1", function() - ESX.ShowNotification("Well ~g~Done~s~!", "success", 3000) - end, false) + ESX.ShowNotification("Well ~g~Done~s~!", "success", 3000, "Achievement") + end) RegisterCommand("notify2", function() - ESX.ShowNotification("Information Recived", "info", 3000) - end, false) + ESX.ShowNotification("Information Received", "info", 3000, "System Info") + end) RegisterCommand("notify3", function() - ESX.ShowNotification("You Did something ~r~WRONG~s~!", "error", 3000) - end, false) + ESX.ShowNotification("You Did something ~r~WRONG~s~!", "error", 3000, "Error") + end) + + RegisterCommand("notify4", function() + ESX.ShowNotification("You Did something ~r~WRONG~s~!", "warning", 3000, "~y~Warning~s~") + end) end diff --git a/[core]/esx_notify/fxmanifest.lua b/[core]/esx_notify/fxmanifest.lua index a7e1b45d..55862551 100644 --- a/[core]/esx_notify/fxmanifest.lua +++ b/[core]/esx_notify/fxmanifest.lua @@ -2,7 +2,7 @@ fx_version 'adamant' lua54 'yes' game 'gta5' -version '1.12.4' +version '1.13.0' author 'ESX-Framework' description 'A beautiful and simple NUI notification system for ESX' diff --git a/[core]/esx_notify/nui/css/style.css b/[core]/esx_notify/nui/css/style.css index 6dc29290..acb1b142 100644 --- a/[core]/esx_notify/nui/css/style.css +++ b/[core]/esx_notify/nui/css/style.css @@ -1,87 +1,196 @@ -@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100&family=Poppins:wght@300;400;500;600;800&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap"); :root { - --color: #919191; + --background: rgba(20, 20, 20, 0.9); + --text-color: #ffffff; + --text-secondary: rgba(255, 255, 255, 0.7); + --success-color: #2ecc71; + --success-border: #1f9c4d; + --error-color: #e74c3c; + --error-border: #c0392b; + --info-color: #3498db; + --info-border: #2980b9; + --warning-color: #f39c12; + --warning-border: #d35400; } * { - border: 0; - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - width: 100vw; - height: 100vh; - color: var(--color); - font-weight: 100; - font-family: "Poppins", sans-serif; - overflow: hidden; + width: 100vw; + height: 100vh; + font-family: "Poppins", sans-serif; + overflow: hidden; } #root { - display: grid; - justify-content: center; + position: fixed; + right: 2rem; + top: 50%; + transform: translateY(-50%); + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 10px; + max-height: 80vh; + z-index: 1000; } -#root .notify { - display: flex; - position: relative; - flex: auto; - min-width: 20rem; - width: fit-content; - height: 3.5rem; - background: rgba(15, 15, 15, 0.9); - border-radius: 0.4rem; - margin-top: 0.5rem; - animation: anim 300ms ease-in-out; - align-items: center; +.notify { + display: flex; + width: 300px; + background: var(--background); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + position: relative; + padding: 12px; + align-items: center; + border-left: 4px solid #555; } -#root .innerText { - padding-left: 0.4rem; - padding-right: 0.4rem; - width: 100%; +.notify-success { + border-left-color: var(--success-border); } -#root .icon { - float: left; +.notify-error { + border-left-color: var(--error-border); } -#root .text { - display: inline-block; - color: #fff; - margin-left: 0.5rem; +.notify-info { + border-left-color: var(--info-border); } -#root .error { - color: #c0392b; - border: 2px solid #c0392b; +.notify-warning { + border-left-color: var(--warning-border); } -#root .success { - color: #2ecc71; - border: 2px solid #2ecc71; +.notify-icon-container { + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; } -#root .info { - color: #fb9b04; - border: 2px solid #fb9b04; +.hexagon { + position: relative; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + background: linear-gradient(145deg, var(--icon-color), rgba(0, 0, 0, 0.7)); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.hexagon::before { + content: ""; + position: absolute; + top: 3px; + left: 3px; + right: 3px; + bottom: 3px; + background: var(--background); + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + z-index: 1; +} + +.hexagon::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(145deg, var(--icon-color) 10%, transparent 70%); + opacity: 0.4; + clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); + z-index: 2; +} + +.hexagon .material-symbols-outlined { + color: var(--icon-color); + font-size: 20px; + position: relative; + z-index: 3; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5)); + display: flex; + align-items: center; + justify-content: center; + height: 24px; + width: 24px; + margin-top: -1px; +} + +.notify-content { + flex: 1; + position: relative; + padding-bottom: 8px; +} + +.notify-title { + color: var(--text-color); + font-size: 14px; + font-weight: 600; + margin-bottom: 2px; +} + +.notify-text { + color: var(--text-secondary); + font-size: 12px; + font-weight: 400; +} + +.notify-progress { + position: absolute; + bottom: 0; + left: 0; + height: 2px; + width: 100%; + border-radius: 1px; } .material-symbols-outlined { - font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 48; + font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 48; } -@keyframes anim { - 0% { - transform: scaleY(0); - } - 80% { - transform: scaleY(1.1); - } - 100% { - transform: scaleY(1); - } +.notify-warning .material-symbols-outlined, +.notify-error .material-symbols-outlined { + margin-top: -2px; +} + +.fadeIn { + animation: fadeIn 0.3s ease-in-out; +} + +.fadeOut { + animation: fadeOut 0.5s ease-in-out; + opacity: 0; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes fadeOut { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(50px); + } } diff --git a/[core]/esx_notify/nui/index.html b/[core]/esx_notify/nui/index.html index 69800a2d..76a62faa 100644 --- a/[core]/esx_notify/nui/index.html +++ b/[core]/esx_notify/nui/index.html @@ -1,15 +1,15 @@ - - - - - - ESX Notify - - -
- -
- + + + + + ESX Notifications + + +
+ +
+ + diff --git a/[core]/esx_notify/nui/js/script.js b/[core]/esx_notify/nui/js/script.js index 64002521..b2a24bd3 100644 --- a/[core]/esx_notify/nui/js/script.js +++ b/[core]/esx_notify/nui/js/script.js @@ -1,83 +1,190 @@ -const w = window; +const w = window -// Gets the current icon it needs to use. const types = { - ["success"]: { - ["icon"]: "check_circle", - }, - ["error"]: { - ["icon"]: "error", - }, - ["info"]: { - ["icon"]: "info", - }, -}; + ["success"]: { + ["icon"]: "check_circle", + ["frequency"]: 800, + ["duration"]: 100, + ["color"]: "#2ecc71", + ["borderColor"]: "#1f9c4d", + }, + ["error"]: { + ["icon"]: "error", + ["frequency"]: 300, + ["duration"]: 150, + ["color"]: "#e74c3c", + ["borderColor"]: "#c0392b", + }, + ["info"]: { + ["icon"]: "info", + ["frequency"]: 600, + ["duration"]: 100, + ["color"]: "#3498db", + ["borderColor"]: "#2980b9", + }, + ["warning"]: { + ["icon"]: "warning_amber", + ["frequency"]: 450, + ["duration"]: 125, + ["color"]: "#f39c12", + ["borderColor"]: "#d35400", + }, +} // the color codes example `i ~r~love~s~ donuts` const codes = { - "~r~": "#c0392b", - "~b~": "#378cbf", - "~g~": "#2ecc71", - "~y~": "yellow", - "~p~": "purple", - "~c~": "grey", - "~m~": "#212121", - "~u~": "black", - "~o~": "#fb9b04", -}; + "~r~": "#c0392b", + "~b~": "#378cbf", + "~g~": "#2ecc71", + "~y~": "yellow", + "~p~": "purple", + "~c~": "grey", + "~m~": "#212121", + "~u~": "black", + "~o~": "#fb9b04", +} + +// Audio context for sound effects +let audioContext + +// Initialize audio context on user interaction +document.addEventListener("click", initAudioContext, { once: true }) +document.addEventListener("keydown", initAudioContext, { once: true }) + +function initAudioContext() { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)() + } +} + +function playNotificationSound(type) { + if (!audioContext) initAudioContext() + + const typeConfig = types[type] || types["info"] + const oscillator = audioContext.createOscillator() + const gainNode = audioContext.createGain() + + oscillator.type = "sine" + oscillator.frequency.setValueAtTime(typeConfig.frequency, audioContext.currentTime) + + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime) + gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + typeConfig.duration / 1000) + + oscillator.connect(gainNode) + gainNode.connect(audioContext.destination) + + oscillator.start() + oscillator.stop(audioContext.currentTime + typeConfig.duration / 1000) +} w.addEventListener("message", (event) => { - notification({ - type: event.data.type, - message: event.data.message, - length: event.data.length, - }); -}); + notification({ + type: event.data.type, + title: event.data.title || "New Notification", + message: event.data.message, + length: event.data.length, + }) +}) const replaceColors = (str, obj) => { - let strToReplace = str; + let strToReplace = str - for (let id in obj) { - strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id]); + for (const id in obj) { + strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id]) + } + + return strToReplace +} + +const sanitizeHTML = (str) => { + const temp = document.createElement("div") + temp.textContent = str + return temp.innerHTML +} + +const processLineBreaks = (str) => { + // Replace
tags with actual line breaks + return str.replace(/<br>/g, "
") +} + +const notification = (data) => { + if (typeof $ === "undefined") { + console.error("jQuery is not loaded. Please ensure jQuery is included in your project.") + return + } + + if (data.message) { + // Remove any standalone ~s~ tags (not preceded by a color code) + data.message = data.message.replace(/~s~/g, "") + } + + if (data.title) { + // Remove any standalone ~s~ tags (not preceded by a color code) + data.title = data.title.replace(/~s~/g, "") + } + + let sanitizedTitle = data.title ? sanitizeHTML(data.title) : "" + let sanitizedMessage = data.message ? sanitizeHTML(data.message) : "" + + if (data.title) { + for (const color in codes) { + if (sanitizedTitle.includes(color)) { + const objArr = {} + objArr[color] = `` + objArr["~s~"] = "" + sanitizedTitle = replaceColors(sanitizedTitle, objArr) + } } + } - return strToReplace; -}; - -notification = (data) => { - for (color in codes) { - if (data["message"].includes(color)) { - let objArr = {}; - objArr[color] = ``; - objArr["~s~"] = ""; - - let newStr = replaceColors(data["message"], objArr); - - data["message"] = newStr; - } + for (const color in codes) { + if (sanitizedMessage.includes(color)) { + const objArr = {} + objArr[color] = `` + objArr["~s~"] = "" + sanitizedMessage = replaceColors(sanitizedMessage, objArr) } + } - const id = Math.floor(Math.random() * Math.random()); - const notification = $(` -
-
- ${types[data.type] ? types[data.type]["icon"] : types["info"]["icon"]} -

${data["message"]}

-
+ sanitizedMessage = processLineBreaks(sanitizedMessage) + sanitizedTitle = processLineBreaks(sanitizedTitle) + + const id = Math.floor(Math.random() * 100000) + const typeInfo = types[data.type] || types["info"] + const duration = data.length || 3000 + + const notificationElement = $(` +
+
+
+ ${typeInfo.icon}
- `).appendTo(`#root`); +
+
+

${sanitizedTitle}

+

${sanitizedMessage}

+
+
+
+ `).appendTo(`#root`) + $(`#${id} .notify-progress`).css({ + transition: `width ${duration}ms linear`, + width: "0%", + }) + + playNotificationSound(data.type) + + setTimeout(() => { + $(`#${id} .notify-progress`).css("width", "100%") + }, 10) + + setTimeout(() => { + $(`#${id}`).removeClass("fadeIn").addClass("fadeOut") setTimeout(() => { - document.getElementById(id).classList.remove("fadeIn"); - }, 300); + $(`#${id}`).remove() + }, 500) + }, duration) - setTimeout(() => { - document.getElementById(id).classList.add("fadeOut"); - - setTimeout(() => { - document.getElementById(id).remove(); - }, 400); - },data.length); - - return notification; -}; + return notificationElement +} From 59d2e9a839249f26881f1aa263d3e75343b5996e Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Mon, 5 May 2025 19:49:07 +0300 Subject: [PATCH 084/132] fix(readme esx_notify): --- [core]/esx_notify/readme.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/[core]/esx_notify/readme.md b/[core]/esx_notify/readme.md index f6bc4c9d..430f8a34 100644 --- a/[core]/esx_notify/readme.md +++ b/[core]/esx_notify/readme.md @@ -1,33 +1,48 @@ -

[ESX] Notify

Discord - Website - Documentation +`

`[ESX] Notify```

``

`````Discord`` - ``Website`` - ``Documentation`````` A beautiful and simple NUI notification system for ESX # Example Code -

Change style and time

+`

`Change style and time`

` ```lua ---usage: message/type/length ESX.ShowNotification("message here", "error", 3000) ESX.ShowNotification("message here", "success", 3000) ESX.ShowNotification("message here", "info", 3000) +ESX.ShowNotification("message here", "warning", 3000) ESX.ShowNotification("text here") -- Default will time and type will be info/3000 + +-- With title (optional 4th parameter) +ESX.ShowNotification("message here", "success", 3000, "Achievement") ``` -

Export Usage

+`

`Export Usage`

` ```lua +-- Basic usage exports["esx_notify"]:Notify("info", 3000, "message here") + +-- With title +exports["esx_notify"]:Notify("success", 3000, "Item purchased!", "Shop") + +-- With line break +exports["esx_notify"]:Notify("warning", 4000, "Inventory full!~br~Some items were dropped.", "Warning") ``` -

Event Usage

+`

`Event Usage`

` ```lua +-- Basic usage TriggerEvent("ESX:Notify", "info", 3000, "message here") + +-- With title +TriggerEvent("ESX:Notify", "error", 5000, "You don't have enough money!", "Transaction Failed") ``` -

Color Code Usage

+`

`Color Code Usage`

` ```lua ~r~ = Red @@ -41,15 +56,14 @@ TriggerEvent("ESX:Notify", "info", 3000, "message here") ~o~ = Orange ESX.ShowNotification("I i ~r~love~s~ donuts", "success", 3000) + +-- Line break example +ESX.ShowNotification("You received ~br~1x ~g~ball~s~!", "success", 3000, "Item Received") ``` # Previews -![Preview_1](https://cdn.discordapp.com/attachments/944789399852417096/997890963445927977/unknown.png) - -![Preview_2_zoom](https://cdn.discordapp.com/attachments/944789399852417096/997892214053163148/unknown.png) - -![Preview_2](https://cdn.discordapp.com/attachments/944789399852417096/997891726326898909/unknown.png) +![Preview 1](https://r2.fivemanage.com/gWoWHGuKZdsK8PFzaVuGC/image_2025-05-05_194204916.png) ## Legal @@ -61,4 +75,4 @@ This program Is free software: you can redistribute it And/Or modify it under th 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 . +You should have received a copy Of the GNU General Public License along with this program. If Not, see ``. \ No newline at end of file From 76ccc4790ec3c732f70051299cc5750c54a93698 Mon Sep 17 00:00:00 2001 From: Sefa <88948517+lod9@users.noreply.github.com> Date: Wed, 7 May 2025 17:12:58 +0300 Subject: [PATCH 085/132] esx_menu_default update --- [core]/esx_menu_default/fxmanifest.lua | 2 +- [core]/esx_menu_default/html/css/app.css | 233 ++++++++++++++++------- [core]/esx_menu_default/html/js/app.js | 39 +++- [core]/esx_menu_default/html/ui.html | 31 +-- 4 files changed, 216 insertions(+), 89 deletions(-) diff --git a/[core]/esx_menu_default/fxmanifest.lua b/[core]/esx_menu_default/fxmanifest.lua index 851fbcd6..2dc064e6 100644 --- a/[core]/esx_menu_default/fxmanifest.lua +++ b/[core]/esx_menu_default/fxmanifest.lua @@ -3,7 +3,7 @@ fx_version 'adamant' game 'gta5' description 'A basic menu system for ESX Legacy.' lua54 'yes' -version '1.12.2' +version '1.12.4' client_scripts { '@es_extended/imports.lua', 'client/main.lua' } diff --git a/[core]/esx_menu_default/html/css/app.css b/[core]/esx_menu_default/html/css/app.css index a02d2a1d..617fd4a3 100644 --- a/[core]/esx_menu_default/html/css/app.css +++ b/[core]/esx_menu_default/html/css/app.css @@ -1,109 +1,198 @@ -@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@500;600&display=swap"); +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); ::-webkit-scrollbar { display: none; } -.menu { - font-family: "Poppins", sans-serif; - min-width: 350px; - color: #fff; +.menu-container { position: absolute; - background: rgba(15, 15, 15, 0.0); - text-align: center; - border-radius: 5px; + min-width: 20.8333vw; + height: fit-content; + display: flex; + flex-direction: column; + gap: 0.9259vh; + transform-origin: center center; +} + +.menu-container.align-left { + left: 0; + top: 50%; + transform: translateY(-50%) scale(var(--scale, 1)); + transform-origin: left center; +} + +.menu-container.align-top-left { + left: 4rem; + top: 0.8rem; + transform: scale(var(--scale, 1)); + transform-origin: left top; +} + +.menu-container.align-top { + left: 50%; + top: 0.8rem; + transform: translateX(-50%) scale(var(--scale, 1)); + transform-origin: center top; +} + +.menu-container.align-top-right { + right: 3rem; + top: 0.8rem; + transform: scale(var(--scale, 1)); + transform-origin: right top; +} + +.menu-container.align-right { + right: 0; + top: 50%; + transform: translateY(-50%) scale(var(--scale, 1)); + transform-origin: right center; +} + +.menu-container.align-bottom-right { + right: 3rem; + bottom: 0.8rem; + transform: scale(var(--scale, 1)); + transform-origin: right bottom; +} + +.menu-container.align-bottom { + left: 50%; + bottom: 0.8rem; + transform: translateX(-50%) scale(var(--scale, 1)); + transform-origin: center bottom; +} + +.menu-container.align-bottom-left { + left: 4rem; + bottom: 0.8rem; + transform: scale(var(--scale, 1)); + transform-origin: left bottom; +} + +.menu-container.align-center { + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(var(--scale, 1)); + transform-origin: center center; +} + +.menu-buttons { + display: flex; + justify-content: center; + gap: .5208vw; + color: #FFF; + font-family: Poppins; + font-size: .8333vw; + font-style: normal; + font-weight: 700; + line-height: normal; +} + +.menu-button { + display: flex; + align-items: center; + justify-content: center; + gap: .4167vw; + cursor: pointer; + transition: all 0.3s ease; +} + +.backspace-btn, .close-btn { + width: 10.6771vw; + height: 6.0185vh; + border-radius: .2604vw; + background: #161616; +} + +.menu-button:hover { + background-color: #252525; + transform: scale(1.02); +} + +.menu { + display: flex; + flex-direction: column; + gap: 0.9259vh; + font-family: "Poppins", sans-serif; + color: #fff; + padding: 8px; + background-color: #161616; + border-radius: 4px; } .head { display: block; overflow: hidden; - padding-bottom: 3px; + min-width: 20.4688vw; + min-height: 6.0185vh; + padding-bottom: 0.2778vh; text-align: center; white-space: nowrap; - background: rgba(10, 10, 10, 1); - border-bottom: 2px solid #fb9b04; + border-radius: .2604vw; + background: #FB9B04; } .menu .head { - text-align: center; - height: 28px; - color: #ffffff; + display: flex; + justify-content: center; + align-items: center; + color: #252525; + font-family: Poppins; + font-size: 1.25vw; + font-style: normal; font-weight: 700; - font-size: 20px; + line-height: normal; + height: 100%; } .menu .menu-items { - max-height: 450px; + display: flex; + flex-direction: column; + gap: 0.9259vh; + max-height: 41.6667vh; overflow-y: auto; font-weight: 550; -} - -.menu-items { - margin-bottom: 2px; + margin-bottom: 0.1852vh; } .menu .menu-items .menu-item { - display: block; - padding: 7px; - font-size: 13px; - height: 16px; - text-indent: 5px; - background: rgba(30, 30, 30, 0.9); - color: rgb(243, 243, 243); + display: flex; + justify-content: center; + align-items: center; + gap: 1.0833vw; + min-width: 20.4688vw; + min-height: 6.0185vh; + border-radius: .2604vw; + background: #252525; + color: #FFF; + font-family: Poppins; + font-size: .8333vw; + font-style: normal; + font-weight: 700; + line-height: normal; } - .menu .menu-items .menu-item.selected { - border: 1px solid #fb9c04ec; - background: rgba(15, 15, 15, 0.9); - color: rgba(243, 243, 243); - letter-spacing: 0.2px; - font-weight: 500; + border: 2px solid #FB9B04; + background: #252525; } -.menu.align-left { - left: 0; - top: 50%; +.menu-item { + display: flex; + align-items: center; + padding: .4167vw 0.9259vh; } -.menu.align-top-left { - left: 4rem; - top: 0; +.menu-item-icon { + font-size: 1vw; + text-align: center; } -.menu.align-top { - left: 50%; - top: 0; +.menu-item.selected { + background-color: rgba(255, 255, 255, 0.1); } -.menu.align-top-right { - right: 3rem; - top: 0; -} - -.menu.align-right { - right: 0; - top: 50%; -} - -.menu.align-bottom-right { - right: 3rem; - bottom: 0; -} - -.menu.align-bottom { - left: 50%; - bottom: 0; - transform: translate(0, -50%); -} - -.menu.align-bottom-left { - left: 4rem; - bottom: 0; -} - -.menu.align-center { - left: 50%; - top: 35%; - transform: translate(-50%, 50%); +.fas { + color: white; } diff --git a/[core]/esx_menu_default/html/js/app.js b/[core]/esx_menu_default/html/js/app.js index da6a906a..852140ea 100644 --- a/[core]/esx_menu_default/html/js/app.js +++ b/[core]/esx_menu_default/html/js/app.js @@ -1,21 +1,31 @@ (function () { let MenuTpl = - '"; window.ESX_MENU = {}; ESX_MENU.ResourceName = "esx_menu_default"; ESX_MENU.opened = {}; ESX_MENU.focus = []; ESX_MENU.pos = {}; + ESX_MENU.locales = { + backspace: "BACK", + select: "SELECT" + }; ESX_MENU.open = function (namespace, name, data) { if (typeof ESX_MENU.opened[namespace] === "undefined") { @@ -30,6 +40,12 @@ ESX_MENU.pos[namespace] = {}; } + if (data.locales) { + ESX_MENU.locales = data.locales; + } + + data.scale = data.scale || 1; + for (let i = 0; i < data.elements.length; i++) { if (typeof data.elements[i].type === "undefined") { data.elements[i].type = "default"; @@ -39,6 +55,7 @@ data._index = ESX_MENU.focus.length; data._namespace = namespace; data._name = name; + data.locales = ESX_MENU.locales; for (let i = 0; i < data.elements.length; i++) { data.elements[i]._namespace = namespace; @@ -362,5 +379,21 @@ window.addEventListener("message", (event) => { onData(event.data); }); + + document.addEventListener('click', function(event) { + if (event.target.closest('.backspace-btn')) { + let focused = ESX_MENU.getFocused(); + if (typeof focused != "undefined") { + ESX_MENU.cancel(focused.namespace, focused.name); + } + } + + if (event.target.closest('.close-btn')) { + let focused = ESX_MENU.getFocused(); + if (typeof focused != "undefined") { + ESX_MENU.close(focused.namespace, focused.name); + } + } + }); }; })(); diff --git a/[core]/esx_menu_default/html/ui.html b/[core]/esx_menu_default/html/ui.html index 2cb88e7c..8f3ea43e 100644 --- a/[core]/esx_menu_default/html/ui.html +++ b/[core]/esx_menu_default/html/ui.html @@ -1,16 +1,21 @@ - - - - - - - - + + + + + + + - - - - - + + + + + + + + + \ No newline at end of file From 8f07d854eb677f5f1376e90090d3e51bbcfddd5d Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Wed, 7 May 2025 12:23:25 -0400 Subject: [PATCH 086/132] refactor(main.lua): use ESX.IsValidLocaleString with configurable character sets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced the manual character validation logic in checkNameFormat with ESX.IsValidLocaleString to centralize string validation and allow configuration of valid character sets per locale. Added support for Config.ValidCharacterSets to enable or disable specific Unicode blocks such as Greek, Cyrillic, Hebrew, Arabic, or CJK (Chinese, Japanese, Korean). This change introduces greater flexibility for multilingual servers while maintaining basic Latin character support by default. ⚠️ Behavior changes: Characters that were previously allowed may now be rejected depending on the server’s locale configuration. --- [core]/esx_identity/server/main.lua | 31 +---------------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/[core]/esx_identity/server/main.lua b/[core]/esx_identity/server/main.lua index 5dcc4d7a..9ee283da 100644 --- a/[core]/esx_identity/server/main.lua +++ b/[core]/esx_identity/server/main.lua @@ -84,37 +84,8 @@ local function formatDate(str) return date end -local function checkValidCharacter(str) - for _, code in utf8.codes(str) do - if not ( - - (code >= 0x0041 and code <= 0x005A) or -- Basic Latin uppercase - (code >= 0x0061 and code <= 0x007A) or -- Basic Latin lowercase - (code == 0x0020 or code == 0x002D) or -- Space or dash - (code >= 0x00C0 and code <= 0x02AF) or -- Latin Extended - (code >= 0x0370 and code <= 0x03FF) or -- Greek - (code >= 0x0400 and code <= 0x04FF) or -- Cyrillic - (code >= 0x05D0 and code <= 0x05EA) or -- Hebrew letters - ( -- Arabic - (code >= 0x0620 and code <= 0x063F) or - (code >= 0x0641 and code <= 0x064A) or - (code >= 0x066E and code <= 0x066F) or - (code >= 0x0671 and code <= 0x06D3) or - (code == 0x06D5) or - (code >= 0x0750 and code <= 0x077F) or - (code >= 0x08A0 and code <= 0x08BD) - ) or - (code >= 0x4E00 and code <= 0x9FFF) -- CJK - ) - then - return false - end - end - return true -end - local function checkNameFormat(name) - if checkValidCharacter(name) then + if ESX.IsValidLocaleString(name) then local stringLength = string.len(name) return stringLength > 0 and stringLength < Config.MaxNameLength end From e9fe5babdb783b997bda7ab8689000d412b10909 Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Wed, 7 May 2025 12:31:14 -0400 Subject: [PATCH 087/132] Refactor: Add ESX.IsValidLocaleString function to validate locale-based characters This commit introduces the new `ESX.IsValidLocaleString` function, which checks if a given string contains valid characters based on the configured locale. It validates characters from various locales, such as Greek, Cyrillic, Hebrew, Arabic, and Chinese, in addition to basic Latin characters. The function is flexible, allowing for the inclusion of additional character sets via the `Config.ValidCharacterSets` configuration. Changes include: - A new function, `ESX.IsValidLocaleString`, added to validate characters based on the locale. - The validation takes into account default Latin ranges as well as specific ranges for supported languages. - The function supports dynamic locale configurations by using `Config.ValidCharacterSets`. This refactor improves handling of locale-specific characters and provides a way to easily extend validation for new locales as needed. --- [core]/es_extended/shared/functions.lua | 69 +++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/[core]/es_extended/shared/functions.lua b/[core]/es_extended/shared/functions.lua index 184def1b..c24ead38 100644 --- a/[core]/es_extended/shared/functions.lua +++ b/[core]/es_extended/shared/functions.lua @@ -210,3 +210,72 @@ function ESX.Await(conditionFunc, errorMessage, timeoutMs) return false end + +---@param str string +---@return boolean +function ESX.IsValidLocaleString(str) + local locale = string.lower(Config.Locale) + + local defaultRanges ={ + {0x0041, 0x005A}, -- Basic Latin uppercase + {0x0061, 0x007A}, -- Basic Latin lowercase + {0x0020, 0x0020}, -- Space + {0x002D, 0x002D}, -- Dash + {0x00C0, 0x02AF} -- Latin Extended + } + + local localeRanges = { + ["el"] = { {0x0370, 0x03FF} }, -- Greek + ["sr"] ={ {0x0400, 0x04FF} }, -- Cyrillic + ["he"] ={ {0x05D0, 0x05EA} }, -- Hebrew letters + ["ar"] = { + {0x0620, 0x063F}, -- Arabic + {0x0641, 0x064A}, + {0x066E, 0x066F}, + {0x0671, 0x06D3}, + {0x06D5, 0x06D5}, + {0x0750, 0x077F}, + {0x08A0, 0x08BD} + }, + ["zh-cn"] ={ {0x4E00, 0x9FFF} } -- CJK + } + + local validRanges = {} + + for i = 1, #defaultRanges do + validRanges[#validRanges + 1] = defaultRanges[i] + end + + if localeRanges[locale] then + for i = 1, #localeRanges[locale] do + validRanges[#validRanges + 1] = localeRanges[locale][i] + end + end + + if Config.ValidCharacterSets then + for charset, enabled in pairs(Config.ValidCharacterSets) do + if enabled and charset ~= locale and localeRanges[charset] then + for i = 1, #localeRanges[charset] do + validRanges[#validRanges + 1] = localeRanges[charset][i] + end + end + end + end + + for _, code in utf8.codes(str) do + local isValid = false + + for _, range in ipairs(validRanges) do + if code >= range[1] and code <= range[2] then + isValid = true + break + end + end + + if not isValid then + return false + end + end + + return true +end From e30d4e49f5279fc1f6ba5bf3b5cabdd711a19105 Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Wed, 7 May 2025 12:34:42 -0400 Subject: [PATCH 088/132] Added support for additional character sets and language customization This update introduces a new configuration option `Config.ValidCharacterSets` to allow support for additional character sets in case the server is multilingual. This allows the server to handle character sets such as Greek, Cyrillic, Hebrew, Arabic, and East Asian languages (Chinese, Japanese, Korean). By default, these character sets are disabled (`false`). The user can enable them if needed, depending on the language requirements of the server. --- [core]/es_extended/shared/config/main.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/[core]/es_extended/shared/config/main.lua b/[core]/es_extended/shared/config/main.lua index 108d292d..8932e30c 100644 --- a/[core]/es_extended/shared/config/main.lua +++ b/[core]/es_extended/shared/config/main.lua @@ -39,6 +39,14 @@ Config.AdminGroups = { ["admin"] = true, } +Config.ValidCharacterSets = { -- Only enable additional charsets if your server is multilingual. By default everything is false. + ['el'] = false, -- Greek + ['sr'] = false, -- Cyrillic + ['he'] = false, -- Hebrew + ['ar'] = false, -- Arabic + ['zh-cn'] = false -- Chinese, Japanese, Korean +} + Config.EnablePaycheck = true -- enable paycheck Config.LogPaycheck = false -- Logs paychecks to a nominated Discord channel via webhook (default is false) Config.EnableSocietyPayouts = false -- pay from the society account that the player is employed at? Requirement: esx_society From 01e29f6e9b28310dd151caf5694016995e3e7d67 Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Thu, 8 May 2025 10:33:03 -0400 Subject: [PATCH 089/132] refactor(locale): improve performance and type safety in IsValidLocaleString - Replaced `ipairs` with a numeric `for` loop in the UTF-8 validation step for better performance. - Stored `validRanges[i]` in a local `range` variable to avoid duplicate table lookups. - Added a call to `ESX.ValidateType(str, 'string')` to ensure input is of type string before processing. These changes improve both performance and code safety. --- [core]/es_extended/shared/functions.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/shared/functions.lua b/[core]/es_extended/shared/functions.lua index c24ead38..7da3e887 100644 --- a/[core]/es_extended/shared/functions.lua +++ b/[core]/es_extended/shared/functions.lua @@ -214,6 +214,10 @@ end ---@param str string ---@return boolean function ESX.IsValidLocaleString(str) + if not ESX.ValidateType(str, 'string') then + return false + end + local locale = string.lower(Config.Locale) local defaultRanges ={ @@ -265,7 +269,8 @@ function ESX.IsValidLocaleString(str) for _, code in utf8.codes(str) do local isValid = false - for _, range in ipairs(validRanges) do + for i = 1, #validRanges do + local range = validRanges[i] if code >= range[1] and code <= range[2] then isValid = true break From 099bd7dd4645e972edf662f7df54314459219959 Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Thu, 8 May 2025 13:03:40 -0400 Subject: [PATCH 090/132] refactor(functions.lua): use table.unpack to initialize validRanges Replaces manual loop for copying `defaultRanges` into `validRanges` with `table.unpack`. Avoids unnecessary iteration and duplication. --- [core]/es_extended/shared/functions.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/[core]/es_extended/shared/functions.lua b/[core]/es_extended/shared/functions.lua index 7da3e887..32c7ea33 100644 --- a/[core]/es_extended/shared/functions.lua +++ b/[core]/es_extended/shared/functions.lua @@ -244,11 +244,7 @@ function ESX.IsValidLocaleString(str) ["zh-cn"] ={ {0x4E00, 0x9FFF} } -- CJK } - local validRanges = {} - - for i = 1, #defaultRanges do - validRanges[#validRanges + 1] = defaultRanges[i] - end + local validRanges = { table.unpack(defaultRanges) } if localeRanges[locale] then for i = 1, #localeRanges[locale] do From dc512f97d29da782449dc1d4d88e6d4fbdf8a4b2 Mon Sep 17 00:00:00 2001 From: YOMAN1792 <94007829+YOMAN1792@users.noreply.github.com> Date: Thu, 8 May 2025 14:18:57 -0400 Subject: [PATCH 091/132] feat(functions.lua): add optional digit support to IsValidLocaleString Modified the ESX.IsValidLocaleString function to accept an optional `allowDigits` parameter. When true, the function now allows numerical characters (0-9) in addition to the configured character ranges. This improves string validation flexibility across locales. --- [core]/es_extended/shared/functions.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/shared/functions.lua b/[core]/es_extended/shared/functions.lua index 32c7ea33..8e326150 100644 --- a/[core]/es_extended/shared/functions.lua +++ b/[core]/es_extended/shared/functions.lua @@ -212,8 +212,9 @@ function ESX.Await(conditionFunc, errorMessage, timeoutMs) end ---@param str string +---@param allowDigits boolean? Allow numbers if necessary ---@return boolean -function ESX.IsValidLocaleString(str) +function ESX.IsValidLocaleString(str, allowDigits) if not ESX.ValidateType(str, 'string') then return false end @@ -228,6 +229,10 @@ function ESX.IsValidLocaleString(str) {0x00C0, 0x02AF} -- Latin Extended } + if allowDigits then + defaultRanges[#defaultRanges + 1] = {0x0030, 0x0039} -- 0-9 Numbers + end + local localeRanges = { ["el"] = { {0x0370, 0x03FF} }, -- Greek ["sr"] ={ {0x0400, 0x04FF} }, -- Cyrillic From 7d61f378e2d2520cf4575674e29ca7919ffe0f17 Mon Sep 17 00:00:00 2001 From: zlexif <146517988+auradevelopment5m@users.noreply.github.com> Date: Thu, 8 May 2025 21:53:34 +0300 Subject: [PATCH 092/132] fix:('added title parameter to ESX.ShowNotification. --- [core]/es_extended/client/functions.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 8a577e7e..1ad0edc3 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -152,9 +152,10 @@ end ---@param message string The message to show ---@param notifyType? string The type of notification to show ---@param length? number The length of the notification +---@param title? string The title of the notification ---@return nil -function ESX.ShowNotification(message, notifyType, length) - return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message) +function ESX.ShowNotification(message, notifyType, length, title) + return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message, title) end function ESX.TextUI(...) From 9d91ba01952a65900dbf7d9f78b16c3e98cf3c55 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 9 May 2025 07:51:40 +0530 Subject: [PATCH 093/132] Refatored Set vehicle Properties --- [core]/es_extended/client/functions.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 8a577e7e..42a2a912 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -997,12 +997,23 @@ function ESX.Game.SetVehicleProperties(vehicle, props) if props.customSecondaryColor ~= nil then SetVehicleCustomSecondaryColour(vehicle, props.customSecondaryColor[1], props.customSecondaryColor[2], props.customSecondaryColor[3]) end + if props.color1 ~= nil then - SetVehicleColours(vehicle, props.color1, colorSecondary) + if type(props.color1) == "table" then + SetVehicleCustomPrimaryColour(vehicle, props.color1[1], props.color1[2], props.color1[3]) + else + SetVehicleColours(vehicle, props.color1, colorSecondary) + end end + if props.color2 ~= nil then - SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) + if type(props.color2) == "table" then + SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3]) + else + SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) + end end + if props.pearlescentColor ~= nil then SetVehicleExtraColours(vehicle, props.pearlescentColor, wheelColor) end From 542b143e750bc6e7de6709274a65074dc2d25320 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 9 May 2025 08:34:46 +0530 Subject: [PATCH 094/132] Removed duplication of Custom primary and secondary colors --- [core]/es_extended/client/functions.lua | 9 --------- 1 file changed, 9 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 42a2a912..71ed4882 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -991,13 +991,6 @@ function ESX.Game.SetVehicleProperties(vehicle, props) if props.dirtLevel ~= nil then SetVehicleDirtLevel(vehicle, props.dirtLevel + 0.0) end - if props.customPrimaryColor ~= nil then - SetVehicleCustomPrimaryColour(vehicle, props.customPrimaryColor[1], props.customPrimaryColor[2], props.customPrimaryColor[3]) - end - if props.customSecondaryColor ~= nil then - SetVehicleCustomSecondaryColour(vehicle, props.customSecondaryColor[1], props.customSecondaryColor[2], props.customSecondaryColor[3]) - end - if props.color1 ~= nil then if type(props.color1) == "table" then SetVehicleCustomPrimaryColour(vehicle, props.color1[1], props.color1[2], props.color1[3]) @@ -1005,7 +998,6 @@ function ESX.Game.SetVehicleProperties(vehicle, props) SetVehicleColours(vehicle, props.color1, colorSecondary) end end - if props.color2 ~= nil then if type(props.color2) == "table" then SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3]) @@ -1013,7 +1005,6 @@ function ESX.Game.SetVehicleProperties(vehicle, props) SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) end end - if props.pearlescentColor ~= nil then SetVehicleExtraColours(vehicle, props.pearlescentColor, wheelColor) end From 9b0ffead471c6574d0c8998b61af17f10a5a8207 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 9 May 2025 08:48:44 +0530 Subject: [PATCH 095/132] Revamped GetVehicleProperties in compatible with the removal of customPrimay and secondary colors --- [core]/es_extended/client/functions.lua | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 71ed4882..aa5f572e 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -805,14 +805,19 @@ function ESX.Game.GetVehicleProperties(vehicle) return end - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) + local colorPrimary, colorSecondary = GetVehicleColours(vehicle) ---@type number | number[], number | number[] local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) local hasCustomPrimaryColor = GetIsVehiclePrimaryColourCustom(vehicle) + local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle) local dashboardColor = GetVehicleDashboardColor(vehicle) local interiorColor = GetVehicleInteriorColour(vehicle) - local customPrimaryColor = nil + if hasCustomPrimaryColor then - customPrimaryColor = { GetVehicleCustomPrimaryColour(vehicle) } + colorPrimary = { GetVehicleCustomPrimaryColour(vehicle) } + end + + if hasCustomSecondaryColor then + colorSecondary = { GetVehicleCustomSecondaryColour(vehicle) } end local hasCustomXenonColor, customXenonColorR, customXenonColorG, customXenonColorB = GetVehicleXenonLightsCustomColor(vehicle) @@ -821,12 +826,6 @@ function ESX.Game.GetVehicleProperties(vehicle) customXenonColor = { customXenonColorR, customXenonColorG, customXenonColorB } end - local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle) - local customSecondaryColor = nil - if hasCustomSecondaryColor then - customSecondaryColor = { GetVehicleCustomSecondaryColour(vehicle) } - end - local extras = {} for extraId = 0, 20 do if DoesExtraExist(vehicle, extraId) then @@ -879,8 +878,6 @@ function ESX.Game.GetVehicleProperties(vehicle) dirtLevel = ESX.Math.Round(GetVehicleDirtLevel(vehicle), 1), color1 = colorPrimary, color2 = colorSecondary, - customPrimaryColor = customPrimaryColor, - customSecondaryColor = customSecondaryColor, pearlescentColor = pearlescentColor, wheelColor = wheelColor, From 30a13c572622896984b16bb09c06dd1377baa8b8 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 9 May 2025 08:54:35 +0530 Subject: [PATCH 096/132] Revert "Revamped GetVehicleProperties in compatible with the removal of customPrimay and secondary colors" This reverts commit 9b0ffead471c6574d0c8998b61af17f10a5a8207. --- [core]/es_extended/client/functions.lua | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index aa5f572e..71ed4882 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -805,19 +805,14 @@ function ESX.Game.GetVehicleProperties(vehicle) return end - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) ---@type number | number[], number | number[] + local colorPrimary, colorSecondary = GetVehicleColours(vehicle) local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) local hasCustomPrimaryColor = GetIsVehiclePrimaryColourCustom(vehicle) - local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle) local dashboardColor = GetVehicleDashboardColor(vehicle) local interiorColor = GetVehicleInteriorColour(vehicle) - + local customPrimaryColor = nil if hasCustomPrimaryColor then - colorPrimary = { GetVehicleCustomPrimaryColour(vehicle) } - end - - if hasCustomSecondaryColor then - colorSecondary = { GetVehicleCustomSecondaryColour(vehicle) } + customPrimaryColor = { GetVehicleCustomPrimaryColour(vehicle) } end local hasCustomXenonColor, customXenonColorR, customXenonColorG, customXenonColorB = GetVehicleXenonLightsCustomColor(vehicle) @@ -826,6 +821,12 @@ function ESX.Game.GetVehicleProperties(vehicle) customXenonColor = { customXenonColorR, customXenonColorG, customXenonColorB } end + local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle) + local customSecondaryColor = nil + if hasCustomSecondaryColor then + customSecondaryColor = { GetVehicleCustomSecondaryColour(vehicle) } + end + local extras = {} for extraId = 0, 20 do if DoesExtraExist(vehicle, extraId) then @@ -878,6 +879,8 @@ function ESX.Game.GetVehicleProperties(vehicle) dirtLevel = ESX.Math.Round(GetVehicleDirtLevel(vehicle), 1), color1 = colorPrimary, color2 = colorSecondary, + customPrimaryColor = customPrimaryColor, + customSecondaryColor = customSecondaryColor, pearlescentColor = pearlescentColor, wheelColor = wheelColor, From 9582c31a910a7b3641938c829559da14c496402b Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 9 May 2025 08:59:08 +0530 Subject: [PATCH 097/132] Revamped variable orders --- [core]/es_extended/client/functions.lua | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 71ed4882..02fe9772 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -805,14 +805,18 @@ function ESX.Game.GetVehicleProperties(vehicle) return end + ---@type number | number[], number | number[] local colorPrimary, colorSecondary = GetVehicleColours(vehicle) local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) - local hasCustomPrimaryColor = GetIsVehiclePrimaryColourCustom(vehicle) local dashboardColor = GetVehicleDashboardColor(vehicle) local interiorColor = GetVehicleInteriorColour(vehicle) - local customPrimaryColor = nil - if hasCustomPrimaryColor then - customPrimaryColor = { GetVehicleCustomPrimaryColour(vehicle) } + + if GetIsVehiclePrimaryColourCustom(vehicle) then + colorPrimary = { GetVehicleCustomPrimaryColour(vehicle) } + end + + if GetIsVehicleSecondaryColourCustom(vehicle) then + colorSecondary = { GetVehicleCustomSecondaryColour(vehicle) } end local hasCustomXenonColor, customXenonColorR, customXenonColorG, customXenonColorB = GetVehicleXenonLightsCustomColor(vehicle) @@ -821,12 +825,6 @@ function ESX.Game.GetVehicleProperties(vehicle) customXenonColor = { customXenonColorR, customXenonColorG, customXenonColorB } end - local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle) - local customSecondaryColor = nil - if hasCustomSecondaryColor then - customSecondaryColor = { GetVehicleCustomSecondaryColour(vehicle) } - end - local extras = {} for extraId = 0, 20 do if DoesExtraExist(vehicle, extraId) then @@ -879,8 +877,6 @@ function ESX.Game.GetVehicleProperties(vehicle) dirtLevel = ESX.Math.Round(GetVehicleDirtLevel(vehicle), 1), color1 = colorPrimary, color2 = colorSecondary, - customPrimaryColor = customPrimaryColor, - customSecondaryColor = customSecondaryColor, pearlescentColor = pearlescentColor, wheelColor = wheelColor, From 3a68968e9a149e5ae226fb5201d4a587afacc5d6 Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Tue, 20 May 2025 09:45:16 +0800 Subject: [PATCH 098/132] fix(esx_multicharacter/server/modules/functions): fix failed to reconnect after client crashed --- [core]/esx_multicharacter/server/modules/functions.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 8792cec1..6c816571 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -27,7 +27,7 @@ function Server:OnConnecting(source, deferrals) deferrals.defer() Wait(0) -- Required local identifier = self:GetIdentifier(source) - + -- luacheck: ignore if not SetEntityOrphanMode then return deferrals.done(("[ESX] ESX Requires a minimum Artifact version of 10188, Please update your server.")) @@ -47,7 +47,7 @@ function Server:OnConnecting(source, deferrals) if identifier then if not ESX.GetConfig().EnableDebug then - if ESX.Players[identifier] then + if not source and ESX.Players[identifier] then deferrals.done(("[ESX Multicharacter] A player is already connected to the server with this identifier.\nYour identifier: %s:%s"):format(Server.identifierType, identifier)) else deferrals.done() From 7b84a02ef2862b401695ff69858d9cc1ce43ad32 Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Tue, 20 May 2025 10:07:21 +0800 Subject: [PATCH 099/132] fix(es_extended/server/main): fix failed to reconnect after client crashed --- [core]/es_extended/server/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index fe0821a0..993e99ec 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -50,7 +50,7 @@ local function onPlayerJoined(playerId) return DropPlayer(playerId, "there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - if ESX.GetPlayerFromIdentifier(identifier) then + if not playerId and ESX.GetPlayerFromIdentifier(identifier) then DropPlayer( playerId, ("there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s"):format( @@ -116,7 +116,7 @@ if not Config.Multichar then end if identifier then - if ESX.GetPlayerFromIdentifier(identifier) then + if not playerId and ESX.GetPlayerFromIdentifier(identifier) then return deferrals.done( ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) ) From 3603962413ad75eafaf1ac44cac6d92811fc2f53 Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Tue, 20 May 2025 14:13:13 +0800 Subject: [PATCH 100/132] fix(es_extended/server/main): remove old player from memory (non multichar) --- [core]/es_extended/server/main.lua | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 993e99ec..b54450bb 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -50,7 +50,7 @@ local function onPlayerJoined(playerId) return DropPlayer(playerId, "there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - if not playerId and ESX.GetPlayerFromIdentifier(identifier) then + if ESX.GetPlayerFromIdentifier(identifier) then DropPlayer( playerId, ("there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s"):format( @@ -115,17 +115,27 @@ if not Config.Multichar then return deferrals.done("[ESX] OxMySQL Was Unable To Connect to your database. Please make sure it is turned on and correctly configured in your server.cfg") end - if identifier then - if not playerId and ESX.GetPlayerFromIdentifier(identifier) then + if not identifier then + return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") + end + + local xIdentifier = ESX.GetPlayerFromIdentifier(identifier) + + if not playerId and xIdentifier then + local xId = xIdentifier.playerId + + if ESX.Players[xId] then + ESX.Players[xId] = nil + Core.playersByIdentifier[identifier] = nil + print(("[ESX] Cleaning old ESX.Players entry for %s (ped invalid)"):format(identifier)) + else return deferrals.done( ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) ) - else - return deferrals.done() end - else - return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end + + return deferrals.done() end) end From d2cf43830127ae6ba2ac7ebfa3b1057071dde462 Mon Sep 17 00:00:00 2001 From: FirstSanchez <87505001+FirstSanchez@users.noreply.github.com> Date: Tue, 20 May 2025 09:00:57 +0200 Subject: [PATCH 101/132] fixxed false german translation --- [core]/skinchanger/locales/de.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/[core]/skinchanger/locales/de.lua b/[core]/skinchanger/locales/de.lua index a4741a61..54e1f1e7 100644 --- a/[core]/skinchanger/locales/de.lua +++ b/[core]/skinchanger/locales/de.lua @@ -5,6 +5,8 @@ Locales["de"] = { ["resemblance"] = "Ähnlichkeit", ["skin_tone"] = "Hautton", ["nose_1"] = "Nasenbreite", + ["grandparents"] = "Großeltern", + ["resemblance_g"] = "Großeltern Ähnlichkeit", ["nose_2"] = "Höhe der Nasenspitze", ["nose_3"] = "Länge der Nasenspitze", ["nose_4"] = "Nasenbeinhöhe", @@ -59,8 +61,8 @@ Locales["de"] = { ["arms_2"] = "Arme 2", ["pants_1"] = "Hose 1", ["pants_2"] = "Hose 2", - ["schuhe_1"] = "Schuhe 1", - ["Schuhe_2"] = "Schuhe 2", + ["shoes_1"] = "Schuhe 1", + ["shoes_2"] = "Schuhe 2", ["mask_1"] = "Maske 1", ["mask_2"] = "Maske 2", ["bproof_1"] = "Kugelsichere Weste 1", @@ -88,8 +90,8 @@ Locales["de"] = { ["complexion_1"] = "Teintstärke", ["sun"] = "Sonne", ["sun_1"] = "Sonnenstärke", - ["sommersprossen"] = "Sommersprossen", - ["sommersprossen_1"] = "Sommersprossenstärke", + ["freckles_1"] = "Sommersprossen", + ["freckles_2"] = "Sommersprossenstärke", ["chest_hair"] = "Brusthaar", ["chest_hair_1"] = "Brusthaarstärke", ["chest_color"] = "Brusthaarfarbe", From 5120e79e532668188fd576c0ca16e80f313f697c Mon Sep 17 00:00:00 2001 From: RodericAguilar <84584545+RodericAguilar@users.noreply.github.com> Date: Tue, 20 May 2025 10:00:55 +0200 Subject: [PATCH 102/132] Update es.lua Fixed spanish translation, also add some translations missed. --- [core]/es_extended/locales/es.lua | 622 ++++++++++++++++-------------- 1 file changed, 324 insertions(+), 298 deletions(-) diff --git a/[core]/es_extended/locales/es.lua b/[core]/es_extended/locales/es.lua index 9d03a65d..8ba92c14 100644 --- a/[core]/es_extended/locales/es.lua +++ b/[core]/es_extended/locales/es.lua @@ -1,399 +1,425 @@ Locales["es"] = { -- Inventory - ["inventory"] = "Inventario %s / %s", + ["inventory"] = "Inventario (Peso %s / %s)", ["use"] = "Usar", ["give"] = "Dar", ["remove"] = "Tirar", - ["return"] = "Volver", + ["return"] = "Devolver", ["give_to"] = "Dar a", ["amount"] = "Cantidad", ["giveammo"] = "Dar munición", ["amountammo"] = "Cantidad de munición", - ["noammo"] = "No tienes suficiente munición!", - ["gave_item"] = "Has dado %sx %s a %s", - ["received_item"] = "Has recibido %sx %s de %s", - ["gave_weapon"] = "Has dado %s a %s", - ["gave_weapon_ammo"] = "Has dado ~o~%sx %s para %s a %s", - ["gave_weapon_withammo"] = "Has dado %s con ~o~%sx %s a %s", - ["gave_weapon_hasalready"] = "%s ya tiene un/a %s", - ["gave_weapon_noweapon"] = "%s no tiene ese arma", - ["received_weapon"] = "Has recibido %s de %s", - ["received_weapon_ammo"] = "Has recibido ~o~%sx %s para su %s de %s", - ["received_weapon_withammo"] = "Has recibido %s con ~o~%sx %s de %s", - ["received_weapon_hasalready"] = "%s intentó darle un/a %s, pero ya tienes uno", - ["received_weapon_noweapon"] = "%s intentó darles munición para un %s, pero no tiene uno", - ["gave_account_money"] = "Has dado $%s (%s) a %s", - ["received_account_money"] = "Has recibido $%s (%s) de %s", + ["noammo"] = "¡Insuficiente!", + ["gave_item"] = "Dando %sx %s a %s", + ["received_item"] = "Recibido %sx %s de %s", + ["gave_weapon"] = "Dando %s a %s", + ["gave_weapon_ammo"] = "Dando ~o~%sx %s para %s a %s", + ["gave_weapon_withammo"] = "Dando %s con ~o~%sx %s a %s", + ["gave_weapon_hasalready"] = "%s ya tiene un %s", + ["gave_weapon_noweapon"] = "%s no tiene esa arma", + ["received_weapon"] = "Recibido %s de %s", + ["received_weapon_ammo"] = "Recibido ~o~%sx %s para tu %s de %s", + ["received_weapon_withammo"] = "Recibido %s con ~o~%sx %s de %s", + ["received_weapon_hasalready"] = "%s ha intentado darte un %s, pero ya tienes esta arma", + ["received_weapon_noweapon"] = "%s ha intentado darte munición para un %s, pero no tienes esta arma", + ["gave_account_money"] = "Dando $%s (%s) a %s", + ["received_account_money"] = "Recibido $%s (%s) de %s", ["amount_invalid"] = "Cantidad inválida", ["players_nearby"] = "No hay jugadores cerca", - ["ex_inv_lim"] = "Acción no posible, excediendo el límite de inventario para %s", - ["imp_invalid_quantity"] = "Acción imposible, cantidad inválida", - ["imp_invalid_amount"] = "Acción imposible, cantidad inválida", - ["threw_standard"] = "Has tirado %sx %s", - ["threw_account"] = "Has tirado $%s %s", - ["threw_weapon"] = "Has tirado %s", - ["threw_weapon_ammo"] = "Has tirado %s con ~o~%sx %s", - ["threw_weapon_already"] = "Ya llevas el mismo arma", - ["threw_cannot_pickup"] = "No puedes recogerlo porque tu inventario está lleno!", + ["ex_inv_lim"] = "No se puede realizar la acción, se excede el peso máximo de %s", + ["imp_invalid_quantity"] = "No se puede realizar la acción, la cantidad es inválida", + ["imp_invalid_amount"] = "No se puede realizar la acción, la cantidad es inválida", + ["threw_standard"] = "Tirando %sx %s", + ["threw_account"] = "Tirando $%s %s", + ["threw_weapon"] = "Tirando %s", + ["threw_weapon_ammo"] = "Tirando %s con ~o~%sx %s", + ["threw_weapon_already"] = "Ya tienes esta arma", + ["threw_cannot_pickup"] = "¡Inventario lleno, no se puede recoger!", ["threw_pickup_prompt"] = "Pulsa E para recoger", -- Key mapping - ["keymap_showinventory"] = "Ver Inventario", + ["keymap_showinventory"] = "Mostrar inventario", -- Salary related - ["received_salary"] = "Has recibido tu sueldo: $%s", - ["received_help"] = "Has recibido su cheque de bienestar: $%s", - ["company_nomoney"] = "La empresa en la que trabajas no tiene dinero para pagar tu sueldo", - ["received_paycheck"] = "Recibió su paga", - ["bank"] = "Banco", + ["received_salary"] = "Has cobrado: $%s", + ["received_help"] = "Has cobrado tu subsidio: $%s", + ["company_nomoney"] = "La empresa para la que trabajas es demasiado pobre para pagarte el sueldo", + ["received_paycheck"] = "Nómina recibida", + ["bank"] = "Maze Bank", ["account_bank"] = "Banco", - ["account_black_money"] = "Dinero Negro", + ["account_black_money"] = "Dinero negro", ["account_money"] = "Efectivo", - ["act_imp"] = "No se pudo realizar la acción.", - ["in_vehicle"] = "Acción rechazada. El jugador se encuentra en un vehículo", - ["not_in_vehicle"] = "Cannot Perform Action, Player isn't in a vehicle", + ["act_imp"] = "No se puede realizar la acción", + ["in_vehicle"] = "No se puede realizar la acción, el jugador está en un vehículo", + ["not_in_vehicle"] = "No se puede realizar la acción, el jugador no está en un vehículo", -- Commands - ["command_bring"] = "Traer un jugador hacia ti", - ["command_car"] = "Spawnear un vehículo", - ["command_car_car"] = "Nombre del vehículo", + ["command_bring"] = "Traer jugador hacia ti", + ["command_car"] = "Generar un vehículo", + ["command_car_car"] = "Modelo o hash del vehículo", ["command_cardel"] = "Eliminar vehículos cercanos", - ["command_cardel_radius"] = "Opcional, eliminar todos los vehículos en el radio especificado", - ["command_repair"] = "Reparar tu vehiculo", - ["command_repair_success"] = "Vehiculo reparado correctamente", - ["command_repair_success_target"] = "Un administrador reparo tu vehiculo", - ["command_clear"] = "Limpiar chat para ti", - ["command_clearall"] = "Limpiar chat para todos los jugadores", - ["command_clearinventory"] = "Limpiar el inventario del jugador", - ["command_clearloadout"] = "Limpiar inventario de un jugador", - ["command_freeze"] = "Congelar un jugador", - ["command_unfreeze"] = "Descongelar un jugador", - ["command_giveaccountmoney"] = "Dar dinero", - ["command_giveaccountmoney_account"] = "Nombre de cuenta válido", + ["command_cardel_radius"] = "Elimina todos los vehículos dentro del radio especificado", + ["command_repair"] = "Reparar tu vehículo", + ["command_repair_success"] = "Vehículo reparado con éxito", + ["command_repair_success_target"] = "Un administrador reparó tu vehículo", + ["command_clear"] = "Limpiar texto del chat", + ["command_clearall"] = "Limpiar texto del chat para todos los jugadores", + ["command_clearinventory"] = "Eliminar todos los objetos del inventario del jugador", + ["command_clearloadout"] = "Eliminar todas las armas del equipamiento del jugador", + ["command_freeze"] = "Congelar a un jugador", + ["command_unfreeze"] = "Descongelar a un jugador", + ["command_giveaccountmoney"] = "Dar dinero a una cuenta específica", + ["command_giveaccountmoney_account"] = "Cuenta a la que añadir", ["command_giveaccountmoney_amount"] = "Cantidad a añadir", - ["command_giveaccountmoney_invalid"] = "Nombre de cuenta no existente. [bank, money, black_money]", - ["command_giveitem"] = "Dar un objeto a un jugador", - ["command_giveitem_item"] = "Nombre del artículo", - ["command_giveitem_count"] = "Cantidad de articulos", - ["command_giveweapon"] = "Dar un arma a un jugador", + ["command_giveaccountmoney_invalid"] = "Nombre de cuenta inválido", + ["command_removeaccountmoney"] = "Quitar dinero de una cuenta específica", + ["command_removeaccountmoney_account"] = "Cuenta de la que quitar", + ["command_removeaccountmoney_amount"] = "Cantidad a quitar", + ["command_removeaccountmoney_invalid"] = "Nombre de cuenta inválido", + ["command_giveitem"] = "Dar un objeto al jugador", + ["command_giveitem_item"] = "Nombre del objeto", + ["command_giveitem_count"] = "Cantidad", + ["command_giveweapon"] = "Dar un arma al jugador", ["command_giveweapon_weapon"] = "Nombre del arma", - ["command_giveweapon_ammo"] = "Cantidad de municion", - ["command_giveweapon_hasalready"] = "El jugador ya tiene esa arma", - ["command_giveweaponcomponent"] = "Dar el componente del arma", + ["command_giveweapon_ammo"] = "Cantidad de munición", + ["command_giveweapon_hasalready"] = "El jugador ya tiene esta arma", + ["command_giveweaponcomponent"] = "Dar componente de arma al jugador", ["command_giveweaponcomponent_component"] = "Nombre del componente", - ["command_giveweaponcomponent_invalid"] = "Componente del arma no válido", - ["command_giveweaponcomponent_hasalready"] = "El jugador ya tiene ese componente del arma", - ["command_giveweaponcomponent_missingweapon"] = "El jugador no tiene esa arma", - ["command_goto"] = "Teletransportarte hacia un jugador", - ["command_kill"] = "Matar un jugador", - ["command_save"] = "Guardar la informacion de un jugador en la base de datos.", - ["command_saveall"] = "Guardar toda la informacion de jugadores en la base de datos.", - ["command_setaccountmoney"] = "Establecer el dinero de la cuenta para un jugador", - ["command_setaccountmoney_amount"] = "Cantidad de dinero a establecer", - ["command_setcoords"] = "Teletransporte a coordenadas", - ["command_setcoords_x"] = "Eje X", - ["command_setcoords_y"] = "Eje Y", - ["command_setcoords_z"] = "Eje Z", - ["command_setjob"] = "Dar un trabajo a un jugador", - ["command_setjob_job"] = "Nombre del trabajo", + ["command_giveweaponcomponent_invalid"] = "Componente de arma inválido", + ["command_giveweaponcomponent_hasalready"] = "El jugador ya tiene este componente de arma", + ["command_giveweaponcomponent_missingweapon"] = "El jugador no tiene esta arma", + ["command_goto"] = "Teletransportarte a un jugador", + ["command_kill"] = "Matar a un jugador", + ["command_save"] = "Forzar guardado de datos de un jugador", + ["command_saveall"] = "Forzar guardado de todos los datos de los jugadores", + ["command_setaccountmoney"] = "Establecer el dinero en una cuenta específica", + ["command_setaccountmoney_amount"] = "Cantidad", + ["command_setcoords"] = "Teletransportar a coordenadas específicas", + ["command_setcoords_x"] = "Valor X", + ["command_setcoords_y"] = "Valor Y", + ["command_setcoords_z"] = "Valor Z", + ["command_setjob"] = "Establecer el trabajo de un jugador", + ["command_setjob_job"] = "Nombre", ["command_setjob_grade"] = "Rango del trabajo", - ["command_setjob_invalid"] = "El trabajo o el rango no son válidos", - ["command_setgroup"] = "Establecer el grupo de un jugador", + ["command_setjob_invalid"] = "El trabajo, el rango o ambos son inválidos", + ["command_setgroup"] = "Establecer el grupo de permisos de un jugador", ["command_setgroup_group"] = "Nombre del grupo", - ["commanderror_argumentmismatch"] = "Error en el recuento de argumentos (pasado %s, deseado %s)", - ["commanderror_argumentmismatch_number"] = "Argumento #%s tipo no coincide (cadena pasada, número deseado)", - ["commanderror_argumentmismatch_string"] = "Invalid Argument #%s data type (passed number, wanted string)", - ["commanderror_invaliditem"] = "Nombre del artículo no válido", + ["commanderror_argumentmismatch"] = "Número de argumentos inválido (pasados %s, esperados %s)", + ["commanderror_argumentmismatch_number"] = "Tipo de datos del argumento #%s inválido (pasado string, esperado número)", + ["commanderror_argumentmismatch_string"] = "Tipo de datos del argumento #%s inválido (pasado número, esperado string)", + ["commanderror_invaliditem"] = "Objeto inválido", ["commanderror_invalidweapon"] = "Arma inválida", - ["commanderror_console"] = "Ese comando no se puede ejecutar desde la consola", - ["commanderror_invalidcommand"] = "/%s ¡No es un comando válido!", - ["commanderror_invalidplayerid"] = "No hay ningún jugador online con la ID especificada", - ["commandgeneric_playerid"] = "ID del jugador", - ["command_giveammo_noweapon_found"] = "%s no posee esa arma", + ["commanderror_console"] = "El comando no se puede ejecutar desde la consola", + ["commanderror_invalidcommand"] = "Comando inválido - /%s", + ["commanderror_invalidplayerid"] = "El jugador especificado no está en línea", + ["commandgeneric_playerid"] = "ID del servidor del jugador", + ["commandgeneric_dimension"] = "Dimensión de destino", + ["command_giveammo_noweapon_found"] = "%s no tiene esa arma", ["command_giveammo_weapon"] = "Nombre del arma", - ["command_giveammo_ammo"] = "Cantidad de municion", + ["command_giveammo_ammo"] = "Cantidad de munición", + ["command_setdim"] = "Establecer la dimensión de un jugador", + ["tpm_nowaypoint"] = "No hay punto de ruta establecido.", + ["tpm_success"] = "Teletransportado con éxito", + + ["noclip_message"] = "Noclip ha sido %s", + ["enabled"] = "~g~activado~s~", + ["disabled"] = "~r~desactivado~s~", -- Locale settings - ["locale_digit_grouping_symbol"] = ",", - ["locale_currency"] = "$%s", + ["locale_digit_grouping_symbol"] = ".", + ["locale_currency"] = "%s€", -- Weapons - -- Drug Wars DLC - ["weapon_candycane"] = "Hacha de Caramelo ", - ["weapon_acidpackage"] = "Paquete de Acido", - ["weapon_pistolxm3"] = "Pistola WM 29", - ["weapon_railgunxm3"] = "Fusil electromagnético", - -- Melee ["weapon_dagger"] = "Daga", ["weapon_bat"] = "Bate", - ["weapon_battleaxe"] = "Hacha de combate", + ["weapon_battleaxe"] = "Hacha de guerra", ["weapon_bottle"] = "Botella", ["weapon_crowbar"] = "Palanca", ["weapon_flashlight"] = "Linterna", - ["weapon_golfclub"] = "Palo de Golf", + ["weapon_golfclub"] = "Palo de golf", ["weapon_hammer"] = "Martillo", - ["weapon_hatchet"] = "Hacha", + ["weapon_hatchet"] = "Hachuela", ["weapon_knife"] = "Cuchillo", - ["weapon_knuckle"] = "Puño Americano", + ["weapon_knuckle"] = "Puño americano", ["weapon_machete"] = "Machete", ["weapon_nightstick"] = "Porra", - ["weapon_wrench"] = "Llave Inglesa", - ["weapon_poolcue"] = "Taco de Billar", - ["weapon_stone_hatchet"] = "Hacha de Piedra", - ["weapon_switchblade"] = "Navaja", + ["weapon_wrench"] = "Llave inglesa", + ["weapon_poolcue"] = "Taco de billar", + ["weapon_stone_hatchet"] = "Hacha de piedra", + ["weapon_switchblade"] = "Navaja automática", -- Handguns - ["weapon_appistol"] = "Pistola AP", - ["weapon_ceramicpistol"] = "Pistola de Ceramica", - ["weapon_combatpistol"] = "Pistola de Combate", - ["weapon_doubleaction"] = "Revólver de Doble Acción", - ["weapon_navyrevolver"] = "Revólver de la Armada", - ["weapon_flaregun"] = "Pistola de Bengalas", - ["weapon_gadgetpistol"] = "Pistola de Perico", - ["weapon_heavypistol"] = "Pistola Pesada", - ["weapon_revolver"] = "Revólver Pesado", - ["weapon_revolver_mk2"] = "Revólver Pesado MK2", - ["weapon_marksmanpistol"] = "Pistola Marksman", - ["weapon_pistol"] = "Pistola 9mm", - ["weapon_pistol_mk2"] = "Pistola MK2", + ["weapon_appistol"] = "Pistola PA", + ["weapon_ceramicpistol"] = "Pistola de cerámica", + ["weapon_combatpistol"] = "Pistola de combate", + ["weapon_doubleaction"] = "Revólver de doble acción", + ["weapon_navyrevolver"] = "Revólver de la Marina", + ["weapon_flaregun"] = "Pistola de bengalas", + ["weapon_gadgetpistol"] = "Pistola de Cayo Perico", + ["weapon_heavypistol"] = "Pistola pesada", + ["weapon_revolver"] = "Revólver pesado", + ["weapon_revolver_mk2"] = "Revólver pesado Mk2", + ["weapon_marksmanpistol"] = "Pistola de tirador", + ["weapon_pistol"] = "Pistola", + ["weapon_pistol_mk2"] = "Pistola Mk2", ["weapon_pistol50"] = "Pistola .50", ["weapon_snspistol"] = "Pistola SNS", - ["weapon_snspistol_mk2"] = "Pistola SNS MK2", - ["weapon_stungun"] = "Taser", - ["weapon_raypistol"] = "Up-N-Atomizer", - ["weapon_vintagepistol"] = "Pistola Vintage", + ["weapon_snspistol_mk2"] = "Pistola SNS Mk2", + ["weapon_stungun"] = "Pistola eléctrica", + ["weapon_raypistol"] = "Pistola de Rayos", + ["weapon_vintagepistol"] = "Pistola vintage", -- Shotguns - ["weapon_assaultshotgun"] = "Escopeta de Asalto", - ["weapon_autoshotgun"] = "Escopeta Automática", - ["weapon_bullpupshotgun"] = "Escopeta Bullpup", - ["weapon_combatshotgun"] = "Escopeta Combate", - ["weapon_dbshotgun"] = "Escopeta de Doble Barril", - ["weapon_heavyshotgun"] = "Escopeta Pesada", + ["weapon_assaultshotgun"] = "Escopeta de asalto", + ["weapon_autoshotgun"] = "Escopeta automática", + ["weapon_bullpupshotgun"] = "Escopeta bullpup", + ["weapon_combatshotgun"] = "Escopeta de combate", + ["weapon_dbshotgun"] = "Escopeta de dos cañones", + ["weapon_heavyshotgun"] = "Escopeta pesada", ["weapon_musket"] = "Mosquete", - ["weapon_pumpshotgun"] = "Escopeta de Bombeo", - ["weapon_pumpshotgun_mk2"] = "Escopeta de Bombeo MK2", - ["weapon_sawnoffshotgun"] = "Escopeta Recortada", + ["weapon_pumpshotgun"] = "Escopeta de corredera", + ["weapon_pumpshotgun_mk2"] = "Escopeta de corredera Mk2", + ["weapon_sawnoffshotgun"] = "Escopeta recortada", -- SMG & LMG - ["weapon_assaultsmg"] = "Subfusil de Asalto", - ["weapon_combatmg"] = "Ametralladora de Combate", - ["weapon_combatmg_mk2"] = "Ametralladora MK2", - ["weapon_combatpdw"] = "Subfusil PDW", - ["weapon_gusenberg"] = "Subfusil de Barril", - ["weapon_machinepistol"] = "Pistola Ametralladora", + ["weapon_assaultsmg"] = "Subfusil de asalto", + ["weapon_combatmg"] = "Ametralladora de combate", + ["weapon_combatmg_mk2"] = "Ametralladora de combate Mk2", + ["weapon_combatpdw"] = "PDW de combate" + ["weapon_gusenberg"] = "Subfusil Gusenberg", + ["weapon_machinepistol"] = "Pistola ametralladora", ["weapon_mg"] = "Ametralladora", - ["weapon_microsmg"] = "Micro Subfusil", - ["weapon_minismg"] = "Mini Subfusil", + ["weapon_microsmg"] = "Microsubfusil", + ["weapon_minismg"] = "Minisubfusil", ["weapon_smg"] = "Subfusil", - ["weapon_smg_mk2"] = "Subfusil MK2", - ["weapon_raycarbine"] = "Ametralladora de Rayos", + ["weapon_smg_mk2"] = "Subfusil Mk2", + ["weapon_raycarbine"] = "Carabina de Rayos", + ["weapon_tecpistol"] = "Subfusil táctico", -- Rifles - ["weapon_advancedrifle"] = "Rifle Avanzado", - ["weapon_assaultrifle"] = "Rifle de Asalto", - ["weapon_assaultrifle_mk2"] = "Rifle de Asalto MK2", - ["weapon_bullpuprifle"] = "Rifle Bullpup", - ["weapon_bullpuprifle_mk2"] = "Rifle Bullpup MK2", + ["weapon_advancedrifle"] = "Fusil avanzado", + ["weapon_assaultrifle"] = "Fusil de asalto", + ["weapon_assaultrifle_mk2"] = "Fusil de asalto Mk2", + ["weapon_bullpuprifle"] = "Fusil bullpup", + ["weapon_bullpuprifle_mk2"] = "Fusil bullpup Mk2", ["weapon_carbinerifle"] = "Carabina", - ["weapon_carbinerifle_mk2"] = "Carabina MK2", - ["weapon_compactrifle"] = "Rifle Compacto", - ["weapon_militaryrifle"] = "Rifle Militar", - ["weapon_specialcarbine"] = "Carabina Especial", - ["weapon_specialcarbine_mk2"] = "Carabina Especial MK2", - ["weapon_heavyrifle"] = "Rifle Pesado", + ["weapon_carbinerifle_mk2"] = "Carabina Mk2", + ["weapon_compactrifle"] = "Fusil compacto", + ["weapon_militaryrifle"] = "Fusil militar", + ["weapon_specialcarbine"] = "Carabina especial", + ["weapon_specialcarbine_mk2"] = "Carabina especial Mk2", + ["weapon_heavyrifle"] = "Fusil pesado", + ["weapon_battlerifle"] = "Fusil de combate", -- Sniper - ["weapon_heavysniper"] = "Francotirador Pesado", - ["weapon_heavysniper_mk2"] = "Francotirador Pesado MK2", - ["weapon_marksmanrifle"] = "Rifle Marksman", - ["weapon_marksmanrifle_mk2"] = "Rifle Marksman MK2", - ["weapon_sniperrifle"] = "Rifle de Francotirador", + ["weapon_heavysniper"] = "Francotirador pesado", + ["weapon_heavysniper_mk2"] = "Francotirador pesado Mk2", + ["weapon_marksmanrifle"] = "Fusil de tirador", + ["weapon_marksmanrifle_mk2"] = "Fusil de tirador Mk2", + ["weapon_sniperrifle"] = "Fusil de francotirador", -- Heavy / Launchers - ["weapon_compactlauncher"] = "Lanzador Compacto", - ["weapon_firework"] = "Lanzador de Fuegos Artificiales", + ["weapon_compactlauncher"] = "Lanzagranadas compacto", + ["weapon_firework"] = "Lanzador de pirotecnia", ["weapon_grenadelauncher"] = "Lanzagranadas", - ["weapon_hominglauncher"] = "Lanzacohetes Guiado", + ["weapon_hominglauncher"] = "Lanzacohetes teledirigido", ["weapon_minigun"] = "Minigun", ["weapon_railgun"] = "Cañón de riel", - ["weapon_rpg"] = "Lanzador de cohetes", - ["weapon_rayminigun"] = "Minigun de Rayos", + ["weapon_rpg"] = "Lanzacohetes", + ["weapon_rayminigun"] = "Ametralladora de rayos", + + -- Criminal Enterprises DLC + ["weapon_metaldetector"] = "Detector de metales", + ["weapon_precisionrifle"] = "Fusil de precisión", + ["weapon_tactilerifle"] = "Carabina de servicio", + + -- Drug wars dlc + ["weapon_candycane"] = "Bastón de caramelo", + ["weapon_acidpackage"] = "Paquete de ácido", + ["weapon_pistolxm3"] = "Pistola WM 29", + ["weapon_railgunxm3"] = "Cañón de riel", + + -- Chop Shop DLC + ["weapon_snowlauncher"] = "Lanzador de nieve", + ["weapon_hackingdevice"] = "Dispositivo de hackeo", + + -- Bottom Dollar Bounties DLC + ["weapon_stunrod"] = "Porra eléctrica", -- Thrown - ["weapon_ball"] = "Pelota de Beisbol", - ["weapon_bzgas"] = "Gas Pimienta", + ["weapon_ball"] = "Pelota de béisbol", + ["weapon_bzgas"] = "Gas BZ", ["weapon_flare"] = "Bengala", ["weapon_grenade"] = "Granada", - ["weapon_petrolcan"] = "Bidon de Gasolina", - ["weapon_hazardcan"] = "Bidón de Gasolina Peligroso", - ["weapon_molotov"] = "Molotov", - ["weapon_proxmine"] = "Mina de Proximidad ", + ["weapon_petrolcan"] = "Bidón de gasolina", + ["weapon_hazardcan"] = "Bidón de material peligroso", + ["weapon_molotov"] = "Cóctel molotov", + ["weapon_proxmine"] = "Mina de proximidad", ["weapon_pipebomb"] = "Bomba casera", ["weapon_snowball"] = "Bola de nieve", - ["weapon_stickybomb"] = "C4", - ["weapon_smokegrenade"] = "Granada de Humo", + ["weapon_stickybomb"] = "Bomba lapa", + ["weapon_smokegrenade"] = "Granada de humo", -- Special ["weapon_fireextinguisher"] = "Extintor", - ["weapon_digiscanner"] = "Escaner Digital", - ["weapon_garbagebag"] = "Bolsa de Basura", - ["weapon_handcuffs"] = "Grilletes", - ["gadget_nightvision"] = "Vision Nocturna", - ["gadget_parachute"] = "Paracaidas", + ["weapon_digiscanner"] = "Escáner digital", + ["weapon_garbagebag"] = "Bolsa de basura", + ["weapon_handcuffs"] = "Esposas", + ["gadget_nightvision"] = "Visión nocturna", + ["gadget_parachute"] = "Paracaídas", -- Weapon Components - ["component_knuckle_base"] = "Modelo Basico", - ["component_knuckle_pimp"] = "el Proxeneta", - ["component_knuckle_ballas"] = "los Ballas", - ["component_knuckle_dollar"] = "el Buscavidas", - ["component_knuckle_diamond"] = "la Roca", - ["component_knuckle_hate"] = "el Hater", - ["component_knuckle_love"] = "el Amante", - ["component_knuckle_player"] = "el Jugador", - ["component_knuckle_king"] = "el Rey", - ["component_knuckle_vagos"] = "los Vagos", + ["component_knuckle_base"] = "Modelo base", + ["component_knuckle_pimp"] = "El Chulo", + ["component_knuckle_ballas"] = "Los Ballas", + ["component_knuckle_dollar"] = "El Buscavidas", + ["component_knuckle_diamond"] = "La Roca", + ["component_knuckle_hate"] = "El Odiador", + ["component_knuckle_love"] = "El Amante", + ["component_knuckle_player"] = "El Jugador", + ["component_knuckle_king"] = "El Rey", + ["component_knuckle_vagos"] = "Los Vagos", - ["component_luxary_finish"] = "Acabado de Armas de Lujo", + ["component_luxary_finish"] = "Acabado de arma de lujo", - ["component_handle_default"] = "Mango Default", - ["component_handle_vip"] = "Mango VIP", - ["component_handle_bodyguard"] = "Mango de Guardaespaldas", + ["component_handle_default"] = "Empuñadura por defecto", + ["component_handle_vip"] = "Empuñadura VIP", + ["component_handle_bodyguard"] = "Empuñadura de guardaespaldas", ["component_vip_finish"] = "Acabado VIP", - ["component_bodyguard_finish"] = "Acabado Guardaespaldas", + ["component_bodyguard_finish"] = "Acabado de guardaespaldas", - ["component_camo_finish"] = "Camuflaje Digital", - ["component_camo_finish2"] = "Camuflaje Pincelada", - ["component_camo_finish3"] = "Camuflaje Bosque", - ["component_camo_finish4"] = "Camuflaje Calavera", + ["component_camo_finish"] = "Camuflaje digital", + ["component_camo_finish2"] = "Camuflaje pincelada", + ["component_camo_finish3"] = "Camuflaje boscoso", + ["component_camo_finish4"] = "Camuflaje calavera", ["component_camo_finish5"] = "Camuflaje Sessanta Nove", ["component_camo_finish6"] = "Camuflaje Perseo", - ["component_camo_finish7"] = "Camuflaje Leopardo", - ["component_camo_finish8"] = "Camuflaje Zebra", - ["component_camo_finish9"] = "Camuflaje Geométrico", + ["component_camo_finish7"] = "Camuflaje leopardo", + ["component_camo_finish8"] = "Camuflaje cebra", + ["component_camo_finish9"] = "Camuflaje geométrico", ["component_camo_finish10"] = "Camuflaje Boom", - ["component_camo_finish11"] = "Camuflaje Patriotico", + ["component_camo_finish11"] = "Camuflaje patriótico", - ["component_camo_slide_finish"] = "Camuflaje Digital Deslizante", - ["component_camo_slide_finish2"] = "Camuflaje Pincelada Deslizante", - ["component_camo_slide_finish3"] = "Camuflaje Bosque Deslizante", - ["component_camo_slide_finish4"] = "Camuflaje Calavera Deslizante", - ["component_camo_slide_finish5"] = "Camuflaje Sessanta Nove Deslizante", - ["component_camo_slide_finish6"] = "Camuflaje Perseo Deslizante", - ["component_camo_slide_finish7"] = "Camuflaje Leopardo Deslizante", - ["component_camo_slide_finish8"] = "Camuflaje Zebra Deslizante", - ["component_camo_slide_finish9"] = "Camuflaje Geométrico Deslizante", - ["component_camo_slide_finish10"] = "Camuflaje Boom Deslizante", - ["component_camo_slide_finish11"] = "Camuflaje Patriotico Deslizante", + ["component_camo_slide_finish"] = "Camuflaje digital (corredera)", + ["component_camo_slide_finish2"] = "Camuflaje pincelada (corredera)", + ["component_camo_slide_finish3"] = "Camuflaje boscoso (corredera)", + ["component_camo_slide_finish4"] = "Camuflaje calavera (corredera)", + ["component_camo_slide_finish5"] = "Camuflaje Sessanta Nove (corredera)", + ["component_camo_slide_finish6"] = "Camuflaje Perseo (corredera)", + ["component_camo_slide_finish7"] = "Camuflaje leopardo (corredera)", + ["component_camo_slide_finish8"] = "Camuflaje cebra (corredera)", + ["component_camo_slide_finish9"] = "Camuflaje geométrico (corredera)", + ["component_camo_slide_finish10"] = "Camuflaje Boom (corredera)", + ["component_camo_slide_finish11"] = "Camuflaje patriótico (corredera)", - ["component_clip_default"] = "Cargador Default", - ["component_clip_extended"] = "Cargador Extendido", - ["component_clip_drum"] = "Cargador Barril", - ["component_clip_box"] = "Caja de Cargador", + ["component_clip_default"] = "Cargador por defecto", + ["component_clip_extended"] = "Cargador ampliado", + ["component_clip_drum"] = "Cargador de tambor", + ["component_clip_box"] = "Cargador de caja", - ["component_scope_holo"] = "Mira Holográfica", - ["component_scope_small"] = "Mira Pequeña", - ["component_scope_medium"] = "Mira Mediana", - ["component_scope_large"] = "Mira Larga", - ["component_scope"] = "Mira", - ["component_scope_advanced"] = "Mira Avanzada", - ["component_ironsights"] = "Mira de Hierro", + ["component_scope_holo"] = "Mira holográfica", + ["component_scope_small"] = "Mira pequeña", + ["component_scope_medium"] = "Mira mediana", + ["component_scope_large"] = "Mira telescópica", + ["component_scope"] = "Mira óptica", + ["component_scope_advanced"] = "Mira avanzada", + ["component_ironsights"] = "Miras de hierro", ["component_suppressor"] = "Silenciador", - ["component_compensator"] = "Estabilizador", + ["component_compensator"] = "Compensador", - ["component_muzzle_flat"] = "Boquilla de Freno Plana", - ["component_muzzle_tactical"] = "Boquilla de Freno Tactica", - ["component_muzzle_fat"] = "Boquilla de Freno Punta Gorda", - ["component_muzzle_precision"] = "Boquilla de Freno de Precision", - ["component_muzzle_heavy"] = "Boquilla de Freno Pesada", - ["component_muzzle_slanted"] = "Boquilla de Freno inclinada", - ["component_muzzle_split"] = "Boquilla de Freno de Puntas Abiertas", - ["component_muzzle_squared"] = "Boquilla de Freno Cuadrada", + ["component_muzzle_flat"] = "Freno de boca plano", + ["component_muzzle_tactical"] = "Freno de boca táctico", + ["component_muzzle_fat"] = "Freno de boca grueso", + ["component_muzzle_precision"] = "Freno de boca de precisión", + ["component_muzzle_heavy"] = "Freno de boca pesado", + ["component_muzzle_slanted"] = "Freno de boca inclinado", + ["component_muzzle_split"] = "Freno de boca dividido", + ["component_muzzle_squared"] = "Freno de boca cuadrado", ["component_flashlight"] = "Linterna", - ["component_grip"] = "Agarre", + ["component_grip"] = "Empuñadura", - ["component_barrel_default"] = "Barril Por Defecto", - ["component_barrel_heavy"] = "Barril Pesado", + ["component_barrel_default"] = "Cañón por defecto", + ["component_barrel_heavy"] = "Cañón pesado", - ["component_ammo_tracer"] = "Munición de Rastreo", - ["component_ammo_incendiary"] = "Munición Incendiaria", - ["component_ammo_hollowpoint"] = "Munición de Punta Hueca", - ["component_ammo_fmj"] = "Munición fMJ", - ["component_ammo_armor"] = "Munición Perforante para Blindaje", - ["component_ammo_explosive"] = "Munición Incendiaria Perforadora de Blindajes", + ["component_ammo_tracer"] = "Munición trazadora", + ["component_ammo_incendiary"] = "Munición incendiaria", + ["component_ammo_hollowpoint"] = "Munición de punta hueca", + ["component_ammo_fmj"] = "Munición FMJ", + ["component_ammo_armor"] = "Munición perforante", + ["component_ammo_explosive"] = "Munición explosiva perforante", - ["component_shells_default"] = "Casquillos Por Defecto", - ["component_shells_incendiary"] = "Casquillos Aliento de Dragón", - ["component_shells_armor"] = "Casquillos Perdigones de Acero", - ["component_shells_hollowpoint"] = "Casquillos Punta Hueca", - ["component_shells_explosive"] = "Casquillos Posta Explosiva", + ["component_shells_default"] = "Cartuchos por defecto", + ["component_shells_incendiary"] = "Cartuchos Aliento de dragón", + ["component_shells_armor"] = "Cartuchos de postas de acero", + ["component_shells_hollowpoint"] = "Cartuchos de perdigones expansivos", + ["component_shells_explosive"] = "Cartuchos de postas explosivas", -- Weapon Ammo - ["ammo_rounds"] = "Redonda/s", - ["ammo_shells"] = "Casquillo/s", - ["ammo_charge"] = "Carga", - ["ammo_petrol"] = "Galones de Combustible", - ["ammo_firework"] = "Fuegos Artificiale/s", - ["ammo_rockets"] = "Cohete/s", - ["ammo_grenadelauncher"] = "Granada/s", - ["ammo_grenade"] = "Granada/s", - ["ammo_stickybomb"] = "Bomba/s", - ["ammo_pipebomb"] = "Bomba/s", - ["ammo_smokebomb"] = "Bomba/s", - ["ammo_molotov"] = "Molotov/s", - ["ammo_proxmine"] = "Mina(s)", - ["ammo_bzgas"] = "Lata(s)", - ["ammo_ball"] = "Bola(s)", - ["ammo_snowball"] = "Bola(s)", - ["ammo_flare"] = "Bengala(s)", - ["ammo_flaregun"] = "Bengala(s)", + ["ammo_rounds"] = "proyectil(es)", + ["ammo_shells"] = "cartucho(s)", + ["ammo_charge"] = "carga(s)", + ["ammo_petrol"] = "litros de combustible", + ["ammo_firework"] = "cohete(s) de pirotecnia", + ["ammo_rockets"] = "cohete(s)", + ["ammo_grenadelauncher"] = "granada(s)", + ["ammo_grenade"] = "granada(s)", + ["ammo_stickybomb"] = "bomba(s) lapa", + ["ammo_pipebomb"] = "bomba(s) casera", + ["ammo_smokebomb"] = "bomba(s) de humo", + ["ammo_molotov"] = "cóctel(es) molotov", + ["ammo_proxmine"] = "mina(s) de proximidad", + ["ammo_bzgas"] = "bote(s) de gas", + ["ammo_ball"] = "pelota(s)", + ["ammo_snowball"] = "bola(s) de nieve", + ["ammo_flare"] = "bengala(s)", + ["ammo_flaregun"] = "bengala(s)", -- Weapon Tints - ["tint_default"] = "Skin común", - ["tint_green"] = "Skin Verde", - ["tint_gold"] = "Skin Oro", - ["tint_pink"] = "Skin Rosa", - ["tint_army"] = "Skin Militar", - ["tint_lspd"] = "Skin Azul", - ["tint_orange"] = "Skin Naranja", - ["tint_platinum"] = "Skin Plata", + ["tint_default"] = "Diseño por defecto", + ["tint_green"] = "Diseño verde", + ["tint_gold"] = "Diseño dorado", + ["tint_pink"] = "Diseño rosa", + ["tint_army"] = "Diseño militar", + ["tint_lspd"] = "Diseño LSPD", + ["tint_orange"] = "Diseño naranja", + ["tint_platinum"] = "Diseño platino", -- MK2 Weapon Tints - ["tint_classic_black"] = "negro clásico", - ["tint_classic_gray"] = "gris clásico", - ["tint_classic_two_tone"] = "dos tonos clásicos", - ["tint_classic_white"] = "blanco clásico", - ["tint_classic_beige"] = "beige clásico", - ["tint_classic_green"] = "verde clásico", - ["tint_classic_blue"] = "azul clásico", - ["tint_classic_earth"] = "tierra clásica", - ["tint_classic_brown_black"] = "marrón negro clásico", - ["tint_contrast_red"] = "rojo de contraste", - ["tint_contrast_blue"] = "azul de contraste", - ["tint_contrast_yellow"] = "amarillo de contraste", - ["tint_contrast_orange"] = "naranja de contraste", - ["tint_bold_pink"] = "rosa atrevida", - ["tint_bold_purple_yellow"] = "púrpura amarilla atrevida", - ["tint_bold_orange"] = "naranja atrevido", - ["tint_bold_green_purple"] = "verde púrpura atrevido", - ["tint_bold_red_feat"] = "rojo atrevido feat", - ["tint_bold_green_feat"] = "verde atrevido feat", - ["tint_bold_cyan_feat"] = "cian atrevido feat", - ["tint_bold_yellow_feat"] = "amarillo atrevido feat", - ["tint_bold_red_white"] = "rojo blanco atrevido", - ["tint_bold_blue_white"] = "azul blanco atrevido", - ["tint_metallic_gold"] = "oro metálico", - ["tint_metallic_platinum"] = "platino metálico", - ["tint_metallic_gray_lilac"] = "gris lila metálico", - ["tint_metallic_purple_lime"] = "púrpura lima metálico", - ["tint_metallic_red"] = "rojo metálico", - ["tint_metallic_green"] = "verde metálico", - ["tint_metallic_blue"] = "azul metálico", - ["tint_metallic_white_aqua"] = "blanco aqua metálico", - ["tint_metallic_red_yellow"] = "rojo amarillo metálico", + ["tint_classic_black"] = "Negro clásico", + ["tint_classic_gray"] = "Gris clásico", + ["tint_classic_two_tone"] = "Dos tonos clásico", + ["tint_classic_white"] = "Blanco clásico", + ["tint_classic_beige"] = "Beige clásico", + ["tint_classic_green"] = "Verde clásico", + ["tint_classic_blue"] = "Azul clásico", + ["tint_classic_earth"] = "Tierra clásico", + ["tint_classic_brown_black"] = "Marrón y negro clásico", + ["tint_contrast_red"] = "Rojo contraste", + ["tint_contrast_blue"] = "Azul contraste", + ["tint_contrast_yellow"] = "Amarillo contraste", + ["tint_contrast_orange"] = "Naranja contraste", + ["tint_bold_pink"] = "Rosa intenso", + ["tint_bold_purple_yellow"] = "Morado y amarillo intenso", + ["tint_bold_orange"] = "Naranja intenso", + ["tint_bold_green_purple"] = "Verde y morado intenso", + ["tint_bold_red_feat"] = "Rojo intenso especial", + ["tint_bold_green_feat"] = "Verde intenso especial", + ["tint_bold_cyan_feat"] = "Cian intenso especial", + ["tint_bold_yellow_feat"] = "Amarillo intenso especial", + ["tint_bold_red_white"] = "Rojo y blanco intenso", + ["tint_bold_blue_white"] = "Azul y blanco intenso", + ["tint_metallic_gold"] = "Dorado metálico", + ["tint_metallic_platinum"] = "Platino metálico", + ["tint_metallic_gray_lilac"] = "Gris lila metálico", + ["tint_metallic_purple_lime"] = "Morado lima metálico", + ["tint_metallic_red"] = "Rojo metálico", + ["tint_metallic_green"] = "Verde metálico", + ["tint_metallic_blue"] = "Azul metálico", + ["tint_metallic_white_aqua"] = "Blanco aguamarina metálico", + ["tint_metallic_red_yellow"] = "Rojo y amarillo metálico", } From 86e9735b81eb4e12936e8d2aec7dd86fe032407b Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Tue, 20 May 2025 16:01:16 +0800 Subject: [PATCH 103/132] fix(esx_multicharacter/server/modules/functions): remove old player from memory after crashing --- .../esx_multicharacter/server/modules/functions.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 6c816571..ccf113d3 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -47,8 +47,16 @@ function Server:OnConnecting(source, deferrals) if identifier then if not ESX.GetConfig().EnableDebug then - if not source and ESX.Players[identifier] then - deferrals.done(("[ESX Multicharacter] A player is already connected to the server with this identifier.\nYour identifier: %s:%s"):format(Server.identifierType, identifier)) + if ESX.Players[identifier] then + local identifierExist = ESX.GetPlayerFromIdentifier(identifier) + + if not identifierExist then -- no player found + ESX.Players[identifier] = nil + print(('[ESX Multicharacter] no player found for identifier (%s), removing from ESX.Players'):format(identifier)) + deferrals.done() + else -- player found + deferrals.done(("[ESX Multicharacter] A player is already connected to the server with this identifier.\nYour identifier: %s:%s"):format(Server.identifierType, identifier)) + end else deferrals.done() end From 0ece059c1934b377e050cd760ef9437b8467bd9f Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Wed, 21 May 2025 13:48:26 +0800 Subject: [PATCH 104/132] fix(es_extended/server/main): add fallback check if player is exist --- [core]/es_extended/server/main.lua | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index b54450bb..a276dafc 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -119,19 +119,23 @@ if not Config.Multichar then return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - local xIdentifier = ESX.GetPlayerFromIdentifier(identifier) + local xPlayer = ESX.GetPlayerFromIdentifier(identifier) - if not playerId and xIdentifier then - local xId = xIdentifier.playerId + if xPlayer then + local xPlayerId = xPlayer.playerId - if ESX.Players[xId] then - ESX.Players[xId] = nil - Core.playersByIdentifier[identifier] = nil - print(("[ESX] Cleaning old ESX.Players entry for %s (ped invalid)"):format(identifier)) - else - return deferrals.done( - ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) - ) + if ESX.Players[xPlayerId] then + local xPlayerExist = DoesPlayerExist(xPlayerId --[[@as string]]) + + if xPlayerExist ~= 0 then + ESX.Players[xPlayerId] = nil + Core.playersByIdentifier[identifier] = nil + deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) + else + return deferrals.done( + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) + end end end From 0b0fd234447aff2262712e7bde8687c5d78963a4 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Thu, 22 May 2025 12:44:39 +0530 Subject: [PATCH 105/132] ESX_MULTICHARACTER --- [core]/esx_multicharacter/client/main.lua | 19 +- .../client/modules/menu.lua | 164 +++--------------- .../client/modules/multicharacter.lua | 21 ++- .../esx_multicharacter/client/modules/nui.lua | 23 +++ [core]/esx_multicharacter/config.lua | 2 +- [core]/esx_multicharacter/fxmanifest.lua | 5 +- [core]/esx_multicharacter/html/css/main.css | 76 -------- [core]/esx_multicharacter/html/js/app.js | 66 ------- [core]/esx_multicharacter/html/locales/cs.js | 8 - [core]/esx_multicharacter/html/locales/de.js | 8 - [core]/esx_multicharacter/html/locales/en.js | 8 - [core]/esx_multicharacter/html/locales/es.js | 8 - [core]/esx_multicharacter/html/locales/fi.js | 8 - [core]/esx_multicharacter/html/locales/fr.js | 8 - [core]/esx_multicharacter/html/locales/hu.js | 8 - [core]/esx_multicharacter/html/locales/it.js | 8 - [core]/esx_multicharacter/html/locales/nl.js | 8 - [core]/esx_multicharacter/html/locales/pl.js | 8 - [core]/esx_multicharacter/html/locales/pt.js | 8 - [core]/esx_multicharacter/html/locales/sr.js | 8 - [core]/esx_multicharacter/html/locales/sv.js | 8 - .../esx_multicharacter/html/locales/zh-cn.js | 8 - [core]/esx_multicharacter/html/ui.html | 22 --- [core]/esx_multicharacter/locales/cs.lua | 28 --- [core]/esx_multicharacter/locales/da.lua | 32 ---- [core]/esx_multicharacter/locales/de.lua | 32 ---- [core]/esx_multicharacter/locales/en.lua | 22 +-- [core]/esx_multicharacter/locales/es.lua | 28 --- [core]/esx_multicharacter/locales/fi.lua | 33 ---- [core]/esx_multicharacter/locales/fr.lua | 32 ---- [core]/esx_multicharacter/locales/gr.lua | 32 ---- [core]/esx_multicharacter/locales/he.lua | 32 ---- [core]/esx_multicharacter/locales/hu.lua | 32 ---- [core]/esx_multicharacter/locales/it.lua | 32 ---- [core]/esx_multicharacter/locales/nl.lua | 32 ---- [core]/esx_multicharacter/locales/pl.lua | 33 ---- [core]/esx_multicharacter/locales/pt.lua | 25 --- [core]/esx_multicharacter/locales/sl.lua | 33 ---- [core]/esx_multicharacter/locales/sr.lua | 33 ---- [core]/esx_multicharacter/locales/sv.lua | 32 ---- [core]/esx_multicharacter/locales/zh-cn.lua | 33 ---- .../web/build/assets/esxLogo-sNwPFb_p.png | Bin 0 -> 19788 bytes .../web/build/assets/index-Cy_cJ0Pf.css | 1 + .../web/build/assets/index-DGfzXZP_.js | 93 ++++++++++ .../esx_multicharacter/web/build/index.html | 14 ++ .../esx_multicharacter/web/eslint.config.js | 28 +++ [core]/esx_multicharacter/web/index.html | 13 ++ [core]/esx_multicharacter/web/package.json | 34 ++++ .../esx_multicharacter/web/postcss.config.js | 6 + [core]/esx_multicharacter/web/src/App.tsx | 44 +++++ .../web/src/assets/esxLogo.png | Bin 0 -> 19788 bytes .../web/src/components/CharacterCard.tsx | 102 +++++++++++ .../web/src/components/CharacterInfo.tsx | 71 ++++++++ .../web/src/components/CharacterSelection.tsx | 119 +++++++++++++ [core]/esx_multicharacter/web/src/index.css | 58 +++++++ [core]/esx_multicharacter/web/src/main.tsx | 7 + .../web/src/types/Character.ts | 15 ++ .../web/src/utils/fetchNui.ts | 39 +++++ .../esx_multicharacter/web/src/utils/misc.ts | 6 + .../web/src/utils/useNuiEvent.tsx | 49 ++++++ .../esx_multicharacter/web/src/vite-env.d.ts | 1 + .../esx_multicharacter/web/tailwind.config.js | 33 ++++ .../esx_multicharacter/web/tsconfig.app.json | 24 +++ [core]/esx_multicharacter/web/tsconfig.json | 7 + .../esx_multicharacter/web/tsconfig.node.json | 22 +++ [core]/esx_multicharacter/web/vite.config.ts | 14 ++ 66 files changed, 871 insertions(+), 995 deletions(-) create mode 100644 [core]/esx_multicharacter/client/modules/nui.lua delete mode 100644 [core]/esx_multicharacter/html/css/main.css delete mode 100644 [core]/esx_multicharacter/html/js/app.js delete mode 100644 [core]/esx_multicharacter/html/locales/cs.js delete mode 100644 [core]/esx_multicharacter/html/locales/de.js delete mode 100644 [core]/esx_multicharacter/html/locales/en.js delete mode 100644 [core]/esx_multicharacter/html/locales/es.js delete mode 100644 [core]/esx_multicharacter/html/locales/fi.js delete mode 100644 [core]/esx_multicharacter/html/locales/fr.js delete mode 100644 [core]/esx_multicharacter/html/locales/hu.js delete mode 100644 [core]/esx_multicharacter/html/locales/it.js delete mode 100644 [core]/esx_multicharacter/html/locales/nl.js delete mode 100644 [core]/esx_multicharacter/html/locales/pl.js delete mode 100644 [core]/esx_multicharacter/html/locales/pt.js delete mode 100644 [core]/esx_multicharacter/html/locales/sr.js delete mode 100644 [core]/esx_multicharacter/html/locales/sv.js delete mode 100644 [core]/esx_multicharacter/html/locales/zh-cn.js delete mode 100644 [core]/esx_multicharacter/html/ui.html delete mode 100644 [core]/esx_multicharacter/locales/cs.lua delete mode 100644 [core]/esx_multicharacter/locales/da.lua delete mode 100644 [core]/esx_multicharacter/locales/de.lua delete mode 100644 [core]/esx_multicharacter/locales/es.lua delete mode 100644 [core]/esx_multicharacter/locales/fi.lua delete mode 100644 [core]/esx_multicharacter/locales/fr.lua delete mode 100644 [core]/esx_multicharacter/locales/gr.lua delete mode 100644 [core]/esx_multicharacter/locales/he.lua delete mode 100644 [core]/esx_multicharacter/locales/hu.lua delete mode 100644 [core]/esx_multicharacter/locales/it.lua delete mode 100644 [core]/esx_multicharacter/locales/nl.lua delete mode 100644 [core]/esx_multicharacter/locales/pl.lua delete mode 100644 [core]/esx_multicharacter/locales/pt.lua delete mode 100644 [core]/esx_multicharacter/locales/sl.lua delete mode 100644 [core]/esx_multicharacter/locales/sr.lua delete mode 100644 [core]/esx_multicharacter/locales/sv.lua delete mode 100644 [core]/esx_multicharacter/locales/zh-cn.lua create mode 100644 [core]/esx_multicharacter/web/build/assets/esxLogo-sNwPFb_p.png create mode 100644 [core]/esx_multicharacter/web/build/assets/index-Cy_cJ0Pf.css create mode 100644 [core]/esx_multicharacter/web/build/assets/index-DGfzXZP_.js create mode 100644 [core]/esx_multicharacter/web/build/index.html create mode 100644 [core]/esx_multicharacter/web/eslint.config.js create mode 100644 [core]/esx_multicharacter/web/index.html create mode 100644 [core]/esx_multicharacter/web/package.json create mode 100644 [core]/esx_multicharacter/web/postcss.config.js create mode 100644 [core]/esx_multicharacter/web/src/App.tsx create mode 100644 [core]/esx_multicharacter/web/src/assets/esxLogo.png create mode 100644 [core]/esx_multicharacter/web/src/components/CharacterCard.tsx create mode 100644 [core]/esx_multicharacter/web/src/components/CharacterInfo.tsx create mode 100644 [core]/esx_multicharacter/web/src/components/CharacterSelection.tsx create mode 100644 [core]/esx_multicharacter/web/src/index.css create mode 100644 [core]/esx_multicharacter/web/src/main.tsx create mode 100644 [core]/esx_multicharacter/web/src/types/Character.ts create mode 100644 [core]/esx_multicharacter/web/src/utils/fetchNui.ts create mode 100644 [core]/esx_multicharacter/web/src/utils/misc.ts create mode 100644 [core]/esx_multicharacter/web/src/utils/useNuiEvent.tsx create mode 100644 [core]/esx_multicharacter/web/src/vite-env.d.ts create mode 100644 [core]/esx_multicharacter/web/tailwind.config.js create mode 100644 [core]/esx_multicharacter/web/tsconfig.app.json create mode 100644 [core]/esx_multicharacter/web/tsconfig.json create mode 100644 [core]/esx_multicharacter/web/tsconfig.node.json create mode 100644 [core]/esx_multicharacter/web/vite.config.ts diff --git a/[core]/esx_multicharacter/client/main.lua b/[core]/esx_multicharacter/client/main.lua index 0e6da55c..16a04f96 100644 --- a/[core]/esx_multicharacter/client/main.lua +++ b/[core]/esx_multicharacter/client/main.lua @@ -1,13 +1,3 @@ - --- Connection Logic - -local function AwaitContext() - while GetResourceState("esx_context") ~= "started" do - Wait(100) - end - return true -end - CreateThread(function() while not ESX.PlayerLoaded do @@ -16,13 +6,8 @@ CreateThread(function() if NetworkIsPlayerActive(ESX.playerId) then ESX.DisableSpawnManager() DoScreenFadeOut(0) - - local ready = AwaitContext() - if ready then - - Multicharacter:SetupCharacters() - break - end + Multicharacter:SetupCharacters() + break end end end) diff --git a/[core]/esx_multicharacter/client/modules/menu.lua b/[core]/esx_multicharacter/client/modules/menu.lua index 97d2c43f..69fc5dbc 100644 --- a/[core]/esx_multicharacter/client/modules/menu.lua +++ b/[core]/esx_multicharacter/client/modules/menu.lua @@ -1,15 +1,4 @@ Menu = {} -Menu._index = Menu -Menu.currentElements = {} - -function Menu:OpenMenu() - ESX.OpenContext("left", self.currentElements, self.onUse, nil, false) -end - -function Menu:Close() - self.currentElements = {} - ESX.CloseContext() -end function Menu:CheckModel(character) if not character.model and character.skin then @@ -23,18 +12,6 @@ function Menu:CheckModel(character) end end -function Menu:AddCharacters() - for _, v in pairs(Multicharacter.Characters) do - self:CheckModel(v) - - local label = ("%s %s"):format(v.firstname, v.lastname) - self.currentElements[#self.currentElements + 1] = { title = label, icon = "fa-regular fa-user", value = v.id} - end - if #self.currentElements - 1 < Multicharacter.slots then - self.currentElements[#self.currentElements + 1] = { title = TranslateCap("create_char"), icon = "fa-solid fa-plus", value = (#self.currentElements + 1), new = true } - end -end - local GetSlot = function() for i = 1, Multicharacter.slots do if not Multicharacter.Characters[i] then @@ -44,7 +21,6 @@ local GetSlot = function() end function Menu:NewCharacter() - self:Close() local slot = GetSlot() TriggerServerEvent("esx_multicharacter:CharacterChosen", slot, true) @@ -59,7 +35,7 @@ function Menu:NewCharacter() end -function Menu:SelectCharacter() +function Menu:InitCharacter() local Characters = Multicharacter.Characters local Character = next(Characters) self:CheckModel(Characters[Character]) @@ -67,125 +43,35 @@ function Menu:SelectCharacter() if not Multicharacter.spawned then Multicharacter:SetupCharacter(Character) end - - self.currentElements = { - { - title = TranslateCap("select_char"), - icon = "fa-solid fa-users", - description = TranslateCap("select_char_description"), - unselectable = true + Wait(500) + + SendNUIMessage({ + action = "ToggleMulticharacter", + data = { + show = true, + Characters = Characters, + CanDelete = Config.CanDelete, + AllowedSlot = Multicharacter.slots, + Locale = Locales[Config.Locale].UI, } - } + }) - self:AddCharacters() - self.onUse = function(_, SelectedCharacter) - if SelectedCharacter.new then - self:NewCharacter() - else - if SelectedCharacter.value ~= Multicharacter.spawned then - Multicharacter:SetupCharacter(SelectedCharacter.value) - local playerPed = PlayerPedId() - SetPedAoBlobRendering(playerPed, true) - ResetEntityAlpha(playerPed) - end - self:CharacterOptions() - end - end - - self:OpenMenu() + SetNuiFocus(true, true) end - -function Menu:CharacterOptions() - local currentCharacter = Multicharacter.Characters[Multicharacter.spawned] - local elements = { - { - title = TranslateCap("character", currentCharacter.firstname .. " " .. currentCharacter.lastname), - icon = "fa-regular fa-user", - unselectable = true - }, - { - title = TranslateCap("return"), - unselectable = false, - icon = "fa-solid fa-arrow-left", - description = TranslateCap("return_description"), - action = "return" - }, - } - - if not currentCharacter.disabled then - elements[3] = { - title = TranslateCap("char_play"), - description = TranslateCap("char_play_description"), - icon = "fa-solid fa-play", - action = "play", - } - else - elements[3] = { - title = TranslateCap("char_disabled"), - icon = "fa-solid fa-xmark", - description = TranslateCap("char_disabled_description") - } - end - if Config.CanDelete then - elements[4] = { - title = TranslateCap("char_delete"), - icon = "fa-solid fa-xmark", - description = TranslateCap("char_delete_description"), - action = "delete", - } - end - - self.currentElements = elements - self.onUse = function(_, Action) - if Action.action == "play" then - Multicharacter:CloseUI() - self:Close() - - TriggerServerEvent("esx_multicharacter:CharacterChosen", Multicharacter.spawned, false) - elseif Action.action == "delete" then - self:ConfirmDeletion() - elseif Action.action == "return" then - self:SelectCharacter() - end - end - - self:OpenMenu() +function Menu:SelectCharacter(index) + Multicharacter:SetupCharacter(index) + local playerPed = PlayerPedId() + SetPedAoBlobRendering(playerPed, true) + ResetEntityAlpha(playerPed) end -function Menu:ConfirmDeletion() - self.currentElements = { - { - title = TranslateCap("char_delete_confirmation"), - icon = "fa-solid fa-users", - description = TranslateCap("char_delete_confirmation_description"), - unselectable = true - }, - { - title = TranslateCap("char_delete"), - icon = "fa-solid fa-xmark", - description = TranslateCap("char_delete_yes_description"), - action = "delete", - }, - { - title = TranslateCap("return"), - unselectable = false, - icon = "fa-solid fa-arrow-left", - description = TranslateCap("char_delete_no_description"), - action = "return" - }, - } +function Menu:PlayCharacter() + Multicharacter:CloseUI() + TriggerServerEvent("esx_multicharacter:CharacterChosen", Multicharacter.spawned, false) +end - self.onUse = function(_, Action) - if Action.action == "delete" then - self:Close() - - TriggerServerEvent("esx_multicharacter:DeleteCharacter", Multicharacter.spawned) - Multicharacter.spawned = false - elseif Action.action == "return" then - self:CharacterOptions() - end - end - - self:OpenMenu() +function Menu:DeleteCharacter() + TriggerServerEvent("esx_multicharacter:DeleteCharacter", Multicharacter.spawned) + Multicharacter.spawned = false end \ No newline at end of file diff --git a/[core]/esx_multicharacter/client/modules/multicharacter.lua b/[core]/esx_multicharacter/client/modules/multicharacter.lua index ef099f58..1c6014fc 100644 --- a/[core]/esx_multicharacter/client/modules/multicharacter.lua +++ b/[core]/esx_multicharacter/client/modules/multicharacter.lua @@ -12,8 +12,8 @@ function Multicharacter:SetupCamera() local offset = GetOffsetFromEntityInWorldCoords(self.playerPed, 0, 1.7, 0.4) - SetCamCoord(self.cam, offset.x, offset.y, offset.z) - PointCamAtCoord(self.cam, self.spawnCoords.x, self.spawnCoords.y, self.spawnCoords.z + 1.3) + SetCamCoord(self.cam, offset.x + 0.7, offset.y , offset.z) + PointCamAtCoord(self.cam, self.spawnCoords.x + 0.4, self.spawnCoords.y, self.spawnCoords.z + 1.3) end function Multicharacter:AwaitFadeIn() @@ -116,6 +116,10 @@ function Multicharacter:ChangeExistingPed() local newCharacter = self.Characters[self.tempIndex] local spawnedCharacter = self.Characters[self.spawned] + if not newCharacter.model then + newCharacter.model = newCharacter.sex == TranslateCap("male") and `mp_m_freemode_01` or `mp_f_freemode_01` + end + if spawnedCharacter and spawnedCharacter.model then local model = ESX.Streaming.RequestModel(newCharacter.model) if model then @@ -123,7 +127,6 @@ function Multicharacter:ChangeExistingPed() SetModelAsNoLongerNeeded(newCharacter.model) end end - TriggerEvent("skinchanger:loadSkin", newCharacter.skin) end @@ -135,8 +138,12 @@ end function Multicharacter:CloseUI() SendNUIMessage({ - action = "closeui", + action = "ToggleMulticharacter", + data = { + show = false + } }) + SetNuiFocus(false, false) end function Multicharacter:SetupCharacter(index) @@ -152,10 +159,6 @@ function Multicharacter:SetupCharacter(index) self.spawned = index self.playerPed = PlayerPedId() self:PrepForUI() - SendNUIMessage({ - action = "openui", - character = character, - }) end function Multicharacter:SetupUI(characters, slots) @@ -180,7 +183,7 @@ function Multicharacter:SetupUI(characters, slots) TriggerEvent("esx_identity:showRegisterIdentity") end) else - Menu:SelectCharacter() + Menu:InitCharacter() end end diff --git a/[core]/esx_multicharacter/client/modules/nui.lua b/[core]/esx_multicharacter/client/modules/nui.lua new file mode 100644 index 00000000..fc132b40 --- /dev/null +++ b/[core]/esx_multicharacter/client/modules/nui.lua @@ -0,0 +1,23 @@ +RegisterNuiCallback('SelectCharacter', function (data, cb) + local selectedIndex = tonumber(data.id) + + if selectedIndex then + Menu:SelectCharacter(selectedIndex) + end + cb('ok') +end) + +RegisterNuiCallback('PlayCharacter', function (data, cb) + Menu:PlayCharacter() + cb('ok') +end) + +RegisterNuiCallback('DeleteCharacter', function (data, cb) + Menu:DeleteCharacter() + cb('ok') +end) + +RegisterNuiCallback('CreateCharacter', function (data, cb) + Menu:NewCharacter() + cb('ok') +end) \ No newline at end of file diff --git a/[core]/esx_multicharacter/config.lua b/[core]/esx_multicharacter/config.lua index 9c80d374..aa2aa241 100644 --- a/[core]/esx_multicharacter/config.lua +++ b/[core]/esx_multicharacter/config.lua @@ -7,7 +7,7 @@ Config.CanDelete = true if IsDuplicityVersion() then -- This is the default number of slots for EVERY player -- If you want to manage extra slots for specific players you can do it by using '/setslots' and '/remslots' commands - Config.Slots = 4 + Config.Slots = 3 -------------------- -- Text to prepend to each character (char#:identifier) - keep it short diff --git a/[core]/esx_multicharacter/fxmanifest.lua b/[core]/esx_multicharacter/fxmanifest.lua index 3f0e7c09..ee9248a7 100644 --- a/[core]/esx_multicharacter/fxmanifest.lua +++ b/[core]/esx_multicharacter/fxmanifest.lua @@ -1,5 +1,4 @@ fx_version 'cerulean' - game 'gta5' author 'ESX-Framework - Linden - KASH' description 'Allows players to have multiple characters on the same account.' @@ -21,6 +20,6 @@ client_scripts { 'client/*.lua' } -ui_page { 'html/ui.html' } +ui_page 'web/build/index.html' -files { 'html/ui.html', 'html/css/main.css', 'html/js/app.js', 'html/locales/*.js' } +files { 'web/build/index.html', 'web/build/**/*.*'} diff --git a/[core]/esx_multicharacter/html/css/main.css b/[core]/esx_multicharacter/html/css/main.css deleted file mode 100644 index b6d866d5..00000000 --- a/[core]/esx_multicharacter/html/css/main.css +++ /dev/null @@ -1,76 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=Raleway:wght@300;600&display=swap"); -@import url("https://fonts.googleapis.com/css2?family=Oswald&display=swap"); -* { - margin: 0; - padding: 0; - user-select: none; - color: rgb(255, 255, 255); - border-radius: 15px; - font-weight: 300; - font-size: 1.6vh; - font-family: Calibri, "Helvetica", san-serif; -} - -html { - overflow: hidden; -} - -p { - margin: 0 !important; -} - -body { - background: transparent; -} - -.main-container { - display: none; - position: absolute; - top: 50%; - right: 150; - transform: translate(0, -50%); - border-radius: 15px; - background: rgba(15, 15, 15, 0.9); -} - -.header { - font-family: "Oswald", sans-serif; - position: absolute; - background: rgba(10, 10, 10, 0.9); - border-radius: 15px 15px 0px 0px; - height: 10%; - width: 100%; - left: 50%; - text-align: center; - transform: translate(-50%); - font-weight: 700; - padding-bottom: 5px; - font-size: 1.6rem; -} - -.footer { - position: absolute; - font-size: 0rem; -} - -.character-box { - display: flex; - right: 0; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; - line-height: 1.4rem; - height: calc(30rem); - width: 17rem; - border: 1px rgba(10, 10, 10, 0.7) solid; - box-shadow: 2px 1px 9px 3px rgba(10, 10, 10, 0.7); -} - -h1 { - font-family: "Oswald", sans-serif; - font-size: 22px; - padding-top: 1.3rem; - display: block; - font-weight: 0; -} diff --git a/[core]/esx_multicharacter/html/js/app.js b/[core]/esx_multicharacter/html/js/app.js deleted file mode 100644 index 00cdb7f9..00000000 --- a/[core]/esx_multicharacter/html/js/app.js +++ /dev/null @@ -1,66 +0,0 @@ -var money = Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", - minimumFractionDigits: 0, -}); - -(() => { - Kashacter = {}; - - Kashacter.ShowUI = function (data) { - $("body").css({ display: "block" }); - $(".main-container").css({ display: "block" }); - $("[data-charid=1]") - .html( - '

' + - `${translate.name} ` + - "

" + - data.firstname + - " " + - data.lastname + - '

' + - `${translate.job} ` + - "

" + - data.job + - " " + - data.job_grade + - '

' + - `${translate.money} ` + - "

" + - money.format(data.money) + - '

' + - `${translate.bank} ` + - "

" + - money.format(data.bank) + - '

' + - `${translate.dob} ` + - "

" + - data.dateofbirth + - '

' + - `${translate.gender} ` + - "

" + - data.sex + - "

" - ) - .attr("data-ischar", "true"); - }; - - Kashacter.CloseUI = function () { - $("body").css({ display: "none" }); - $(".main-container").css({ display: "none" }); - $("[data-charid=1]").html('

'); - }; - - window.onload = function (e) { - window.addEventListener("message", function (event) { - switch (event.data.action) { - case "openui": - Kashacter.ShowUI(event.data.character); - break; - case "closeui": - Kashacter.CloseUI(); - break; - } - }); - }; -})(); diff --git a/[core]/esx_multicharacter/html/locales/cs.js b/[core]/esx_multicharacter/html/locales/cs.js deleted file mode 100644 index cc84dedc..00000000 --- a/[core]/esx_multicharacter/html/locales/cs.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Jméno"; -translate.job = "Práce"; -translate.bank = "Banka"; -translate.money = "Peníze"; -translate.gender = "Pohlaví"; -translate.dob = "Datum narození"; diff --git a/[core]/esx_multicharacter/html/locales/de.js b/[core]/esx_multicharacter/html/locales/de.js deleted file mode 100644 index efd17188..00000000 --- a/[core]/esx_multicharacter/html/locales/de.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Name"; -translate.job = "Beruf"; -translate.bank = "Bankguthaben"; -translate.money = "Bargeld"; -translate.gender = "Geschlecht"; -translate.dob = "Geburtsdatum"; diff --git a/[core]/esx_multicharacter/html/locales/en.js b/[core]/esx_multicharacter/html/locales/en.js deleted file mode 100644 index 125dd3f0..00000000 --- a/[core]/esx_multicharacter/html/locales/en.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Name"; -translate.job = "Job"; -translate.bank = "Bank"; -translate.money = "Cash"; -translate.gender = "Gender"; -translate.dob = "Date of birth"; diff --git a/[core]/esx_multicharacter/html/locales/es.js b/[core]/esx_multicharacter/html/locales/es.js deleted file mode 100644 index b0791792..00000000 --- a/[core]/esx_multicharacter/html/locales/es.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Nombre"; -translate.job = "Trabajo"; -translate.bank = "Banco"; -translate.money = "Dinero"; -translate.gender = "Género"; -translate.dob = "Fecha de nacimiento"; diff --git a/[core]/esx_multicharacter/html/locales/fi.js b/[core]/esx_multicharacter/html/locales/fi.js deleted file mode 100644 index 2daf5ea3..00000000 --- a/[core]/esx_multicharacter/html/locales/fi.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Nimi"; -translate.job = "Työ"; -translate.bank = "Pankki"; -translate.money = "Käteinen"; -translate.gender = "Sukupuoli"; -translate.dob = "Syntymäaika"; diff --git a/[core]/esx_multicharacter/html/locales/fr.js b/[core]/esx_multicharacter/html/locales/fr.js deleted file mode 100644 index 597a217d..00000000 --- a/[core]/esx_multicharacter/html/locales/fr.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Nom"; -translate.job = "Métier"; -translate.bank = "Banque"; -translate.money = "Argent"; -translate.gender = "Sexe"; -translate.dob = "Date de naissance"; diff --git a/[core]/esx_multicharacter/html/locales/hu.js b/[core]/esx_multicharacter/html/locales/hu.js deleted file mode 100644 index 34091a2c..00000000 --- a/[core]/esx_multicharacter/html/locales/hu.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Név"; -translate.job = "Munka"; -translate.bank = "Bank"; -translate.money = "Készpénz"; -translate.gender = "Nem"; -translate.dob = "Születési idő"; diff --git a/[core]/esx_multicharacter/html/locales/it.js b/[core]/esx_multicharacter/html/locales/it.js deleted file mode 100644 index 0c10c733..00000000 --- a/[core]/esx_multicharacter/html/locales/it.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Nome"; -translate.job = "Lavoro"; -translate.bank = "Banca"; -translate.money = "Contanti"; -translate.gender = "Genere"; -translate.dob = "Data di nascita"; diff --git a/[core]/esx_multicharacter/html/locales/nl.js b/[core]/esx_multicharacter/html/locales/nl.js deleted file mode 100644 index 44c59000..00000000 --- a/[core]/esx_multicharacter/html/locales/nl.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Naam"; -translate.job = "Job"; -translate.bank = "Bank"; -translate.money = "Cash"; -translate.gender = "Gender"; -translate.dob = "Geboortedatum"; diff --git a/[core]/esx_multicharacter/html/locales/pl.js b/[core]/esx_multicharacter/html/locales/pl.js deleted file mode 100644 index 07b27356..00000000 --- a/[core]/esx_multicharacter/html/locales/pl.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "imię"; -translate.job = "Zajęcie"; -translate.bank = "Bank"; -translate.money = "Gotówka"; -translate.gender = "Seks"; -translate.dob = "Data urodzenia"; diff --git a/[core]/esx_multicharacter/html/locales/pt.js b/[core]/esx_multicharacter/html/locales/pt.js deleted file mode 100644 index c8eca4f8..00000000 --- a/[core]/esx_multicharacter/html/locales/pt.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Nome"; -translate.job = "Trabalho"; -translate.bank = "Banco"; -translate.money = "Dinheiro"; -translate.gender = "Género"; -translate.dob = "Data de Nascimento"; diff --git a/[core]/esx_multicharacter/html/locales/sr.js b/[core]/esx_multicharacter/html/locales/sr.js deleted file mode 100644 index 79ecfbc2..00000000 --- a/[core]/esx_multicharacter/html/locales/sr.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Ime"; -translate.job = "Posao"; -translate.bank = "Banka"; -translate.money = "Novac"; -translate.gender = "Pol"; -translate.dob = "Datum rodjenja"; diff --git a/[core]/esx_multicharacter/html/locales/sv.js b/[core]/esx_multicharacter/html/locales/sv.js deleted file mode 100644 index 0a116611..00000000 --- a/[core]/esx_multicharacter/html/locales/sv.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "Namn"; -translate.job = "Jobb"; -translate.bank = "Bank"; -translate.money = "Kontanter"; -translate.gender = "Kön"; -translate.dob = "Födelsedatum"; diff --git a/[core]/esx_multicharacter/html/locales/zh-cn.js b/[core]/esx_multicharacter/html/locales/zh-cn.js deleted file mode 100644 index 8bbf9ffc..00000000 --- a/[core]/esx_multicharacter/html/locales/zh-cn.js +++ /dev/null @@ -1,8 +0,0 @@ -const translate = new Object(); - -translate.name = "我的姓名"; -translate.job = "我的职业"; -translate.bank = "银行存款"; -translate.money = "手持现金"; -translate.gender = "我的性别"; -translate.dob = "出生日期"; \ No newline at end of file diff --git a/[core]/esx_multicharacter/html/ui.html b/[core]/esx_multicharacter/html/ui.html deleted file mode 100644 index 77ee78f9..00000000 --- a/[core]/esx_multicharacter/html/ui.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - -
-
Character Info
-
-

-
-
- -
- - - - diff --git a/[core]/esx_multicharacter/locales/cs.lua b/[core]/esx_multicharacter/locales/cs.lua deleted file mode 100644 index 5421e450..00000000 --- a/[core]/esx_multicharacter/locales/cs.lua +++ /dev/null @@ -1,28 +0,0 @@ -Locales["cs"] = { - ["male"] = "Muž", - ["female"] = "Žena", - ["select_char"] = "Zvolit Postavu", - ["select_char_description"] = "Zvol si postavu za kterou budeš hrát.", - ["create_char"] = "Vytvořit Novou Postavu", - ["char_play"] = "Hrát za postavu", - ["char_play_description"] = "Pokračovat do města.", - ["char_disabled"] = "Tato postava je zakázána!", - ["char_disabled_description"] = "Tuto postavu nemůžeš používat.", - ["char_delete"] = "Vymazat postavu", - ["char_delete_description"] = "Na vždy smazat tuto postavu.", - ["character"] = "Postava: %s", - ["return"] = "Zpět", - ["return_description"] = "Vrátit se na vybírání postav.", - ["command_setslots"] = "Nastavit sloty pro hráče v multicharacteru", - ["command_remslots"] = "Odebrat sloty pro hráče v multicharacteru", - ["command_enablechar"] = "Povolit zvolený slot hráči", - ["command_disablechar"] = "Zakázat zvolený slot pro hrače", - ["command_charslot"] = "Číslo slotu", - ["command_identifier"] = "Identifier hráče", - ["command_slots"] = "# ze slotu", - ["slotsadd"] = "Nastavil si slot %s hráči %s", - ["slotsrem"] = "Odebral si slot %s", - ["charenabled"] = "Povolil si postavu #%s z %s", - ["chardisabled"] = "Zakázal si postavu #%s z %s", - ["charnotfound"] = "Postava #%s z %s nebyla nalezena/neexistuje", -} diff --git a/[core]/esx_multicharacter/locales/da.lua b/[core]/esx_multicharacter/locales/da.lua deleted file mode 100644 index 3bec8971..00000000 --- a/[core]/esx_multicharacter/locales/da.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["da"] = { - ["male"] = "Mand", - ["female"] = "Kvinde", - ["select_char"] = "Vælg karakter", - ["select_char_description"] = "Vælg en karakter at spille som.", - ["create_char"] = "Ny Karakter", - ["char_play"] = "Spil", - ["char_play_description"] = "Fortsæt ind i byen.", - ["char_disabled"] = "Deaktiveret", - ["char_disabled_description"] = "Denne karakter er ubrugelig.", - ["char_delete"] = "Slet", - ["char_delete_description"] = "Fjern denne karakter permanent.", - ["char_delete_confirmation"] = "Slet bekræftelse", - ["char_delete_confirmation_description"] = "Er du sikker på at fjerne det valgte karakter?", - ["char_delete_yes_description"] = "Ja, jeg er sikker på at fjerne det valgte karakter", - ["char_delete_no_description"] = "Nej, vend tilbage til karakter-indstillinger", - ["character"] = "Karakter: %s", - ["return"] = "Tilbage", - ["return_description"] = "Vend tilbage til valg af karaktere.", - ["command_setslots"] = "Indstil flerkarakters slotsnummer for en spiller", - ["command_remslots"] = "Fjern flerkarakters slotsnummer for en spiller", - ["command_enablechar"] = "Aktiver en valgt karakter af en spiller", - ["command_disablechar"] = "Deaktiver en valgt karakter af en spiller", - ["command_charslot"] = "Karakterens plads nummer", - ["command_identifier"] = "Spiller identifikator", - ["command_slots"] = "# af slots", - ["slotsadd"] = "Du indstillerde %s slots til %s", - ["slotsrem"] = "Du fjernede slots til %s", - ["charenabled"] = "Du aktiverede karakter #%s af %s", - ["chardisabled"] = "Du deaktiverede tegn #%s af %s", - ["charnotfound"] = "Karakter #%s af %s eksisterer ikke", -} diff --git a/[core]/esx_multicharacter/locales/de.lua b/[core]/esx_multicharacter/locales/de.lua deleted file mode 100644 index f33c433e..00000000 --- a/[core]/esx_multicharacter/locales/de.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["de"] = { - ["male"] = "Männlich", - ["female"] = "Weiblich", - ["select_char"] = "Charakter auswählen", - ["select_char_description"] = "Wähle einen Charakter aus, mit den du spielen willst.", - ["create_char"] = "Neuer Charakter", - ["char_play"] = "Spielen", - ["char_play_description"] = "Weiter in die Stadt.", - ["char_disabled"] = "Deaktiviert", - ["char_disabled_description"] = "Dieser Charakter ist deaktiviert.", - ["char_delete"] = "Löschen", - ["char_delete_description"] = "Dieser Charakter wird dauerhaft entfernt.", - ["char_delete_confirmation"] = "Bestätigung zur Entfernung deines Charakters.", - ["char_delete_confirmation_description"] = "Bist du sicher, dass du den ausgewählten Charakter entfernen willst?", - ["char_delete_yes_description"] = "Ja, ich bin sicher, dass ich den ausgewählten Charakter lösche", - ["char_delete_no_description"] = "Nein, zurück zu den Charakteroptionen", - ["character"] = " Charakter: %s", - ["return"] = "Zurück", - ["return_description"] = "Zur Charakterauswahl zurückkehren.", - ["command_setslots"] = "Anzahl der Charakter-Slots eines Spielers festlegen", - ["command_remslots"] = "Die Anzahl der Charakter-Slots eines Spielers entfernen", - ["command_enablechar"] = "Aktiviere einen bestimmten Charakter eines Spielers", - ["command_disablechar"] = "Deaktiviere einen bestimmten Charakter eines Spielers", - ["command_charslot"] = "Slotnummer des Charakters", - ["command_identifier"] = "Spieler R* ID", - ["command_slots"] = "Anzahl von Charakter-Slots", - ["slotsadd"] = "Du hast %s Charakter-Slots auf %s gesetzt", - ["slotsrem"] = "Du hast &s Charakter-Slots auf %s entfernt", - ["charenabled"] = "Du hast den Charakter #%s von %s aktiviert", - ["chardisabled"] = "Du hast den Charakter #%s von %s deaktiviert", - ["charnotfound"] = "Charakter #%s von %s existiert nicht", -} \ No newline at end of file diff --git a/[core]/esx_multicharacter/locales/en.lua b/[core]/esx_multicharacter/locales/en.lua index 5c55fd8f..8e6ff047 100644 --- a/[core]/esx_multicharacter/locales/en.lua +++ b/[core]/esx_multicharacter/locales/en.lua @@ -1,22 +1,6 @@ Locales["en"] = { ["male"] = "Male", ["female"] = "Female", - ["select_char"] = "Select Character", - ["select_char_description"] = "Select a character to play as.", - ["create_char"] = "New Character", - ["char_play"] = "Play", - ["char_play_description"] = "Continue Into The City.", - ["char_disabled"] = "Disabled", - ["char_disabled_description"] = "This Character Is Unusable.", - ["char_delete"] = "Delete", - ["char_delete_description"] = "Permanently Remove This Character.", - ["char_delete_confirmation"] = "Delete Confirmation", - ["char_delete_confirmation_description"] = "Are you sure removing selected character?", - ["char_delete_yes_description"] = "Yes, I am sure removing selected character", - ["char_delete_no_description"] = "No, return to character options", - ["character"] = "Character: %s", - ["return"] = "Return", - ["return_description"] = "Return To Character Selection.", ["command_setslots"] = "Set multicharacter slots number of a player", ["command_remslots"] = "Remove multicharacter slots number of a player", ["command_enablechar"] = "Enable a given character of a player", @@ -29,4 +13,10 @@ Locales["en"] = { ["charenabled"] = "You enabled character #%s of %s", ["chardisabled"] = "You disabled character #%s of %s", ["charnotfound"] = "Character #%s of %s doesn't exist", + + UI = { + ["title"] = "CHARACTER SELECTION", + ["char_info_title"] = "Character Info", + ["play"] = "PLAY", + } } diff --git a/[core]/esx_multicharacter/locales/es.lua b/[core]/esx_multicharacter/locales/es.lua deleted file mode 100644 index 0f3cc6bf..00000000 --- a/[core]/esx_multicharacter/locales/es.lua +++ /dev/null @@ -1,28 +0,0 @@ -Locales["es"] = { - ["male"] = "Masculino", - ["female"] = "Femenino", - ["select_char"] = "Seleccionar personaje", - ["select_char_description"] = "Selecciona un personaje para jugar.", - ["create_char"] = "Crear nuevo personaje", - ["char_play"] = "Seleccionar personaje", - ["char_play_description"] = "Ingresar a la ciudad.", - ["char_disabled"] = "Este personaje está deshabilitado", - ["char_disabled_description"] = "Este personaje no se encuentra disponible.", - ["char_delete"] = "Borrar el personaje seleccionado", - ["char_delete_description"] = "Eliminar este personaje.", - ["character"] = "Personaje: %s", - ["return"] = "Volver", - ["return_description"] = "Volver a la selección de personajes.", - ["command_setslots"] = "Establecer el numero de slots de un jugador", - ["command_remslots"] = "Elimina slots de un jugador.", - ["command_enablechar"] = "Activar el personaje de un jugador", - ["command_disablechar"] = "Deshabilitar personaje al jugador.", - ["command_charslot"] = "Número de slot del personaje", - ["command_identifier"] = "Identificador del jugador", - ["command_slots"] = "Nº de slots", - ["slotsadd"] = "Has establecido los slots de %s a %s", - ["slotsrem"] = "Has eliminado un slot a %s", - ["charenabled"] = "Has habilitado el personaje Nº%s de %s", - ["chardisabled"] = "Has deshabilitado el personaje Nº%s de %s", - ["charnotfound"] = "Personaje Nº%s de %s no existe", -} diff --git a/[core]/esx_multicharacter/locales/fi.lua b/[core]/esx_multicharacter/locales/fi.lua deleted file mode 100644 index 9da10652..00000000 --- a/[core]/esx_multicharacter/locales/fi.lua +++ /dev/null @@ -1,33 +0,0 @@ -Locales["fi"] = { - ["male"] = "Mies", - ["female"] = "Nainen", - ["select_char"] = "Valitse hahmo", - ["select_char_description"] = "Valitse hahmo, jolla haluat pelata.", - ["create_char"] = "Uusi hahmo", - ["char_play"] = "Pelaa", - ["char_play_description"] = "Jatka kaupunkiin.", - ["char_disabled"] = "Ei käytössä", - ["char_disabled_description"] = "Tämä hahmo on käyttökelvoton.", - ["char_delete"] = "Poista", - ["char_delete_description"] = "Poista tämä hahmo pysyvästi.", - ["char_delete_confirmation"] = "Vahvista poistaminen.", - ["char_delete_confirmation_description"] = "Oletko varma, että poistat valitun hahmon?", - ["char_delete_yes_description"] = "Kyllä, olen varma, että poistan valitun hahmon", - ["char_delete_no_description"] = "Ei, Palaa hahmojen valintaan", - ["character"] = "Hahmo: %s", - ["return"] = "Takaisin", - ["return_description"] = "Palaa hahmojen valintaan.", - ["command_setslots"] = "Määritä pelaajan multicharacter paikkojen määrä", - ["command_remslots"] = "Vähennä pelaajan multicharacter paikkojen määrää", - ["command_enablechar"] = "Muuta pelaajan tietty hahmo käyttöön", - ["command_disablechar"] = "Muuta pelaajan tietty hahmo pois käytöstä", - ["command_charslot"] = "Hahmon paikan numero", - ["command_identifier"] = "Pelaajan tunniste", - ["command_slots"] = "# paikoista", - ["slotsadd"] = "Lisäsit %s paikkaa kohteeseen %s", - ["slotsedit"] = "Olet määrittänyt %s kohteeseen %s", - ["slotsrem"] = "Poistit paikat kohteesta %s", - ["charenabled"] = "Otit käyttöön hahmon #%s / %s", - ["chardisabled"] = "Poistit hahmon #%s / %s käytöstä", - ["charnotfound"] = "Hahmoa #%s / %s ei ole olemassa", -} diff --git a/[core]/esx_multicharacter/locales/fr.lua b/[core]/esx_multicharacter/locales/fr.lua deleted file mode 100644 index a64e27af..00000000 --- a/[core]/esx_multicharacter/locales/fr.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["fr"] = { - ["male"] = "Homme", - ["female"] = "Femme", - ["select_char"] = "Sélectionnez un personnage", - ["select_char_description"] = "Sélectionnez un personnage avec lequel jouer.", - ["create_char"] = "Créer un nouveau personnage", - ["char_play"] = "Jouer ce personnage", - ["char_play_description"] = "Continuez dans la ville.", - ["char_disabled"] = "Ce personnage est désactivé", - ["char_disabled_description"] = "Ce personnage est inutilisable.", - ["char_delete"] = "Supprimer ce personnage", - ["char_delete_description"] = "Supprimer définitivement ce personnage.", - ["char_delete_confirmation"] = "Confirmation de suppression", - ["char_delete_confirmation_description"] = "Êtes-vous sûr de vouloir supprimer ce personnage?", - ["char_delete_yes_description"] = "Oui, je suis sur de vouloir supprimer ce personnage", - ["char_delete_no_description"] = "Non, retourner aux options de personnages", - ["character"] = "Personnage: %s", - ["return"] = "Retour", - ["return_description"] = "Retourner à la sélection de personnage.", - ["command_setslots"] = "Définir le numéro de créneau multi-caractères d'un joueur", - ["command_remslots"] = "Suppression du numéro de créneau multi-caractères d'un joueur", - ["command_enablechar"] = "Activer un personnage donné d'un joueur", - ["command_disablechar"] = "Désactiver un personnage donné d'un joueur", - ["command_charslot"] = "Numéro d'emplacement du caractère", - ["command_identifier"] = "Identifiant du joueur", - ["command_slots"] = "# de slots", - ["slotsadd"] = "Vous avez défini %s slots à %s", - ["slotsrem"] = "Vous avez suprimé les slots à %s", - ["charenabled"] = "Vous avez activé le personnage #%s de %s", - ["chardisabled"] = "Vous avez désactivé le personnage #%s de %s", - ["charnotfound"] = "Le personnage #%s de %s n'éxiste", -} diff --git a/[core]/esx_multicharacter/locales/gr.lua b/[core]/esx_multicharacter/locales/gr.lua deleted file mode 100644 index 49b832ca..00000000 --- a/[core]/esx_multicharacter/locales/gr.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["gr"] = { - ["male"] = "Άνδρας", - ["female"] = "Γυναίκα", - ["select_char"] = "Επιλογή Χαρακτήρα", - ["select_char_description"] = "Επιλέξτε ένα χαρακτήρα για να παίξετε.", - ["create_char"] = "Νέος Χαρακτήρας", - ["char_play"] = "Παίξτε", - ["char_play_description"] = "Συνέχεια στην πόλη.", - ["char_disabled"] = "Απενεργοποιημένος", - ["char_disabled_description"] = "Αυτός ο χαρακτήρας δεν είναι χρησιμοποιήσιμος.", - ["char_delete"] = "Διαγραφή", - ["char_delete_description"] = "Διαγράψτε οριστικά αυτόν τον χαρακτήρα.", - ["char_delete_confirmation"] = "Επιβεβαίωση Διαγραφής", - ["char_delete_confirmation_description"] = "Είστε σίγουρος ότι θέλετε να διαγράψετε τον επιλεγμένο χαρακτήρα;", - ["char_delete_yes_description"] = "Ναι, είμαι σίγουρος ότι θέλω να διαγράψω τον επιλεγμένο χαρακτήρα", - ["char_delete_no_description"] = "Όχι, επιστροφή στις επιλογές χαρακτήρα", - ["character"] = "Χαρακτήρας: %s", - ["return"] = "Επιστροφή", - ["return_description"] = "Επιστροφή στην επιλογή χαρακτήρα.", - ["command_setslots"] = "Ορίστε τον αριθμό των slot πολλαπλών χαρακτήρων ενός παίκτη", - ["command_remslots"] = "Αφαιρέστε τον αριθμό των slot πολλαπλών χαρακτήρων ενός παίκτη", - ["command_enablechar"] = "Ενεργοποιήστε έναν συγκεκριμένο χαρακτήρα ενός παίκτη", - ["command_disablechar"] = "Απενεργοποιήστε έναν συγκεκριμένο χαρακτήρα ενός παίκτη", - ["command_charslot"] = "Αριθμός υποδιαίρεσης του χαρακτήρα", - ["command_identifier"] = "Ταυτότητα παίκτη", - ["command_slots"] = "Αριθμός slot", - ["slotsadd"] = "Ορίσατε %s slot σε %s", - ["slotsrem"] = "Αφαιρέσατε slot από τον %s", - ["charenabled"] = "Ενεργοποιήσατε τον χαρακτήρα #%s του %s", - ["chardisabled"] = "Απενεργοποιήσατε τον χαρακτήρα #%s του %s", - ["charnotfound"] = "Ο χαρακτήρας #%s του %s δεν υπάρχει", -} diff --git a/[core]/esx_multicharacter/locales/he.lua b/[core]/esx_multicharacter/locales/he.lua deleted file mode 100644 index a6e8267e..00000000 --- a/[core]/esx_multicharacter/locales/he.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["he"] = { - ["male"] = "זכר", - ["female"] = "נקבה", - ["select_char"] = "בחר דמות", - ["select_char_description"] = "בחר דמות לשחק.", - ["create_char"] = "דמות חדשה", - ["char_play"] = "שחק", - ["char_play_description"] = "המשך לעיר.", - ["char_disabled"] = "מושבת", - ["char_disabled_description"] = "דמות זו לא ניתן לשימוש.", - ["char_delete"] = "מחק", - ["char_delete_description"] = "הסר את הדמות לצמיתות.", - ["char_delete_confirmation"] = "אישור מחיקה", - ["char_delete_confirmation_description"] = "האם אתה בטוח שאתה מסיר את הדמות שנבחרה?", - ["char_delete_yes_description"] = "כן, אני בטוח שאני מסיר את הדמות שנבחרה", - ["char_delete_no_description"] = "לא, חזור לאפשרויות הדמות", - ["character"] = "דמות: %s", - ["return"] = "חזור", - ["return_description"] = "חזור לבחירת הדמות.", - ["command_setslots"] = "הגדר מספר מקומות לדמות של שחקן", - ["command_remslots"] = "הסר מספר מקומות לדמות של שחקן", - ["command_enablechar"] = "אפשר דמות מסוימת של שחקן", - ["command_disablechar"] = "השבת דמות מסוימת של שחקן", - ["command_charslot"] = "מספר מקום של הדמות", - ["command_identifier"] = "מזהה שחקן", - ["command_slots"] = "# של מקומות", - ["slotsadd"] = "הגדרת %s מקומות ל- %s", - ["slotsrem"] = "הסרת מקומות ל- %s", - ["charenabled"] = "אפשרת את הדמות #%s של %s", - ["chardisabled"] = "השבתת את הדמות #%s של %s", - ["charnotfound"] = "הדמות #%s של %s לא קיימת", -} diff --git a/[core]/esx_multicharacter/locales/hu.lua b/[core]/esx_multicharacter/locales/hu.lua deleted file mode 100644 index b4d29a7f..00000000 --- a/[core]/esx_multicharacter/locales/hu.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["hu"] = { - ["male"] = "Férfi", - ["female"] = "Nő", - ["select_char"] = "Karakter kiválasztása", - ["select_char_description"] = "Karakter kiválasztása a játékhoz.", - ["create_char"] = "Új karakter létrehozása", - ["char_play"] = "Karakter kiválasztása", - ["char_play_description"] = "Visszatérés a városba.", - ["char_disabled"] = "Karakter le van tiltva", - ["char_disabled_description"] = "Ez a karakter nem használható.", - ["char_delete"] = "Karakter törlése", - ["char_delete_description"] = "Karakter végleges törlése.", - ["char_delete_confirmation"] = "Törlés megerősítés", - ["char_delete_confirmation_description"] = "Biztosan törölni szeretnéd a kiválasztott karaktert?", - ["char_delete_yes_description"] = "Igen, törölni szeretném", - ["char_delete_no_description"] = "Nem, vissza a karakter opciókhoz", - ["character"] = "Karakter: %s", - ["return"] = "Vissza", - ["return_description"] = "Vissza a karakter választáshoz.", - ["command_setslots"] = "Játékos karakter slotok számának beállítása", - ["command_remslots"] = "Játékos karakter slotok számának eltávolítása", - ["command_enablechar"] = "Játékos karakterének engedélyezése", - ["command_disablechar"] = "Játékos karakterének tiltása", - ["command_charslot"] = "Karakter slotok számának beállítása", - ["command_identifier"] = "Játékos identifier", - ["command_slots"] = "# slotok", - ["slotsadd"] = "Beállítottad %s slotokat, neki %s", - ["slotsrem"] = "Eltávolítottad a következő slotokat: %s", - ["charenabled"] = "Karakter engedélyezve #%s-ból/ből %s", - ["chardisabled"] = "Karakter letiltva #%s-ból/ből %s", - ["charnotfound"] = "Karakter #%s-ból/ből %s nem létezik", -} diff --git a/[core]/esx_multicharacter/locales/it.lua b/[core]/esx_multicharacter/locales/it.lua deleted file mode 100644 index f8a596bd..00000000 --- a/[core]/esx_multicharacter/locales/it.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["it"] = { - ["male"] = "Uomo", - ["female"] = "Donna", - ["select_char"] = "Seleziona personaggio", - ["select_char_description"] = "Seleziona un personaggio con cui giocare.", - ["create_char"] = "Nuovo personaggio", - ["char_play"] = "Inizia", - ["char_play_description"] = "Continua in città.", - ["char_disabled"] = "Disabilitato", - ["char_disabled_description"] = "Questo personaggio è inutilizzabile.", - ["char_delete"] = "Elimina", - ["char_delete_description"] = "Rimuovi permanentemente questo personaggio..", - ["char_delete_confirmation"] = "Conferma eliminazione", - ["char_delete_confirmation_description"] = "Sei sicuro di voler rimuovere il personaggio selezionato?", - ["char_delete_yes_description"] = "Si, sono sicuro di voler rimuovere il personaggio", - ["char_delete_no_description"] = "No, ritorna alle opzioni", - ["character"] = "Personaggio: %s", - ["return"] = "Ritorno", - ["return_description"] = "Ritorna alla selezione dei personaggi.", - ["command_setslots"] = "imposta il numero di slot per il giocatore", - ["command_remslots"] = "Rimuovi slot per il giocatore", - ["command_enablechar"] = "Abilita un determinato personaggio di un giocatore", - ["command_disablechar"] = "Disabilita un determinato personaggio per i giocatore", - ["command_charslot"] = "Numero di slot", - ["command_identifier"] = "Identificativo del giocatore", - ["command_slots"] = "# di slot", - ["slotsadd"] = "Hai impostato %s slots a %s", - ["slotsrem"] = "Hai rimosso gli slot in %s", - ["charenabled"] = "Hai abilitato il personaggio #%s di %s", - ["chardisabled"] = "hai disabilitato il personaggio #%s di %s", - ["charnotfound"] = "Il personaggio #%s di %s non esiste", -} diff --git a/[core]/esx_multicharacter/locales/nl.lua b/[core]/esx_multicharacter/locales/nl.lua deleted file mode 100644 index 9bf90040..00000000 --- a/[core]/esx_multicharacter/locales/nl.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["nl"] = { - ["male"] = "Man", - ["female"] = "Vrouw", - ["select_char"] = "Selecteer Karakter", - ["select_char_description"] = "Selecteer een karakter om mee te spelen.", - ["create_char"] = "Maak karakter aan", - ["char_play"] = "Speel", - ["char_play_description"] = "Ga verder naar de stad.", - ["char_disabled"] = "Uitgeschakeld", - ["char_disabled_description"] = "Dit karakter is onbruikbaar.", - ["char_delete"] = "Verwijder", - ["char_delete_description"] = "Verwijder dit karakter (PERMANENT).", - ["char_delete_confirmation"] = "Bevestig", - ["char_delete_confirmation_description"] = "Ben je zeker dat je het geselecteerde karakter wilt verwijderen?", - ["char_delete_yes_description"] = "Ja, ik ben zeker dat ik het karakter wil verwijderen", - ["char_delete_no_description"] = "Nee, ga terug naar karakter opties", - ["character"] = "Karakter: %s", - ["return"] = "Ga terug", - ["return_description"] = "Ga terug naar karakter selectie.", - ["command_setslots"] = "Multicharacter hoeveelheid slots van een speler instellen", - ["command_remslots"] = "Verwijder slotnummer van een speler", - ["command_enablechar"] = "Een bepaald karakter van een speler inschakelen", - ["command_disablechar"] = "Een bepaald karakter van een speler uitschakelen", - ["command_charslot"] = "Slotnummer van het personage", - ["command_identifier"] = "Speler identifier", - ["command_slots"] = "# van de slots", - ["slotsadd"] = "Je hebt %s slots gezet naar %s", - ["slotsrem"] = "Je hebt slots verwijderd naar %s", - ["charenabled"] = "Je hebt karakter #%s ingeschakeld van %s", - ["chardisabled"] = "Je hebt karakter #%s uitgeschakeld van %s", - ["charnotfound"] = "Karakter #%s van %s bestaat niet", -} diff --git a/[core]/esx_multicharacter/locales/pl.lua b/[core]/esx_multicharacter/locales/pl.lua deleted file mode 100644 index b71f35fd..00000000 --- a/[core]/esx_multicharacter/locales/pl.lua +++ /dev/null @@ -1,33 +0,0 @@ -Locales["pl"] = { - ["male"] = "Mężczyzna", - ["female"] = "Kobieta", - ["select_char"] = "Wybierz postać", - ["select_char_description"] = "Wybierz postać, którą chcesz grać.", - ["create_char"] = "Nowa postać", - ["char_play"] = "Graj", - ["char_play_description"] = "Przejdź do miasta.", - ["char_disabled"] = "Wyłączona", - ["char_disabled_description"] = "Ta postać jest niedostępna.", - ["char_delete"] = "Usuń", - ["char_delete_description"] = "Trwale usuń tę postać.", - ["char_delete_confirmation"] = "Potwierdz usunięcie", - ["char_delete_confirmation_description"] = "Czy na pewno chcesz usunąć wybraną postać?", - ["char_delete_yes_description"] = "Tak, jestem pewien, że chcę usunąć wybraną postać", - ["char_delete_no_description"] = "Nie, wróć do opcji postaci", - ["character"] = "Postać: %s", - ["return"] = "Powrót", - ["return_description"] = "Wróć do wyboru postaci.", - ["command_setslots"] = "Ustaw liczbę slotów dla wielu postaci", - ["command_remslots"] = "Usuń liczbę slotów dla wielu postaci", - ["command_enablechar"] = "Włącz określoną postać", - ["command_disablechar"] = "Wyłącz określoną postać", - ["command_charslot"] = "Numer slotu postaci", - ["command_identifier"] = "Identyfikator postaci", - ["command_slots"] = "# liczba slotów", - ["slotsadd"] = "Ustawiłeś %s slotów dla %s", - ["slotsrem"] = "Usunąłeś sloty dla %s", - ["charenabled"] = "Włączyłeś postać #%s gracza %s", - ["chardisabled"] = "Wyłączyłeś postać #%s gracza %s", - ["charnotfound"] = "Postać #%s gracza %s nie istnieje", - -} diff --git a/[core]/esx_multicharacter/locales/pt.lua b/[core]/esx_multicharacter/locales/pt.lua deleted file mode 100644 index 36df3e00..00000000 --- a/[core]/esx_multicharacter/locales/pt.lua +++ /dev/null @@ -1,25 +0,0 @@ -Locales["pt"] = { - ["male"] = "Masculino", - ["female"] = "Feminino", - ["delete_label"] = "Excluir %s %s?", - ["select_char"] = "Selecionar personagem", - ["select_char_description"] = "Select a character to play as.", - ["create_char"] = "Criar novo personagem", - ["char_play"] = "Selecionar", - ["char_disabled"] = "Este personagem está desabilitado", - ["char_delete"] = "Excluir este personagem", - ["cancel"] = "Cancelar", - ["confirm"] = "Confirmar", - ["command_setslots"] = "Definir o número de slots de vários personagens de um jogador", - ["command_remslots"] = "Remover o número de slots de vários personagens de um jogador", - ["command_enablechar"] = "Habilitar um determinado personagem de um jogador", - ["command_disablechar"] = "Desabilitar um determinado personagem de um jogador", - ["command_charslot"] = "Número do slot do personagem", - ["command_identifier"] = "Identificador do jogador", - ["command_slots"] = "# de slots", - ["slotsadd"] = "Você definiu %s slots para %s", - ["slotsrem"] = "Você removeu slots de %s", - ["charenabled"] = "Você ativou o personagem #%s de %s", - ["chardisabled"] = "Você desativou o personagem #%s de %s", - ["charnotfound"] = "Personagem #%s de %s não existe", -} diff --git a/[core]/esx_multicharacter/locales/sl.lua b/[core]/esx_multicharacter/locales/sl.lua deleted file mode 100644 index 799a1563..00000000 --- a/[core]/esx_multicharacter/locales/sl.lua +++ /dev/null @@ -1,33 +0,0 @@ -Locales["sl"] = { - ["male"] = "Moški", - ["female"] = "Ženska", - ["select_char"] = "Izberi znak", - ["select_char_description"] = "Izberite lik za igranje.", - ["create_char"] = "Nov lik", - ["char_play"] = "Predvajaj", - ["char_play_description"] = "Nadaljujte v mesto.", - ["char_disabled"] = "Onemogočeno", - ["char_disabled_description"] = "Ta znak je neuporaben.", - ["char_delete"] = "Izbriši", - ["char_delete_description"] = "Trajno odstrani ta znak.", - ["char_delete_confirmation"] = "Potrditev brisanja", - ["char_delete_confirmation_description"] = "Ali ste prepicani da hocete zbrisati svoj karakter*", - ["char_delete_yes_description"] = "Da", - ["char_delete_no_description"] = "Ne", - ["character"] = "Znak: %s", - ["return"] = "Vrni se", - ["return_description"] = "Nazaj na izbiro znakov.", - ["command_setslots"] = "Nastavi število večznakovnih mest za igralca", - ["command_remslots"] = "Odstrani večznakovno število rež igralca", - ["command_enablechar"] = "Omogoči določen lik igralca", - ["command_disablechar"] = "Onemogoči določen lik igralca", - ["command_charslot"] = "Številka reže znaka", - ["command_identifier"] = "Identifikator igralca", - ["command_slots"] = "Št. rež", - ["slotsadd"] = "Dodali ste %s rež v %s", - ["slotsedit"] = "%s rež ste nastavili na %s", - ["slotsrem"] = "Odstranili ste reže za %s", - ["charenabled"] = "Omogočili ste znak #%s od %s", - ["chardisabled"] = "Onemogočili ste znak #%s od %s", - ["charnotfound"] = "Znak #%s od %s ne obstaja", -} diff --git a/[core]/esx_multicharacter/locales/sr.lua b/[core]/esx_multicharacter/locales/sr.lua deleted file mode 100644 index 85978aca..00000000 --- a/[core]/esx_multicharacter/locales/sr.lua +++ /dev/null @@ -1,33 +0,0 @@ -Locales["sr"] = { - ["male"] = "Muško", - ["female"] = "Žensko", - ["select_char"] = "Izaberi Karakter", - ["select_char_description"] = "Izaberi karakter kojim ćeš igrati", - ["create_char"] = "Novi Karakter", - ["char_play"] = "Igraj", - ["char_play_description"] = "Nastavi u grad.", - ["char_disabled"] = "Onemogućeno", - ["char_disabled_description"] = "Taj karakter je onemogućen", - ["char_delete"] = "Izbriši", - ["char_delete_description"] = "Nepovratno obrišite karakter", - ["char_delete_confirmation"] = "Potvrda brisanja", - ["char_delete_confirmation_description"] = "Da li ste sigurni da želite da obrišete karakter?", - ["char_delete_yes_description"] = "Da, siguran sam da želim obrisati karakter", - ["char_delete_no_description"] = "Ne, vrati se nazad", - ["character"] = "Karakter: %s", - ["return"] = "Nazad", - ["return_description"] = "Povratak na biranje karaktera.", - ["command_setslots"] = "Postavi broj slotova karaktera igraču", - ["command_remslots"] = "Izbriši slotove igraču", - ["command_enablechar"] = "Dozvoli karakter igraču", - ["command_disablechar"] = "Zabrani karakter igraču", - ["command_charslot"] = "Broj slotova", - ["command_identifier"] = "Identifikator Igrača", - ["command_slots"] = "# slotova", - ["slotsadd"] = "Dodali ste %s slotova %s", - ["slotsedit"] = "Postavili ste %s slotova %s", - ["slotsrem"] = "Oduzeli ste slotove %s", - ["charenabled"] = "Dozvolili ste karakter #%s od %s", - ["chardisabled"] = "Zabranili ste karakter #%s od %s", - ["charnotfound"] = "Karakter #%s od %s ne postoji", -} diff --git a/[core]/esx_multicharacter/locales/sv.lua b/[core]/esx_multicharacter/locales/sv.lua deleted file mode 100644 index d726459b..00000000 --- a/[core]/esx_multicharacter/locales/sv.lua +++ /dev/null @@ -1,32 +0,0 @@ -Locales["sv"] = { - ["male"] = "Man", - ["female"] = "Kvinna", - ["select_char"] = "Välj karaktär", - ["select_char_description"] = "Välj en karaktär att spela som.", - ["create_char"] = "Ny karaktär", - ["char_play"] = "Spela", - ["char_play_description"] = "Fortsätt i staden.", - ["char_disabled"] = "Otillgänglig", - ["char_disabled_description"] = "Denna karaktär är inte tillgänglig.", - ["char_delete"] = "Radera", - ["char_delete_description"] = "Permanent radera denna karaktär.", - ["char_delete_confirmation"] = "Acceptera", - ["char_delete_confirmation_description"] = "Säker att du vill radera den?", - ["char_delete_yes_description"] = "Ja, jag är säker", - ["char_delete_no_description"] = "Nej, gå tillbaka till karaktärerna", - ["character"] = "Karaktär: %s", - ["return"] = "Tillbaka", - ["return_description"] = "Tillbaka till karaktärerna.", - ["command_setslots"] = "Ge slots till en spelare", - ["command_remslots"] = "Radera slots från en spelare", - ["command_enablechar"] = "Aktivera vald karaktär till spelare", - ["command_disablechar"] = "Avaktivera vald karaktär till spelare", - ["command_charslot"] = "Slotsnummer på karaktären", - ["command_identifier"] = "Spelare", - ["command_slots"] = "# av slots", - ["slotsadd"] = "Du satte %s slots till %s", - ["slotsrem"] = "Du raderade slots till %s", - ["charenabled"] = "Du aktiverade karaktär #%s av %s", - ["chardisabled"] = "Du avaktiverade #%s av %s", - ["charnotfound"] = "Karaktär #%s av %s existerar inte", -} diff --git a/[core]/esx_multicharacter/locales/zh-cn.lua b/[core]/esx_multicharacter/locales/zh-cn.lua deleted file mode 100644 index 9f45bc58..00000000 --- a/[core]/esx_multicharacter/locales/zh-cn.lua +++ /dev/null @@ -1,33 +0,0 @@ -Locales["zh-cn"] = { - ["male"] = "男性", - ["female"] = "女性", - ["select_char"] = "选择角色", - ["select_char_description"] = "请选择已存在的游戏角色进入.", - ["create_char"] = "创建新角色", - ["char_play"] = "游玩", - ["char_play_description"] = "继而进入城镇.", - ["char_disabled"] = "不可用", - ["char_disabled_description"] = "此角色无法使用.", - ["char_delete"] = "移除", - ["char_delete_description"] = "永久删除此角色.", - ["char_delete_confirmation"] = "删除确认", - ["char_delete_confirmation_description"] = "您确定要删除选定的角色吗?", - ["char_delete_yes_description"] = "是的,我确定删除选定的角色", - ["char_delete_no_description"] = "取消,并返回至角色选项", - ["character"] = "游戏角色: %s", - ["return"] = "返回", - ["return_description"] = "返回至角色选择.", - ["command_setslots"] = "设置玩家多玩家插槽数量", - ["command_remslots"] = "移除玩家多角色位置数量", - ["command_enablechar"] = "启用玩家游玩某个角色", - ["command_disablechar"] = "禁用玩家游玩某个角色", - ["command_charslot"] = "玩家的角色数量上限", - ["command_identifier"] = "玩家标识符", - ["command_slots"] = "# 插槽数量", - ["slotsadd"] = "增加: %s 角色插槽至 %s", - ["slotsedit"] = "成功配置 %s 角色插槽至 %s", - ["slotsrem"] = "成功移除角色插槽 %s", - ["charenabled"] = "您开启了角色: #%s使用权, 拥有人%s", - ["chardisabled"] = "您关闭了角色: #%s使用权, 拥有人%s", - ["charnotfound"] = "角色信息: #%s ,拥有人:%s 并不存在", -} diff --git a/[core]/esx_multicharacter/web/build/assets/esxLogo-sNwPFb_p.png b/[core]/esx_multicharacter/web/build/assets/esxLogo-sNwPFb_p.png new file mode 100644 index 0000000000000000000000000000000000000000..9be59f9992b9df8e2b5f20c858d19a323f402984 GIT binary patch literal 19788 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%0A^;iF4Hueoa%Y*JK1V&F^$J%$E4xJ@W&e9r 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:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-\[5px\]{border-radius:5px}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.border-2{border-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-\[\#fb9b04\]{--tw-border-opacity: 1;border-color:rgb(251 155 4 / var(--tw-border-opacity))}.border-\[\#ffffff50\]{border-color:#ffffff50}.border-neutral-800{--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity))}.bg-\[\#161616F2\]{background-color:#161616f2}.bg-\[\#38383880\]{background-color:#38383880}.bg-\[\#FB9B04\]{--tw-bg-opacity: 1;background-color:rgb(251 155 4 / var(--tw-bg-opacity))}.bg-\[\#fb9b0440\]{background-color:#fb9b0440}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity))}.bg-neutral-800{--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity))}.bg-neutral-900{--tw-bg-opacity: 1;background-color:rgb(28 28 28 / var(--tw-bg-opacity))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(255 152 0 / var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-right{text-align:right}.text-\[16px\]{font-size:16px}.text-\[24px\]{font-size:24px}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wide{letter-spacing:.025em}.text-\[\#383838\]{--tw-text-opacity: 1;color:rgb(56 56 56 / var(--tw-text-opacity))}.text-\[\#fb9b04\]{--tw-text-opacity: 1;color:rgb(251 155 4 / var(--tw-text-opacity))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}:root{--primary: #ff9800;--primary-dark: #f57c00;--dark-bg: #1c1c1c;--card-bg: #262626}body{margin:0;font-family:Poppins,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#fff}@keyframes slideIn{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideDown{0%{max-height:0;opacity:0;transform:translateY(-10px)}to{max-height:500px;opacity:1;transform:translateY(0)}}.animate-slideIn{animation:slideIn .5s ease-out forwards}.animate-slideDown{animation:slideDown .3s ease-out forwards;overflow:hidden}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}.hover\:border-\[\#FFFFFF\]:hover{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.hover\:bg-\[\#FFA31A\]:hover{--tw-bg-opacity: 1;background-color:rgb(255 163 26 / var(--tw-bg-opacity))}.hover\:bg-orange-600:hover{--tw-bg-opacity: 1;background-color:rgb(245 124 0 / var(--tw-bg-opacity))}.hover\:bg-red-900:hover{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity))}.hover\:text-\[\#383838\]:hover{--tw-text-opacity: 1;color:rgb(56 56 56 / var(--tw-text-opacity))}.group:hover .group-hover\:text-\[\#FFFFFF\]{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))} diff --git a/[core]/esx_multicharacter/web/build/assets/index-DGfzXZP_.js b/[core]/esx_multicharacter/web/build/assets/index-DGfzXZP_.js new file mode 100644 index 00000000..385b8f25 --- /dev/null +++ b/[core]/esx_multicharacter/web/build/assets/index-DGfzXZP_.js @@ -0,0 +1,93 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();var Ss={exports:{}},fl={},xs={exports:{}},I={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var er=Symbol.for("react.element"),Gc=Symbol.for("react.portal"),Xc=Symbol.for("react.fragment"),Zc=Symbol.for("react.strict_mode"),Jc=Symbol.for("react.profiler"),qc=Symbol.for("react.provider"),bc=Symbol.for("react.context"),ef=Symbol.for("react.forward_ref"),tf=Symbol.for("react.suspense"),nf=Symbol.for("react.memo"),rf=Symbol.for("react.lazy"),tu=Symbol.iterator;function lf(e){return e===null||typeof e!="object"?null:(e=tu&&e[tu]||e["@@iterator"],typeof e=="function"?e:null)}var Es={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Cs=Object.assign,_s={};function on(e,t,n){this.props=e,this.context=t,this.refs=_s,this.updater=n||Es}on.prototype.isReactComponent={};on.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};on.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Ns(){}Ns.prototype=on.prototype;function ni(e,t,n){this.props=e,this.context=t,this.refs=_s,this.updater=n||Es}var ri=ni.prototype=new Ns;ri.constructor=ni;Cs(ri,on.prototype);ri.isPureReactComponent=!0;var nu=Array.isArray,Ps=Object.prototype.hasOwnProperty,li={current:null},Ts={key:!0,ref:!0,__self:!0,__source:!0};function Ls(e,t,n){var r,l={},o=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(o=""+t.key),t)Ps.call(t,r)&&!Ts.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,Z=N[W];if(0>>1;Wl(zl,j))ytl(ir,zl)?(N[W]=ir,N[yt]=j,W=yt):(N[W]=zl,N[vt]=j,W=vt);else if(ytl(ir,j))N[W]=ir,N[yt]=j,W=yt;else break e}}return z}function l(N,z){var j=N.sortIndex-z.sortIndex;return j!==0?j:N.id-z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var s=[],a=[],p=1,m=null,h=3,v=!1,w=!1,y=!1,C=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,c=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function d(N){for(var z=n(a);z!==null;){if(z.callback===null)r(a);else if(z.startTime<=N)r(a),z.sortIndex=z.expirationTime,t(s,z);else break;z=n(a)}}function g(N){if(y=!1,d(N),!w)if(n(s)!==null)w=!0,Tl(E);else{var z=n(a);z!==null&&Ll(g,z.startTime-N)}}function E(N,z){w=!1,y&&(y=!1,f(P),P=-1),v=!0;var j=h;try{for(d(z),m=n(s);m!==null&&(!(m.expirationTime>z)||N&&!Pe());){var W=m.callback;if(typeof W=="function"){m.callback=null,h=m.priorityLevel;var Z=W(m.expirationTime<=z);z=e.unstable_now(),typeof Z=="function"?m.callback=Z:m===n(s)&&r(s),d(z)}else r(s);m=n(s)}if(m!==null)var or=!0;else{var vt=n(a);vt!==null&&Ll(g,vt.startTime-z),or=!1}return or}finally{m=null,h=j,v=!1}}var S=!1,x=null,P=-1,O=5,L=-1;function Pe(){return!(e.unstable_now()-LN||125W?(N.sortIndex=j,t(a,N),n(s)===null&&N===n(a)&&(y?(f(P),P=-1):y=!0,Ll(g,j-W))):(N.sortIndex=Z,t(s,N),w||v||(w=!0,Tl(E))),N},e.unstable_shouldYield=Pe,e.unstable_wrapCallback=function(N){var z=h;return function(){var j=h;h=z;try{return N.apply(this,arguments)}finally{h=j}}}})(Rs);Ms.exports=Rs;var vf=Ms.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var yf=F,ge=vf;function k(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),oo=Object.prototype.hasOwnProperty,gf=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,lu={},ou={};function wf(e){return oo.call(ou,e)?!0:oo.call(lu,e)?!1:gf.test(e)?ou[e]=!0:(lu[e]=!0,!1)}function kf(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function Sf(e,t,n,r){if(t===null||typeof t>"u"||kf(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ae(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var te={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){te[e]=new ae(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];te[t]=new ae(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){te[e]=new ae(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){te[e]=new ae(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){te[e]=new ae(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){te[e]=new ae(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){te[e]=new ae(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){te[e]=new ae(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){te[e]=new ae(e,5,!1,e.toLowerCase(),null,!1,!1)});var ii=/[\-:]([a-z])/g;function ui(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ii,ui);te[t]=new ae(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ii,ui);te[t]=new ae(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ii,ui);te[t]=new ae(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!1,!1)});te.xlinkHref=new ae("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){te[e]=new ae(e,1,!1,e.toLowerCase(),null,!0,!0)});function si(e,t,n,r){var l=te.hasOwnProperty(t)?te[t]:null;(l!==null?l.type!==0:r||!(2u||l[i]!==o[u]){var s=` +`+l[i].replace(" at new "," at ");return e.displayName&&s.includes("")&&(s=s.replace("",e.displayName)),s}while(1<=i&&0<=u);break}}}finally{Ml=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?xn(e):""}function xf(e){switch(e.tag){case 5:return xn(e.type);case 16:return xn("Lazy");case 13:return xn("Suspense");case 19:return xn("SuspenseList");case 0:case 2:case 15:return e=Rl(e.type,!1),e;case 11:return e=Rl(e.type.render,!1),e;case 1:return e=Rl(e.type,!0),e;default:return""}}function ao(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Rt:return"Fragment";case Mt:return"Portal";case io:return"Profiler";case ai:return"StrictMode";case uo:return"Suspense";case so:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ds:return(e.displayName||"Context")+".Consumer";case Fs:return(e._context.displayName||"Context")+".Provider";case ci:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case fi:return t=e.displayName||null,t!==null?t:ao(e.type)||"Memo";case Je:t=e._payload,e=e._init;try{return ao(e(t))}catch{}}return null}function Ef(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ao(t);case 8:return t===ai?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ft(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Us(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Cf(e){var t=Us(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function ar(e){e._valueTracker||(e._valueTracker=Cf(e))}function $s(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Us(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $r(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function co(e,t){var n=t.checked;return H({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function uu(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ft(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Vs(e,t){t=t.checked,t!=null&&si(e,"checked",t,!1)}function fo(e,t){Vs(e,t);var n=ft(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?po(e,t.type,n):t.hasOwnProperty("defaultValue")&&po(e,t.type,ft(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function su(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function po(e,t,n){(t!=="number"||$r(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var En=Array.isArray;function Wt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=cr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Dn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Nn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},_f=["Webkit","ms","Moz","O"];Object.keys(Nn).forEach(function(e){_f.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Nn[t]=Nn[e]})});function Ws(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Nn.hasOwnProperty(e)&&Nn[e]?(""+t).trim():t+"px"}function Ks(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Ws(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Nf=H({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function vo(e,t){if(t){if(Nf[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(k(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(k(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(k(61))}if(t.style!=null&&typeof t.style!="object")throw Error(k(62))}}function yo(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var go=null;function di(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var wo=null,Kt=null,Yt=null;function fu(e){if(e=rr(e)){if(typeof wo!="function")throw Error(k(280));var t=e.stateNode;t&&(t=vl(t),wo(e.stateNode,e.type,t))}}function Ys(e){Kt?Yt?Yt.push(e):Yt=[e]:Kt=e}function Gs(){if(Kt){var e=Kt,t=Yt;if(Yt=Kt=null,fu(e),t)for(e=0;e>>=0,e===0?32:31-(Df(e)/Af|0)|0}var fr=64,dr=4194304;function Cn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Qr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=Cn(u):(o&=i,o!==0&&(r=Cn(o)))}else i=n&~l,i!==0?r=Cn(i):o!==0&&(r=Cn(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function tr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Ie(t),e[t]=n}function Bf(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Tn),ku=" ",Su=!1;function ha(e,t){switch(e){case"keyup":return vd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ma(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ot=!1;function gd(e,t){switch(e){case"compositionend":return ma(t);case"keypress":return t.which!==32?null:(Su=!0,ku);case"textInput":return e=t.data,e===ku&&Su?null:e;default:return null}}function wd(e,t){if(Ot)return e==="compositionend"||!ki&&ha(e,t)?(e=da(),Tr=yi=tt=null,Ot=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=_u(n)}}function wa(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?wa(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function ka(){for(var e=window,t=$r();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$r(e.document)}return t}function Si(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Td(e){var t=ka(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&wa(n.ownerDocument.documentElement,n)){if(r!==null&&Si(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=Nu(n,o);var i=Nu(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Ft=null,_o=null,zn=null,No=!1;function Pu(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;No||Ft==null||Ft!==$r(r)||(r=Ft,"selectionStart"in r&&Si(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),zn&&Hn(zn,r)||(zn=r,r=Yr(_o,"onSelect"),0Ut||(e.current=Io[Ut],Io[Ut]=null,Ut--)}function D(e,t){Ut++,Io[Ut]=e.current,e.current=t}var dt={},oe=ht(dt),de=ht(!1),_t=dt;function qt(e,t){var n=e.type.contextTypes;if(!n)return dt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function pe(e){return e=e.childContextTypes,e!=null}function Xr(){U(de),U(oe)}function Ru(e,t,n){if(oe.current!==dt)throw Error(k(168));D(oe,t),D(de,n)}function La(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(k(108,Ef(e)||"Unknown",l));return H({},n,r)}function Zr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||dt,_t=oe.current,D(oe,e),D(de,de.current),!0}function Ou(e,t,n){var r=e.stateNode;if(!r)throw Error(k(169));n?(e=La(e,t,_t),r.__reactInternalMemoizedMergedChildContext=e,U(de),U(oe),D(oe,e)):U(de),D(de,n)}var Ve=null,yl=!1,Gl=!1;function za(e){Ve===null?Ve=[e]:Ve.push(e)}function $d(e){yl=!0,za(e)}function mt(){if(!Gl&&Ve!==null){Gl=!0;var e=0,t=R;try{var n=Ve;for(R=1;e>=i,l-=i,Be=1<<32-Ie(t)+l|n<P?(O=x,x=null):O=x.sibling;var L=h(f,x,d[P],g);if(L===null){x===null&&(x=O);break}e&&x&&L.alternate===null&&t(f,x),c=o(L,c,P),S===null?E=L:S.sibling=L,S=L,x=O}if(P===d.length)return n(f,x),$&>(f,P),E;if(x===null){for(;PP?(O=x,x=null):O=x.sibling;var Pe=h(f,x,L.value,g);if(Pe===null){x===null&&(x=O);break}e&&x&&Pe.alternate===null&&t(f,x),c=o(Pe,c,P),S===null?E=Pe:S.sibling=Pe,S=Pe,x=O}if(L.done)return n(f,x),$&>(f,P),E;if(x===null){for(;!L.done;P++,L=d.next())L=m(f,L.value,g),L!==null&&(c=o(L,c,P),S===null?E=L:S.sibling=L,S=L);return $&>(f,P),E}for(x=r(f,x);!L.done;P++,L=d.next())L=v(x,f,P,L.value,g),L!==null&&(e&&L.alternate!==null&&x.delete(L.key===null?P:L.key),c=o(L,c,P),S===null?E=L:S.sibling=L,S=L);return e&&x.forEach(function(cn){return t(f,cn)}),$&>(f,P),E}function C(f,c,d,g){if(typeof d=="object"&&d!==null&&d.type===Rt&&d.key===null&&(d=d.props.children),typeof d=="object"&&d!==null){switch(d.$$typeof){case sr:e:{for(var E=d.key,S=c;S!==null;){if(S.key===E){if(E=d.type,E===Rt){if(S.tag===7){n(f,S.sibling),c=l(S,d.props.children),c.return=f,f=c;break e}}else if(S.elementType===E||typeof E=="object"&&E!==null&&E.$$typeof===Je&&Au(E)===S.type){n(f,S.sibling),c=l(S,d.props),c.ref=yn(f,S,d),c.return=f,f=c;break e}n(f,S);break}else t(f,S);S=S.sibling}d.type===Rt?(c=Ct(d.props.children,f.mode,g,d.key),c.return=f,f=c):(g=Fr(d.type,d.key,d.props,null,f.mode,g),g.ref=yn(f,c,d),g.return=f,f=g)}return i(f);case Mt:e:{for(S=d.key;c!==null;){if(c.key===S)if(c.tag===4&&c.stateNode.containerInfo===d.containerInfo&&c.stateNode.implementation===d.implementation){n(f,c.sibling),c=l(c,d.children||[]),c.return=f,f=c;break e}else{n(f,c);break}else t(f,c);c=c.sibling}c=no(d,f.mode,g),c.return=f,f=c}return i(f);case Je:return S=d._init,C(f,c,S(d._payload),g)}if(En(d))return w(f,c,d,g);if(dn(d))return y(f,c,d,g);wr(f,d)}return typeof d=="string"&&d!==""||typeof d=="number"?(d=""+d,c!==null&&c.tag===6?(n(f,c.sibling),c=l(c,d),c.return=f,f=c):(n(f,c),c=to(d,f.mode,g),c.return=f,f=c),i(f)):n(f,c)}return C}var en=Ra(!0),Oa=Ra(!1),br=ht(null),el=null,Bt=null,_i=null;function Ni(){_i=Bt=el=null}function Pi(e){var t=br.current;U(br),e._currentValue=t}function Oo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Xt(e,t){el=e,_i=Bt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(fe=!0),e.firstContext=null)}function _e(e){var t=e._currentValue;if(_i!==e)if(e={context:e,memoizedValue:t,next:null},Bt===null){if(el===null)throw Error(k(308));Bt=e,el.dependencies={lanes:0,firstContext:e}}else Bt=Bt.next=e;return t}var St=null;function Ti(e){St===null?St=[e]:St.push(e)}function Fa(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Ti(t)):(n.next=l.next,l.next=n),t.interleaved=n,Ye(e,r)}function Ye(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var qe=!1;function Li(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Da(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Qe(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function ut(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,M&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Ye(e,n)}return l=r.interleaved,l===null?(t.next=t,Ti(r)):(t.next=l.next,l.next=t),r.interleaved=t,Ye(e,n)}function zr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,hi(e,n)}}function Uu(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function tl(e,t,n,r){var l=e.updateQueue;qe=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var s=u,a=s.next;s.next=null,i===null?o=a:i.next=a,i=s;var p=e.alternate;p!==null&&(p=p.updateQueue,u=p.lastBaseUpdate,u!==i&&(u===null?p.firstBaseUpdate=a:u.next=a,p.lastBaseUpdate=s))}if(o!==null){var m=l.baseState;i=0,p=a=s=null,u=o;do{var h=u.lane,v=u.eventTime;if((r&h)===h){p!==null&&(p=p.next={eventTime:v,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var w=e,y=u;switch(h=t,v=n,y.tag){case 1:if(w=y.payload,typeof w=="function"){m=w.call(v,m,h);break e}m=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=y.payload,h=typeof w=="function"?w.call(v,m,h):w,h==null)break e;m=H({},m,h);break e;case 2:qe=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,h=l.effects,h===null?l.effects=[u]:h.push(u))}else v={eventTime:v,lane:h,tag:u.tag,payload:u.payload,callback:u.callback,next:null},p===null?(a=p=v,s=m):p=p.next=v,i|=h;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;h=u,u=h.next,h.next=null,l.lastBaseUpdate=h,l.shared.pending=null}}while(!0);if(p===null&&(s=m),l.baseState=s,l.firstBaseUpdate=a,l.lastBaseUpdate=p,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);Tt|=i,e.lanes=i,e.memoizedState=m}}function $u(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Zl.transition;Zl.transition={};try{e(!1),t()}finally{R=n,Zl.transition=r}}function ec(){return Ne().memoizedState}function Qd(e,t,n){var r=at(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},tc(e))nc(t,n);else if(n=Fa(e,t,n,r),n!==null){var l=ue();Me(n,e,r,l),rc(n,t,r)}}function Wd(e,t,n){var r=at(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(tc(e))nc(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,Re(u,i)){var s=t.interleaved;s===null?(l.next=l,Ti(t)):(l.next=s.next,s.next=l),t.interleaved=l;return}}catch{}finally{}n=Fa(e,t,l,r),n!==null&&(l=ue(),Me(n,e,r,l),rc(n,t,r))}}function tc(e){var t=e.alternate;return e===B||t!==null&&t===B}function nc(e,t){jn=rl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function rc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,hi(e,n)}}var ll={readContext:_e,useCallback:ne,useContext:ne,useEffect:ne,useImperativeHandle:ne,useInsertionEffect:ne,useLayoutEffect:ne,useMemo:ne,useReducer:ne,useRef:ne,useState:ne,useDebugValue:ne,useDeferredValue:ne,useTransition:ne,useMutableSource:ne,useSyncExternalStore:ne,useId:ne,unstable_isNewReconciler:!1},Kd={readContext:_e,useCallback:function(e,t){return Fe().memoizedState=[e,t===void 0?null:t],e},useContext:_e,useEffect:Bu,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ir(4194308,4,Xa.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ir(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ir(4,2,e,t)},useMemo:function(e,t){var n=Fe();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Fe();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Qd.bind(null,B,e),[r.memoizedState,e]},useRef:function(e){var t=Fe();return e={current:e},t.memoizedState=e},useState:Vu,useDebugValue:Di,useDeferredValue:function(e){return Fe().memoizedState=e},useTransition:function(){var e=Vu(!1),t=e[0];return e=Hd.bind(null,e[1]),Fe().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=B,l=Fe();if($){if(n===void 0)throw Error(k(407));n=n()}else{if(n=t(),q===null)throw Error(k(349));Pt&30||Va(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,Bu(Ha.bind(null,r,o,e),[e]),r.flags|=2048,Jn(9,Ba.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Fe(),t=q.identifierPrefix;if($){var n=He,r=Be;n=(r&~(1<<32-Ie(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Xn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[De]=t,e[Kn]=r,pc(e,t,!1,!1),t.stateNode=e;e:{switch(i=yo(n,r),n){case"dialog":A("cancel",e),A("close",e),l=r;break;case"iframe":case"object":case"embed":A("load",e),l=r;break;case"video":case"audio":for(l=0;l<_n.length;l++)A(_n[l],e);l=r;break;case"source":A("error",e),l=r;break;case"img":case"image":case"link":A("error",e),A("load",e),l=r;break;case"details":A("toggle",e),l=r;break;case"input":uu(e,r),l=co(e,r),A("invalid",e);break;case"option":l=r;break;case"select":e._wrapperState={wasMultiple:!!r.multiple},l=H({},r,{value:void 0}),A("invalid",e);break;case"textarea":au(e,r),l=ho(e,r),A("invalid",e);break;default:l=r}vo(n,l),u=l;for(o in u)if(u.hasOwnProperty(o)){var s=u[o];o==="style"?Ks(e,s):o==="dangerouslySetInnerHTML"?(s=s?s.__html:void 0,s!=null&&Qs(e,s)):o==="children"?typeof s=="string"?(n!=="textarea"||s!=="")&&Dn(e,s):typeof s=="number"&&Dn(e,""+s):o!=="suppressContentEditableWarning"&&o!=="suppressHydrationWarning"&&o!=="autoFocus"&&(Fn.hasOwnProperty(o)?s!=null&&o==="onScroll"&&A("scroll",e):s!=null&&si(e,o,s,i))}switch(n){case"input":ar(e),su(e,r,!1);break;case"textarea":ar(e),cu(e);break;case"option":r.value!=null&&e.setAttribute("value",""+ft(r.value));break;case"select":e.multiple=!!r.multiple,o=r.value,o!=null?Wt(e,!!r.multiple,o,!1):r.defaultValue!=null&&Wt(e,!!r.multiple,r.defaultValue,!0);break;default:typeof l.onClick=="function"&&(e.onclick=Gr)}switch(n){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}}r&&(t.flags|=4)}t.ref!==null&&(t.flags|=512,t.flags|=2097152)}return re(t),null;case 6:if(e&&t.stateNode!=null)mc(e,t,e.memoizedProps,r);else{if(typeof r!="string"&&t.stateNode===null)throw Error(k(166));if(n=xt(Gn.current),xt(Ue.current),gr(t)){if(r=t.stateNode,n=t.memoizedProps,r[De]=t,(o=r.nodeValue!==n)&&(e=ye,e!==null))switch(e.tag){case 3:yr(r.nodeValue,n,(e.mode&1)!==0);break;case 5:e.memoizedProps.suppressHydrationWarning!==!0&&yr(r.nodeValue,n,(e.mode&1)!==0)}o&&(t.flags|=4)}else r=(n.nodeType===9?n:n.ownerDocument).createTextNode(r),r[De]=t,t.stateNode=r}return re(t),null;case 13:if(U(V),r=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if($&&ve!==null&&t.mode&1&&!(t.flags&128))Ma(),bt(),t.flags|=98560,o=!1;else if(o=gr(t),r!==null&&r.dehydrated!==null){if(e===null){if(!o)throw Error(k(318));if(o=t.memoizedState,o=o!==null?o.dehydrated:null,!o)throw Error(k(317));o[De]=t}else bt(),!(t.flags&128)&&(t.memoizedState=null),t.flags|=4;re(t),o=!1}else je!==null&&(Jo(je),je=null),o=!0;if(!o)return t.flags&65536?t:null}return t.flags&128?(t.lanes=n,t):(r=r!==null,r!==(e!==null&&e.memoizedState!==null)&&r&&(t.child.flags|=8192,t.mode&1&&(e===null||V.current&1?X===0&&(X=3):Qi())),t.updateQueue!==null&&(t.flags|=4),re(t),null);case 4:return tn(),Ho(e,t),e===null&&Qn(t.stateNode.containerInfo),re(t),null;case 10:return Pi(t.type._context),re(t),null;case 17:return pe(t.type)&&Xr(),re(t),null;case 19:if(U(V),o=t.memoizedState,o===null)return re(t),null;if(r=(t.flags&128)!==0,i=o.rendering,i===null)if(r)gn(o,!1);else{if(X!==0||e!==null&&e.flags&128)for(e=t.child;e!==null;){if(i=nl(e),i!==null){for(t.flags|=128,gn(o,!1),r=i.updateQueue,r!==null&&(t.updateQueue=r,t.flags|=4),t.subtreeFlags=0,r=n,n=t.child;n!==null;)o=n,e=r,o.flags&=14680066,i=o.alternate,i===null?(o.childLanes=0,o.lanes=e,o.child=null,o.subtreeFlags=0,o.memoizedProps=null,o.memoizedState=null,o.updateQueue=null,o.dependencies=null,o.stateNode=null):(o.childLanes=i.childLanes,o.lanes=i.lanes,o.child=i.child,o.subtreeFlags=0,o.deletions=null,o.memoizedProps=i.memoizedProps,o.memoizedState=i.memoizedState,o.updateQueue=i.updateQueue,o.type=i.type,e=i.dependencies,o.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),n=n.sibling;return D(V,V.current&1|2),t.child}e=e.sibling}o.tail!==null&&K()>rn&&(t.flags|=128,r=!0,gn(o,!1),t.lanes=4194304)}else{if(!r)if(e=nl(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),gn(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!$)return re(t),null}else 2*K()-o.renderingStartTime>rn&&n!==1073741824&&(t.flags|=128,r=!0,gn(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=K(),t.sibling=null,n=V.current,D(V,r?n&1|2:n&1),t):(re(t),null);case 22:case 23:return Hi(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?me&1073741824&&(re(t),t.subtreeFlags&6&&(t.flags|=8192)):re(t),null;case 24:return null;case 25:return null}throw Error(k(156,t.tag))}function ep(e,t){switch(Ei(t),t.tag){case 1:return pe(t.type)&&Xr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return tn(),U(de),U(oe),Ii(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return ji(t),null;case 13:if(U(V),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(k(340));bt()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return U(V),null;case 4:return tn(),null;case 10:return Pi(t.type._context),null;case 22:case 23:return Hi(),null;case 24:return null;default:return null}}var Sr=!1,le=!1,tp=typeof WeakSet=="function"?WeakSet:Set,_=null;function Ht(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Q(e,t,r)}else n.current=null}function Qo(e,t,n){try{n()}catch(r){Q(e,t,r)}}var bu=!1;function np(e,t){if(Po=Wr,e=ka(),Si(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,s=-1,a=0,p=0,m=e,h=null;t:for(;;){for(var v;m!==n||l!==0&&m.nodeType!==3||(u=i+l),m!==o||r!==0&&m.nodeType!==3||(s=i+r),m.nodeType===3&&(i+=m.nodeValue.length),(v=m.firstChild)!==null;)h=m,m=v;for(;;){if(m===e)break t;if(h===n&&++a===l&&(u=i),h===o&&++p===r&&(s=i),(v=m.nextSibling)!==null)break;m=h,h=m.parentNode}m=v}n=u===-1||s===-1?null:{start:u,end:s}}else n=null}n=n||{start:0,end:0}}else n=null;for(To={focusedElem:e,selectionRange:n},Wr=!1,_=t;_!==null;)if(t=_,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,_=e;else for(;_!==null;){t=_;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var y=w.memoizedProps,C=w.memoizedState,f=t.stateNode,c=f.getSnapshotBeforeUpdate(t.elementType===t.type?y:Le(t.type,y),C);f.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var d=t.stateNode.containerInfo;d.nodeType===1?d.textContent="":d.nodeType===9&&d.documentElement&&d.removeChild(d.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(k(163))}}catch(g){Q(t,t.return,g)}if(e=t.sibling,e!==null){e.return=t.return,_=e;break}_=t.return}return w=bu,bu=!1,w}function In(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&Qo(t,n,o)}l=l.next}while(l!==r)}}function kl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Wo(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function vc(e){var t=e.alternate;t!==null&&(e.alternate=null,vc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[De],delete t[Kn],delete t[jo],delete t[Ad],delete t[Ud])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function yc(e){return e.tag===5||e.tag===3||e.tag===4}function es(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||yc(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ko(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Gr));else if(r!==4&&(e=e.child,e!==null))for(Ko(e,t,n),e=e.sibling;e!==null;)Ko(e,t,n),e=e.sibling}function Yo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Yo(e,t,n),e=e.sibling;e!==null;)Yo(e,t,n),e=e.sibling}var b=null,ze=!1;function Ze(e,t,n){for(n=n.child;n!==null;)gc(e,t,n),n=n.sibling}function gc(e,t,n){if(Ae&&typeof Ae.onCommitFiberUnmount=="function")try{Ae.onCommitFiberUnmount(dl,n)}catch{}switch(n.tag){case 5:le||Ht(n,t);case 6:var r=b,l=ze;b=null,Ze(e,t,n),b=r,ze=l,b!==null&&(ze?(e=b,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):b.removeChild(n.stateNode));break;case 18:b!==null&&(ze?(e=b,n=n.stateNode,e.nodeType===8?Yl(e.parentNode,n):e.nodeType===1&&Yl(e,n),Vn(e)):Yl(b,n.stateNode));break;case 4:r=b,l=ze,b=n.stateNode.containerInfo,ze=!0,Ze(e,t,n),b=r,ze=l;break;case 0:case 11:case 14:case 15:if(!le&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&(o&2||o&4)&&Qo(n,t,i),l=l.next}while(l!==r)}Ze(e,t,n);break;case 1:if(!le&&(Ht(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){Q(n,t,u)}Ze(e,t,n);break;case 21:Ze(e,t,n);break;case 22:n.mode&1?(le=(r=le)||n.memoizedState!==null,Ze(e,t,n),le=r):Ze(e,t,n);break;default:Ze(e,t,n)}}function ts(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new tp),t.forEach(function(r){var l=fp.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Te(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=K()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*lp(r/1960))-r,10e?16:e,nt===null)var r=!1;else{if(e=nt,nt=null,ul=0,M&6)throw Error(k(331));var l=M;for(M|=4,_=e.current;_!==null;){var o=_,i=o.child;if(_.flags&16){var u=o.deletions;if(u!==null){for(var s=0;sK()-Vi?Et(e,0):$i|=n),he(e,t)}function Nc(e,t){t===0&&(e.mode&1?(t=dr,dr<<=1,!(dr&130023424)&&(dr=4194304)):t=1);var n=ue();e=Ye(e,t),e!==null&&(tr(e,t,n),he(e,n))}function cp(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Nc(e,n)}function fp(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(k(314))}r!==null&&r.delete(t),Nc(e,n)}var Pc;Pc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||de.current)fe=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return fe=!1,qd(e,t,n);fe=!!(e.flags&131072)}else fe=!1,$&&t.flags&1048576&&ja(t,qr,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Mr(e,t),e=t.pendingProps;var l=qt(t,oe.current);Xt(t,n),l=Ri(null,t,r,e,l,n);var o=Oi();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,pe(r)?(o=!0,Zr(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,Li(t),l.updater=wl,t.stateNode=l,l._reactInternals=t,Do(t,r,e,n),t=$o(null,t,r,!0,o,n)):(t.tag=0,$&&o&&xi(t),ie(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Mr(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=pp(r),e=Le(r,e),l){case 0:t=Uo(null,t,r,e,n);break e;case 1:t=Zu(null,t,r,e,n);break e;case 11:t=Gu(null,t,r,e,n);break e;case 14:t=Xu(null,t,r,Le(r.type,e),n);break e}throw Error(k(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),Uo(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),Zu(e,t,r,l,n);case 3:e:{if(cc(t),e===null)throw Error(k(387));r=t.pendingProps,o=t.memoizedState,l=o.element,Da(e,t),tl(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=nn(Error(k(423)),t),t=Ju(e,t,r,n,l);break e}else if(r!==l){l=nn(Error(k(424)),t),t=Ju(e,t,r,n,l);break e}else for(ve=it(t.stateNode.containerInfo.firstChild),ye=t,$=!0,je=null,n=Oa(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(bt(),r===l){t=Ge(e,t,n);break e}ie(e,t,r,n)}t=t.child}return t;case 5:return Aa(t),e===null&&Ro(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Lo(r,l)?i=null:o!==null&&Lo(r,o)&&(t.flags|=32),ac(e,t),ie(e,t,i,n),t.child;case 6:return e===null&&Ro(t),null;case 13:return fc(e,t,n);case 4:return zi(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=en(t,null,r,n):ie(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),Gu(e,t,r,l,n);case 7:return ie(e,t,t.pendingProps,n),t.child;case 8:return ie(e,t,t.pendingProps.children,n),t.child;case 12:return ie(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,D(br,r._currentValue),r._currentValue=i,o!==null)if(Re(o.value,i)){if(o.children===l.children&&!de.current){t=Ge(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var s=u.firstContext;s!==null;){if(s.context===r){if(o.tag===1){s=Qe(-1,n&-n),s.tag=2;var a=o.updateQueue;if(a!==null){a=a.shared;var p=a.pending;p===null?s.next=s:(s.next=p.next,p.next=s),a.pending=s}}o.lanes|=n,s=o.alternate,s!==null&&(s.lanes|=n),Oo(o.return,n,t),u.lanes|=n;break}s=s.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(k(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),Oo(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}ie(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Xt(t,n),l=_e(l),r=r(l),t.flags|=1,ie(e,t,r,n),t.child;case 14:return r=t.type,l=Le(r,t.pendingProps),l=Le(r.type,l),Xu(e,t,r,l,n);case 15:return uc(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:Le(r,l),Mr(e,t),t.tag=1,pe(r)?(e=!0,Zr(t)):e=!1,Xt(t,n),lc(t,r,l),Do(t,r,l,n),$o(null,t,r,!0,e,n);case 19:return dc(e,t,n);case 22:return sc(e,t,n)}throw Error(k(156,t.tag))};function Tc(e,t){return ta(e,t)}function dp(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ee(e,t,n,r){return new dp(e,t,n,r)}function Wi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function pp(e){if(typeof e=="function")return Wi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ci)return 11;if(e===fi)return 14}return 2}function ct(e,t){var n=e.alternate;return n===null?(n=Ee(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Fr(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")Wi(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Rt:return Ct(n.children,l,o,t);case ai:i=8,l|=8;break;case io:return e=Ee(12,n,t,l|2),e.elementType=io,e.lanes=o,e;case uo:return e=Ee(13,n,t,l),e.elementType=uo,e.lanes=o,e;case so:return e=Ee(19,n,t,l),e.elementType=so,e.lanes=o,e;case As:return xl(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Fs:i=10;break e;case Ds:i=9;break e;case ci:i=11;break e;case fi:i=14;break e;case Je:i=16,r=null;break e}throw Error(k(130,e==null?e:typeof e,""))}return t=Ee(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function Ct(e,t,n,r){return e=Ee(7,e,r,t),e.lanes=n,e}function xl(e,t,n,r){return e=Ee(22,e,r,t),e.elementType=As,e.lanes=n,e.stateNode={isHidden:!1},e}function to(e,t,n){return e=Ee(6,e,null,t),e.lanes=n,e}function no(e,t,n){return t=Ee(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function hp(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Fl(0),this.expirationTimes=Fl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Fl(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Ki(e,t,n,r,l,o,i,u,s){return e=new hp(e,t,n,u,s),t===1?(t=1,o===!0&&(t|=8)):t=0,o=Ee(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Li(o),e}function mp(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Ic)}catch(e){console.error(e)}}Ic(),Is.exports=we;var kp=Is.exports,Mc,as=kp;Mc=as.createRoot,as.hydrateRoot;/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var Sp={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const xp=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase().trim(),an=(e,t)=>{const n=F.forwardRef(({color:r="currentColor",size:l=24,strokeWidth:o=2,absoluteStrokeWidth:i,className:u="",children:s,...a},p)=>F.createElement("svg",{ref:p,...Sp,width:l,height:l,stroke:r,strokeWidth:i?Number(o)*24/Number(l):o,className:["lucide",`lucide-${xp(e)}`,u].join(" "),...a},[...t.map(([m,h])=>F.createElement(m,h)),...Array.isArray(s)?s:[s]]));return n.displayName=`${e}`,n};/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ep=an("Briefcase",[["rect",{width:"20",height:"14",x:"2",y:"7",rx:"2",ry:"2",key:"eto64e"}],["path",{d:"M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16",key:"zwj3tp"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Cp=an("Cake",[["path",{d:"M20 21v-8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8",key:"1w3rig"}],["path",{d:"M4 16s.5-1 2-1 2.5 2 4 2 2.5-2 4-2 2.5 2 4 2 2-1 2-1",key:"n2jgmb"}],["path",{d:"M2 21h20",key:"1nyx9w"}],["path",{d:"M7 8v3",key:"1qtyvj"}],["path",{d:"M12 8v3",key:"hwp4zt"}],["path",{d:"M17 8v3",key:"1i6e5u"}],["path",{d:"M7 4h0.01",key:"hsw7lv"}],["path",{d:"M12 4h0.01",key:"1e3d8f"}],["path",{d:"M17 4h0.01",key:"p7cxgy"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const _p=an("Info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Np=an("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Pp=an("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);/** + * @license lucide-react v0.344.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Tp=an("UserRound",[["circle",{cx:"12",cy:"8",r:"5",key:"1hypcn"}],["path",{d:"M20 21a8 8 0 0 0-16 0",key:"rfgkzh"}]]),Rc=Object.freeze({left:0,top:0,width:16,height:16}),cl=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),Zi=Object.freeze({...Rc,...cl}),qo=Object.freeze({...Zi,body:"",hidden:!1});function Lp(e,t){const n={};!e.hFlip!=!t.hFlip&&(n.hFlip=!0),!e.vFlip!=!t.vFlip&&(n.vFlip=!0);const r=((e.rotate||0)+(t.rotate||0))%4;return r&&(n.rotate=r),n}function cs(e,t){const n=Lp(e,t);for(const r in qo)r in cl?r in e&&!(r in n)&&(n[r]=cl[r]):r in t?n[r]=t[r]:r in e&&(n[r]=e[r]);return n}function zp(e,t){const n=e.icons,r=e.aliases||Object.create(null),l=Object.create(null);function o(i){if(n[i])return l[i]=[];if(!(i in l)){l[i]=null;const u=r[i]&&r[i].parent,s=u&&o(u);s&&(l[i]=[u].concat(s))}return l[i]}return Object.keys(n).concat(Object.keys(r)).forEach(o),l}function jp(e,t,n){const r=e.icons,l=e.aliases||Object.create(null);let o={};function i(u){o=cs(r[u]||l[u],o)}return i(t),n.forEach(i),cs(e,o)}function Oc(e,t){const n=[];if(typeof e!="object"||typeof e.icons!="object")return n;e.not_found instanceof Array&&e.not_found.forEach(l=>{t(l,null),n.push(l)});const r=zp(e);for(const l in r){const o=r[l];o&&(t(l,jp(e,l,o)),n.push(l))}return n}const Ip={provider:"",aliases:{},not_found:{},...Rc};function ro(e,t){for(const n in t)if(n in e&&typeof e[n]!=typeof t[n])return!1;return!0}function Fc(e){if(typeof e!="object"||e===null)return null;const t=e;if(typeof t.prefix!="string"||!e.icons||typeof e.icons!="object"||!ro(e,Ip))return null;const n=t.icons;for(const l in n){const o=n[l];if(!l||typeof o.body!="string"||!ro(o,qo))return null}const r=t.aliases||Object.create(null);for(const l in r){const o=r[l],i=o.parent;if(!l||typeof i!="string"||!n[i]&&!r[i]||!ro(o,qo))return null}return t}const Dc=/^[a-z0-9]+(-[a-z0-9]+)*$/,Pl=(e,t,n,r="")=>{const l=e.split(":");if(e.slice(0,1)==="@"){if(l.length<2||l.length>3)return null;r=l.shift().slice(1)}if(l.length>3||!l.length)return null;if(l.length>1){const u=l.pop(),s=l.pop(),a={provider:l.length>0?l[0]:r,prefix:s,name:u};return t&&!Dr(a)?null:a}const o=l[0],i=o.split("-");if(i.length>1){const u={provider:r,prefix:i.shift(),name:i.join("-")};return t&&!Dr(u)?null:u}if(n&&r===""){const u={provider:r,prefix:"",name:o};return t&&!Dr(u,n)?null:u}return null},Dr=(e,t)=>e?!!((t&&e.prefix===""||e.prefix)&&e.name):!1,fs=Object.create(null);function Mp(e,t){return{provider:e,prefix:t,icons:Object.create(null),missing:new Set}}function ln(e,t){const n=fs[e]||(fs[e]=Object.create(null));return n[t]||(n[t]=Mp(e,t))}function Ac(e,t){return Fc(t)?Oc(t,(n,r)=>{r?e.icons[n]=r:e.missing.add(n)}):[]}function Rp(e,t,n){try{if(typeof n.body=="string")return e.icons[t]={...n},!0}catch{}return!1}let bn=!1;function Uc(e){return typeof e=="boolean"&&(bn=e),bn}function ds(e){const t=typeof e=="string"?Pl(e,!0,bn):e;if(t){const n=ln(t.provider,t.prefix),r=t.name;return n.icons[r]||(n.missing.has(r)?null:void 0)}}function Op(e,t){const n=Pl(e,!0,bn);if(!n)return!1;const r=ln(n.provider,n.prefix);return t?Rp(r,n.name,t):(r.missing.add(n.name),!0)}function Fp(e,t){if(typeof e!="object")return!1;if(typeof t!="string"&&(t=e.provider||""),bn&&!t&&!e.prefix){let l=!1;return Fc(e)&&(e.prefix="",Oc(e,(o,i)=>{Op(o,i)&&(l=!0)})),l}const n=e.prefix;if(!Dr({prefix:n,name:"a"}))return!1;const r=ln(t,n);return!!Ac(r,e)}const $c=Object.freeze({width:null,height:null}),Vc=Object.freeze({...$c,...cl}),Dp=/(-?[0-9.]*[0-9]+[0-9.]*)/g,Ap=/^-?[0-9.]*[0-9]+[0-9.]*$/g;function ps(e,t,n){if(t===1)return e;if(n=n||100,typeof e=="number")return Math.ceil(e*t*n)/n;if(typeof e!="string")return e;const r=e.split(Dp);if(r===null||!r.length)return e;const l=[];let o=r.shift(),i=Ap.test(o);for(;;){if(i){const u=parseFloat(o);isNaN(u)?l.push(o):l.push(Math.ceil(u*t*n)/n)}else l.push(o);if(o=r.shift(),o===void 0)return l.join("");i=!i}}function Up(e,t="defs"){let n="";const r=e.indexOf("<"+t);for(;r>=0;){const l=e.indexOf(">",r),o=e.indexOf("",o);if(i===-1)break;n+=e.slice(l+1,o).trim(),e=e.slice(0,r).trim()+e.slice(i+1)}return{defs:n,content:e}}function $p(e,t){return e?""+e+""+t:t}function Vp(e,t,n){const r=Up(e);return $p(r.defs,t+r.content+n)}const Bp=e=>e==="unset"||e==="undefined"||e==="none";function Hp(e,t){const n={...Zi,...e},r={...Vc,...t},l={left:n.left,top:n.top,width:n.width,height:n.height};let o=n.body;[n,r].forEach(y=>{const C=[],f=y.hFlip,c=y.vFlip;let d=y.rotate;f?c?d+=2:(C.push("translate("+(l.width+l.left).toString()+" "+(0-l.top).toString()+")"),C.push("scale(-1 1)"),l.top=l.left=0):c&&(C.push("translate("+(0-l.left).toString()+" "+(l.height+l.top).toString()+")"),C.push("scale(1 -1)"),l.top=l.left=0);let g;switch(d<0&&(d-=Math.floor(d/4)*4),d=d%4,d){case 1:g=l.height/2+l.top,C.unshift("rotate(90 "+g.toString()+" "+g.toString()+")");break;case 2:C.unshift("rotate(180 "+(l.width/2+l.left).toString()+" "+(l.height/2+l.top).toString()+")");break;case 3:g=l.width/2+l.left,C.unshift("rotate(-90 "+g.toString()+" "+g.toString()+")");break}d%2===1&&(l.left!==l.top&&(g=l.left,l.left=l.top,l.top=g),l.width!==l.height&&(g=l.width,l.width=l.height,l.height=g)),C.length&&(o=Vp(o,'',""))});const i=r.width,u=r.height,s=l.width,a=l.height;let p,m;i===null?(m=u===null?"1em":u==="auto"?a:u,p=ps(m,s/a)):(p=i==="auto"?s:i,m=u===null?ps(p,a/s):u==="auto"?a:u);const h={},v=(y,C)=>{Bp(C)||(h[y]=C.toString())};v("width",p),v("height",m);const w=[l.left,l.top,s,a];return h.viewBox=w.join(" "),{attributes:h,viewBox:w,body:o}}const Qp=/\sid="(\S+)"/g,Wp="IconifyId"+Date.now().toString(16)+(Math.random()*16777216|0).toString(16);let Kp=0;function Yp(e,t=Wp){const n=[];let r;for(;r=Qp.exec(e);)n.push(r[1]);if(!n.length)return e;const l="suffix"+(Math.random()*16777216|Date.now()).toString(16);return n.forEach(o=>{const i=typeof t=="function"?t(o):t+(Kp++).toString(),u=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp('([#;"])('+u+')([")]|\\.[a-z])',"g"),"$1"+i+l+"$3")}),e=e.replace(new RegExp(l,"g"),""),e}const bo=Object.create(null);function Gp(e,t){bo[e]=t}function ei(e){return bo[e]||bo[""]}function Ji(e){let t;if(typeof e.resources=="string")t=[e.resources];else if(t=e.resources,!(t instanceof Array)||!t.length)return null;return{resources:t,path:e.path||"/",maxURL:e.maxURL||500,rotate:e.rotate||750,timeout:e.timeout||5e3,random:e.random===!0,index:e.index||0,dataAfterTimeout:e.dataAfterTimeout!==!1}}const qi=Object.create(null),kn=["https://api.simplesvg.com","https://api.unisvg.com"],Ar=[];for(;kn.length>0;)kn.length===1||Math.random()>.5?Ar.push(kn.shift()):Ar.push(kn.pop());qi[""]=Ji({resources:["https://api.iconify.design"].concat(Ar)});function Xp(e,t){const n=Ji(t);return n===null?!1:(qi[e]=n,!0)}function bi(e){return qi[e]}const Zp=()=>{let e;try{if(e=fetch,typeof e=="function")return e}catch{}};let hs=Zp();function Jp(e,t){const n=bi(e);if(!n)return 0;let r;if(!n.maxURL)r=0;else{let l=0;n.resources.forEach(i=>{l=Math.max(l,i.length)});const o=t+".json?icons=";r=n.maxURL-l-n.path.length-o.length}return r}function qp(e){return e===404}const bp=(e,t,n)=>{const r=[],l=Jp(e,t),o="icons";let i={type:o,provider:e,prefix:t,icons:[]},u=0;return n.forEach((s,a)=>{u+=s.length+1,u>=l&&a>0&&(r.push(i),i={type:o,provider:e,prefix:t,icons:[]},u=s.length),i.icons.push(s)}),r.push(i),r};function eh(e){if(typeof e=="string"){const t=bi(e);if(t)return t.path}return"/"}const th=(e,t,n)=>{if(!hs){n("abort",424);return}let r=eh(t.provider);switch(t.type){case"icons":{const o=t.prefix,u=t.icons.join(","),s=new URLSearchParams({icons:u});r+=o+".json?"+s.toString();break}case"custom":{const o=t.uri;r+=o.slice(0,1)==="/"?o.slice(1):o;break}default:n("abort",400);return}let l=503;hs(e+r).then(o=>{const i=o.status;if(i!==200){setTimeout(()=>{n(qp(i)?"abort":"next",i)});return}return l=501,o.json()}).then(o=>{if(typeof o!="object"||o===null){setTimeout(()=>{o===404?n("abort",o):n("next",l)});return}setTimeout(()=>{n("success",o)})}).catch(()=>{n("next",l)})},nh={prepare:bp,send:th};function rh(e){const t={loaded:[],missing:[],pending:[]},n=Object.create(null);e.sort((l,o)=>l.provider!==o.provider?l.provider.localeCompare(o.provider):l.prefix!==o.prefix?l.prefix.localeCompare(o.prefix):l.name.localeCompare(o.name));let r={provider:"",prefix:"",name:""};return e.forEach(l=>{if(r.name===l.name&&r.prefix===l.prefix&&r.provider===l.provider)return;r=l;const o=l.provider,i=l.prefix,u=l.name,s=n[o]||(n[o]=Object.create(null)),a=s[i]||(s[i]=ln(o,i));let p;u in a.icons?p=t.loaded:i===""||a.missing.has(u)?p=t.missing:p=t.pending;const m={provider:o,prefix:i,name:u};p.push(m)}),t}function Bc(e,t){e.forEach(n=>{const r=n.loaderCallbacks;r&&(n.loaderCallbacks=r.filter(l=>l.id!==t))})}function lh(e){e.pendingCallbacksFlag||(e.pendingCallbacksFlag=!0,setTimeout(()=>{e.pendingCallbacksFlag=!1;const t=e.loaderCallbacks?e.loaderCallbacks.slice(0):[];if(!t.length)return;let n=!1;const r=e.provider,l=e.prefix;t.forEach(o=>{const i=o.icons,u=i.pending.length;i.pending=i.pending.filter(s=>{if(s.prefix!==l)return!0;const a=s.name;if(e.icons[a])i.loaded.push({provider:r,prefix:l,name:a});else if(e.missing.has(a))i.missing.push({provider:r,prefix:l,name:a});else return n=!0,!0;return!1}),i.pending.length!==u&&(n||Bc([e],o.id),o.callback(i.loaded.slice(0),i.missing.slice(0),i.pending.slice(0),o.abort))})}))}let oh=0;function ih(e,t,n){const r=oh++,l=Bc.bind(null,n,r);if(!t.pending.length)return l;const o={id:r,icons:t,callback:e,abort:l};return n.forEach(i=>{(i.loaderCallbacks||(i.loaderCallbacks=[])).push(o)}),l}function uh(e,t=!0,n=!1){const r=[];return e.forEach(l=>{const o=typeof l=="string"?Pl(l,t,n):l;o&&r.push(o)}),r}var sh={resources:[],index:0,timeout:2e3,rotate:750,random:!1,dataAfterTimeout:!1};function ah(e,t,n,r){const l=e.resources.length,o=e.random?Math.floor(Math.random()*l):e.index;let i;if(e.random){let S=e.resources.slice(0);for(i=[];S.length>1;){const x=Math.floor(Math.random()*S.length);i.push(S[x]),S=S.slice(0,x).concat(S.slice(x+1))}i=i.concat(S)}else i=e.resources.slice(o).concat(e.resources.slice(0,o));const u=Date.now();let s="pending",a=0,p,m=null,h=[],v=[];typeof r=="function"&&v.push(r);function w(){m&&(clearTimeout(m),m=null)}function y(){s==="pending"&&(s="aborted"),w(),h.forEach(S=>{S.status==="pending"&&(S.status="aborted")}),h=[]}function C(S,x){x&&(v=[]),typeof S=="function"&&v.push(S)}function f(){return{startTime:u,payload:t,status:s,queriesSent:a,queriesPending:h.length,subscribe:C,abort:y}}function c(){s="failed",v.forEach(S=>{S(void 0,p)})}function d(){h.forEach(S=>{S.status==="pending"&&(S.status="aborted")}),h=[]}function g(S,x,P){const O=x!=="success";switch(h=h.filter(L=>L!==S),s){case"pending":break;case"failed":if(O||!e.dataAfterTimeout)return;break;default:return}if(x==="abort"){p=P,c();return}if(O){p=P,h.length||(i.length?E():c());return}if(w(),d(),!e.random){const L=e.resources.indexOf(S.resource);L!==-1&&L!==e.index&&(e.index=L)}s="completed",v.forEach(L=>{L(P)})}function E(){if(s!=="pending")return;w();const S=i.shift();if(S===void 0){if(h.length){m=setTimeout(()=>{w(),s==="pending"&&(d(),c())},e.timeout);return}c();return}const x={status:"pending",resource:S,callback:(P,O)=>{g(x,P,O)}};h.push(x),a++,m=setTimeout(E,e.rotate),n(S,t,x.callback)}return setTimeout(E),f}function Hc(e){const t={...sh,...e};let n=[];function r(){n=n.filter(u=>u().status==="pending")}function l(u,s,a){const p=ah(t,u,s,(m,h)=>{r(),a&&a(m,h)});return n.push(p),p}function o(u){return n.find(s=>u(s))||null}return{query:l,find:o,setIndex:u=>{t.index=u},getIndex:()=>t.index,cleanup:r}}function ms(){}const lo=Object.create(null);function ch(e){if(!lo[e]){const t=bi(e);if(!t)return;const n=Hc(t),r={config:t,redundancy:n};lo[e]=r}return lo[e]}function fh(e,t,n){let r,l;if(typeof e=="string"){const o=ei(e);if(!o)return n(void 0,424),ms;l=o.send;const i=ch(e);i&&(r=i.redundancy)}else{const o=Ji(e);if(o){r=Hc(o);const i=e.resources?e.resources[0]:"",u=ei(i);u&&(l=u.send)}}return!r||!l?(n(void 0,424),ms):r.query(t,l,n)().abort}function vs(){}function dh(e){e.iconsLoaderFlag||(e.iconsLoaderFlag=!0,setTimeout(()=>{e.iconsLoaderFlag=!1,lh(e)}))}function ph(e){const t=[],n=[];return e.forEach(r=>{(r.match(Dc)?t:n).push(r)}),{valid:t,invalid:n}}function Sn(e,t,n){function r(){const l=e.pendingIcons;t.forEach(o=>{l&&l.delete(o),e.icons[o]||e.missing.add(o)})}if(n&&typeof n=="object")try{if(!Ac(e,n).length){r();return}}catch(l){console.error(l)}r(),dh(e)}function ys(e,t){e instanceof Promise?e.then(n=>{t(n)}).catch(()=>{t(null)}):t(e)}function hh(e,t){e.iconsToLoad?e.iconsToLoad=e.iconsToLoad.concat(t).sort():e.iconsToLoad=t,e.iconsQueueFlag||(e.iconsQueueFlag=!0,setTimeout(()=>{e.iconsQueueFlag=!1;const{provider:n,prefix:r}=e,l=e.iconsToLoad;if(delete e.iconsToLoad,!l||!l.length)return;const o=e.loadIcon;if(e.loadIcons&&(l.length>1||!o)){ys(e.loadIcons(l,r,n),p=>{Sn(e,l,p)});return}if(o){l.forEach(p=>{const m=o(p,r,n);ys(m,h=>{const v=h?{prefix:r,icons:{[p]:h}}:null;Sn(e,[p],v)})});return}const{valid:i,invalid:u}=ph(l);if(u.length&&Sn(e,u,null),!i.length)return;const s=r.match(Dc)?ei(n):null;if(!s){Sn(e,i,null);return}s.prepare(n,r,i).forEach(p=>{fh(n,p,m=>{Sn(e,p.icons,m)})})}))}const mh=(e,t)=>{const n=uh(e,!0,Uc()),r=rh(n);if(!r.pending.length){let s=!0;return t&&setTimeout(()=>{s&&t(r.loaded,r.missing,r.pending,vs)}),()=>{s=!1}}const l=Object.create(null),o=[];let i,u;return r.pending.forEach(s=>{const{provider:a,prefix:p}=s;if(p===u&&a===i)return;i=a,u=p,o.push(ln(a,p));const m=l[a]||(l[a]=Object.create(null));m[p]||(m[p]=[])}),r.pending.forEach(s=>{const{provider:a,prefix:p,name:m}=s,h=ln(a,p),v=h.pendingIcons||(h.pendingIcons=new Set);v.has(m)||(v.add(m),l[a][p].push(m))}),o.forEach(s=>{const a=l[s.provider][s.prefix];a.length&&hh(s,a)}),t?ih(t,r,o):vs};function vh(e,t){const n={...e};for(const r in t){const l=t[r],o=typeof l;r in $c?(l===null||l&&(o==="string"||o==="number"))&&(n[r]=l):o===typeof n[r]&&(n[r]=r==="rotate"?l%4:l)}return n}const yh=/[\s,]+/;function gh(e,t){t.split(yh).forEach(n=>{switch(n.trim()){case"horizontal":e.hFlip=!0;break;case"vertical":e.vFlip=!0;break}})}function wh(e,t=0){const n=e.replace(/^-?[0-9.]*/,"");function r(l){for(;l<0;)l+=4;return l%4}if(n===""){const l=parseInt(e);return isNaN(l)?0:r(l)}else if(n!==e){let l=0;switch(n){case"%":l=25;break;case"deg":l=90}if(l){let o=parseFloat(e.slice(0,e.length-n.length));return isNaN(o)?0:(o=o/l,o%1===0?r(o):0)}}return t}function kh(e,t){let n=e.indexOf("xlink:")===-1?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(const r in t)n+=" "+r+'="'+t[r]+'"';return'"+e+""}function Sh(e){return e.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(//g,"%3E").replace(/\s+/g," ")}function xh(e){return"data:image/svg+xml,"+Sh(e)}function Eh(e){return'url("'+xh(e)+'")'}let On;function Ch(){try{On=window.trustedTypes.createPolicy("iconify",{createHTML:e=>e})}catch{On=null}}function _h(e){return On===void 0&&Ch(),On?On.createHTML(e):e}const Qc={...Vc,inline:!1},Nh={xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink","aria-hidden":!0,role:"img"},Ph={display:"inline-block"},ti={backgroundColor:"currentColor"},Wc={backgroundColor:"transparent"},gs={Image:"var(--svg)",Repeat:"no-repeat",Size:"100% 100%"},ws={WebkitMask:ti,mask:ti,background:Wc};for(const e in ws){const t=ws[e];for(const n in gs)t[e+n]=gs[n]}const Th={...Qc,inline:!0};function ks(e){return e+(e.match(/^[-0-9.]+$/)?"px":"")}const Lh=(e,t,n)=>{const r=t.inline?Th:Qc,l=vh(r,t),o=t.mode||"svg",i={},u=t.style||{},s={...o==="svg"?Nh:{}};if(n){const C=Pl(n,!1,!0);if(C){const f=["iconify"],c=["provider","prefix"];for(const d of c)C[d]&&f.push("iconify--"+C[d]);s.className=f.join(" ")}}for(let C in t){const f=t[C];if(f!==void 0)switch(C){case"icon":case"style":case"children":case"onLoad":case"mode":case"ssr":break;case"_ref":s.ref=f;break;case"className":s[C]=(s[C]?s[C]+" ":"")+f;break;case"inline":case"hFlip":case"vFlip":l[C]=f===!0||f==="true"||f===1;break;case"flip":typeof f=="string"&&gh(l,f);break;case"color":i.color=f;break;case"rotate":typeof f=="string"?l[C]=wh(f):typeof f=="number"&&(l[C]=f);break;case"ariaHidden":case"aria-hidden":f!==!0&&f!=="true"&&delete s["aria-hidden"];break;default:r[C]===void 0&&(s[C]=f)}}const a=Hp(e,l),p=a.attributes;if(l.inline&&(i.verticalAlign="-0.125em"),o==="svg"){s.style={...i,...u},Object.assign(s,p);let C=0,f=t.id;return typeof f=="string"&&(f=f.replace(/-/g,"_")),s.dangerouslySetInnerHTML={__html:_h(Yp(a.body,f?()=>f+"ID"+C++:"iconifyReact"))},F.createElement("svg",s)}const{body:m,width:h,height:v}=e,w=o==="mask"||(o==="bg"?!1:m.indexOf("currentColor")!==-1),y=kh(m,{...p,width:h+"",height:v+""});return s.style={...i,"--svg":Eh(y),width:ks(p.width),height:ks(p.height),...Ph,...w?ti:Wc,...u},F.createElement("span",s)};Uc(!0);Gp("",nh);if(typeof document<"u"&&typeof window<"u"){const e=window;if(e.IconifyPreload!==void 0){const t=e.IconifyPreload,n="Invalid IconifyPreload syntax.";typeof t=="object"&&t!==null&&(t instanceof Array?t:[t]).forEach(r=>{try{(typeof r!="object"||r===null||r instanceof Array||typeof r.icons!="object"||typeof r.prefix!="string"||!Fp(r))&&console.error(n)}catch{console.error(n)}})}if(e.IconifyProviders!==void 0){const t=e.IconifyProviders;if(typeof t=="object"&&t!==null)for(let n in t){const r="IconifyProviders["+n+"] is invalid.";try{const l=t[n];if(typeof l!="object"||!l||l.resources===void 0)continue;Xp(n,l)||console.error(r)}catch{console.error(r)}}}}function Kc(e){const[t,n]=F.useState(!!e.ssr),[r,l]=F.useState({});function o(v){if(v){const w=e.icon;if(typeof w=="object")return{name:"",data:w};const y=ds(w);if(y)return{name:w,data:y}}return{name:""}}const[i,u]=F.useState(o(!!e.ssr));function s(){const v=r.callback;v&&(v(),l({}))}function a(v){if(JSON.stringify(i)!==JSON.stringify(v))return s(),u(v),!0}function p(){var v;const w=e.icon;if(typeof w=="object"){a({name:"",data:w});return}const y=ds(w);if(a({name:w,data:y}))if(y===void 0){const C=mh([w],p);l({callback:C})}else y&&((v=e.onLoad)===null||v===void 0||v.call(e,w))}F.useEffect(()=>(n(!0),s),[]),F.useEffect(()=>{t&&p()},[e.icon,t]);const{name:m,data:h}=i;return h?Lh({...Zi,...h},e,m):e.children?e.children:e.fallback?e.fallback:F.createElement("span",{})}const Ur=F.forwardRef((e,t)=>Kc({...e,_ref:t}));F.forwardRef((e,t)=>Kc({inline:!0,...e,_ref:t}));const zh=({character:e,onSelect:t,onInfoClick:n,showInfo:r,PlayCharacter:l})=>T.jsx(T.Fragment,{children:T.jsx("div",{className:"flex justify-center items-center mb-4",children:T.jsx("div",{className:` + h-[65px] w-[462px] cursor-pointer rounded-[5px] overflow-hidden group + ${e.isActive?"":"cursor-pointer rounded-[5px] overflow-hidden bg-[#38383880] border-2 border-[#ffffff50] hover:border-[#FFFFFF] hover:bg-[#38383840"} + `,onClick:()=>t(e.id),children:T.jsxs("div",{className:` + flex items-center gap-2 transition-all duration-300 + ${e.isActive?"text-[#fb9b04]":"text-gray-400 group-hover:text-[#FFFFFF]"} + `,children:[T.jsxs("div",{className:` + flex items-center rounded-[5px] p-4 overflow-hidden + ${e.isActive?"h-[65px] bg-[#fb9b0440] border-2 border-[#fb9b04]":"h-[60px]"} + ${e.isActive&&r?"w-full":e.isActive?"w-[411px]":"w-full"} + `,children:[T.jsx(Ur,{icon:"material-symbols:person-rounded",width:"30",height:"30",className:"mr-3 flex-shrink-0"}),T.jsx("div",{className:"flex justify-center items-center w-full h-full overflow-hidden",children:T.jsx("span",{className:`font-bold text-[24px] tracking-wide whitespace-nowrap ${e.isActive?"truncate":"ml-2"}`,children:e.name})})]}),e.isActive&&T.jsxs("div",{className:"flex gap-2 flex-shrink-0",children:[T.jsx("button",{className:` + h-[65px] rounded-[5px] ${e.disabled?"bg-gray-500":"bg-orange-500 hover:bg-orange-600"} flex text-black items-center justify-center transition-all duration-300 + ${r?"w-0 scale-0 opacity-0 overflow-hidden":"w-[65px] h-[65px] scale-100 opacity-100"} + `,onClick:o=>{o.stopPropagation(),l()},disabled:e.disabled,children:T.jsx(Ur,{icon:"si:play-fill",width:"30",height:"30",color:"#383838"})}),T.jsx("button",{className:"w-[65px] h-[65px] rounded-[5px] bg-orange-500 text-black flex items-center justify-center transition-colors hover:bg-orange-600 flex-shrink-0",onClick:o=>{o.stopPropagation(),n(e.id)},children:T.jsx(Ur,{icon:"bi:info",width:"30",height:"30",color:"#383838"})})]})]})})})}),jh=({character:e,onClose:t,isAllowedtoDelete:n,PlayCharacter:r,handleDelete:l,locale:o})=>e?T.jsx("div",{className:"flex justify-center items-center",children:T.jsxs("div",{className:"w-[462px] bg-neutral-900 rounded-b-lg p-4 animate-slideDown",children:[T.jsxs("div",{className:"mb-4",children:[T.jsxs("h3",{className:"text-gray-400 uppercase text-sm mb-3 font-semibold flex items-center",children:[T.jsx(_p,{className:"mr-2",size:16})," ",o.char_info_title]}),T.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[T.jsxs("div",{className:"bg-neutral-800 p-3 rounded flex items-center",children:[T.jsx(Cp,{className:"mr-3 text-white",size:18}),T.jsx("div",{className:"flex justify-center w-full",children:T.jsx("span",{className:"text-white mr-1",children:e.birthDate})})]}),T.jsxs("div",{className:"bg-neutral-800 p-3 rounded flex items-center",children:[T.jsx(Tp,{className:"mr-3 text-white-400",size:18}),T.jsx("div",{className:"flex justify-center w-full",children:T.jsx("span",{className:"text-white mr-6",children:e.gender})})]}),T.jsxs("div",{className:"bg-neutral-800 p-3 rounded flex items-center col-span-2",children:[T.jsx(Ep,{className:"mr-3 text-white-400",size:18}),T.jsx("div",{className:"flex justify-center w-full",children:T.jsx("span",{className:"text-white mr-6",children:e.occupation})})]})]})]}),T.jsxs("div",{className:"flex gap-2",children:[T.jsxs("button",{className:`flex-1 ${e.disabled?"bg-gray-500":"bg-neutral-800 hover:bg-[#FFA31A] hover:text-[#383838]"} text-white py-2 px-4 font-semibold text-[16px] rounded-[5px] flex items-center justify-center transition-colors`,onClick:r,disabled:e.disabled,children:[T.jsx(Ur,{icon:"flowbite:play-solid",width:23,height:23,className:"mr-2"}),o.play]}),n&&T.jsx("button",{className:"bg-neutral-800 text-white p-2 rounded hover:bg-red-900 transition-colors",onClick:l,children:T.jsx(Pp,{size:18})})]})]})}):null,Ih=""+new URL("esxLogo-sNwPFb_p.png",import.meta.url).href,Mh=()=>!window.invokeNative,Rh=()=>{};async function Cr(e,t,n){const r={method:"post",headers:{"Content-Type":"application/json; charset=UTF-8"},body:JSON.stringify(t)};if(Mh()&&n)return n;const l=window.GetParentResourceName?window.GetParentResourceName():"nui-frame-app";return await(await fetch(`https://${l}/${e}`,r)).json()}const Oh=({initialCharacters:e,Candelete:t,MaxAllowedSlot:n,locale:r})=>{const[l,o]=F.useState(e),[i,u]=F.useState(null),[s,a]=F.useState(l.find(y=>y.isActive)||null),p=y=>{if((s==null?void 0:s.id)===y)return;const C=l.map(f=>({...f,isActive:f.id===y}));o(C),a(C.find(f=>f.id===y)||null),u(null),Cr("SelectCharacter",{id:y})},m=y=>{u(i===y?null:y)},h=()=>{Cr("PlayCharacter")},v=()=>{Cr("CreateCharacter")},w=()=>{if(!s)return;const y=l.filter(C=>C.id!==s.id);if(Cr("DeleteCharacter"),y.length>0){const C=y.map((f,c)=>({...f,isActive:c===0}));o(C),a(C[0]),u(null)}else o([]),a(null),u(null),v()};return T.jsxs("div",{className:"h-screen bg-[#161616F2] text-white w-[527px] border-r border-neutral-800 overflow-hidden animate-slideIn flex flex-col",children:[T.jsxs("div",{className:"p-4 flex-1 overflow-auto",children:[T.jsxs("div",{className:"flex items-center justify-between mb-6",children:[T.jsx("img",{src:Ih,alt:"Logo",className:"h-16 ml-1"}),T.jsx("h2",{className:"text-[24px] font-bold tracking-wide text-right flex-1",children:r.title})]}),T.jsx("div",{className:"space-y-2",children:l.map(y=>T.jsxs("div",{children:[T.jsx(zh,{character:y,onSelect:p,onInfoClick:m,showInfo:i===y.id,PlayCharacter:h}),y.isActive&&i===y.id&&T.jsx(jh,{character:y,onClose:()=>u(null),isAllowedtoDelete:t,PlayCharacter:h,handleDelete:w,locale:r})]},y.id))})]}),T.jsx("div",{className:"p-4 border-t border-neutral-800",children:T.jsx("button",{className:`w-full h-[70px] ${l.length>=n?"bg-gray-500":"bg-[#FB9B04] cursor-pointer hover:bg-orange-600"} text-[#383838] p-3 rounded flex items-center justify-center transition-colors`,onClick:v,disabled:l.length>=n,children:T.jsx(Np,{size:24})})})]})},Fh=(e,t)=>{const n=F.useRef(Rh);F.useEffect(()=>{n.current=t},[t]),F.useEffect(()=>{const r=l=>{const{action:o,data:i}=l.data;n.current&&o===e&&n.current(i)};return window.addEventListener("message",r),()=>window.removeEventListener("message",r)},[e])};function Dh(){const[e,t]=F.useState(!1),[n,r]=F.useState([]),[l,o]=F.useState(!1),[i,u]=F.useState(0),[s,a]=F.useState({char_info_title:"",play:"",title:""});return Fh("ToggleMulticharacter",p=>{if(p.show){const h=p.Characters.filter(v=>v!==null).map((v,w)=>{var y;return{id:v.id.toString(),name:`${v.firstname} ${v.lastname}`,birthDate:v.dateofbirth,gender:((y=v.sex)==null?void 0:y.toUpperCase())==="MALE"?"MALE":"FEMALE",occupation:v.job,disabled:v.disabled,isActive:w===0}});t(!0),r(h),o(p.CanDelete),u(p.AllowedSlot),a(p.Locale)}else t(!1),r([])}),e&&T.jsx(Oh,{initialCharacters:n,Candelete:l,MaxAllowedSlot:i,locale:s})}Mc(document.getElementById("root")).render(T.jsx(Dh,{})); diff --git a/[core]/esx_multicharacter/web/build/index.html b/[core]/esx_multicharacter/web/build/index.html new file mode 100644 index 00000000..9667ca2d --- /dev/null +++ b/[core]/esx_multicharacter/web/build/index.html @@ -0,0 +1,14 @@ + + + + + + + ESX MULTICHARACTER + + + + +
+ + diff --git a/[core]/esx_multicharacter/web/eslint.config.js b/[core]/esx_multicharacter/web/eslint.config.js new file mode 100644 index 00000000..82c2e20c --- /dev/null +++ b/[core]/esx_multicharacter/web/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + } +); diff --git a/[core]/esx_multicharacter/web/index.html b/[core]/esx_multicharacter/web/index.html new file mode 100644 index 00000000..ef5abd29 --- /dev/null +++ b/[core]/esx_multicharacter/web/index.html @@ -0,0 +1,13 @@ + + + + + + + ESX MULTICHARACTER + + +
+ + + diff --git a/[core]/esx_multicharacter/web/package.json b/[core]/esx_multicharacter/web/package.json new file mode 100644 index 00000000..f2c19f58 --- /dev/null +++ b/[core]/esx_multicharacter/web/package.json @@ -0,0 +1,34 @@ +{ + "name": "vite-react-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@iconify/react": "^6.0.0", + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "eslint": "^9.9.1", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.11", + "globals": "^15.9.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.5.3", + "typescript-eslint": "^8.3.0", + "vite": "^5.4.2" + } +} diff --git a/[core]/esx_multicharacter/web/postcss.config.js b/[core]/esx_multicharacter/web/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/[core]/esx_multicharacter/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/[core]/esx_multicharacter/web/src/App.tsx b/[core]/esx_multicharacter/web/src/App.tsx new file mode 100644 index 00000000..f44c47f7 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/App.tsx @@ -0,0 +1,44 @@ +import CharacterSelection from './components/CharacterSelection'; +import { useState } from 'react'; +import { useNuiEvent } from './utils/useNuiEvent' +import { Character, Locale } from './types/Character'; + +function App() { + const [isVisible, setIsVisible] = useState(false); + const [characters, setCharacters] = useState([]); + const [Candelete, setCandelete] = useState(false); + const [MaxAllowedSlot, setMaxAllowedSlot] = useState(0); + const [locale, setLocale] = useState({ char_info_title: '', play: '', title: '' }); + + + useNuiEvent('ToggleMulticharacter', (data:any) => { + if (data.show) { + const validCharacters = data.Characters.filter((char: any) => char !== null); + const parsedCharacters: Character[] = validCharacters.map((char: any, index: number) => ({ + id: char.id.toString(), + name: `${char.firstname} ${char.lastname}`, + birthDate: char.dateofbirth, + gender: char.sex?.toUpperCase() === 'MALE' ? 'MALE' : 'FEMALE', + occupation: char.job, + disabled: char.disabled, + isActive: index === 0, + })); + + setIsVisible(true); + setCharacters(parsedCharacters); + setCandelete(data.CanDelete); + setMaxAllowedSlot(data.AllowedSlot) + setLocale(data.Locale) + } else { + setIsVisible(false); + setCharacters([]); + + } + }) + + return isVisible && ( + + ); +} + +export default App; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/assets/esxLogo.png b/[core]/esx_multicharacter/web/src/assets/esxLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..9be59f9992b9df8e2b5f20c858d19a323f402984 GIT binary patch literal 19788 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%0A^;iF4Hueoa%Y*JK1V&F^$J%$E4xJ@W&e9r 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 void; + onInfoClick: (id: string) => void; + showInfo: boolean; + PlayCharacter: () => void; +} + +const CharacterCard: React.FC = ({ + character, + onSelect, + onInfoClick, + showInfo, + PlayCharacter +}) => { + return ( + <> +
+
onSelect(character.id)} + > +
+
+ +
+ + {character.name} + +
+
+ {character.isActive && ( +
+ + +
+ )} +
+
+
+ + ) +}; + +export default CharacterCard; diff --git a/[core]/esx_multicharacter/web/src/components/CharacterInfo.tsx b/[core]/esx_multicharacter/web/src/components/CharacterInfo.tsx new file mode 100644 index 00000000..c98727ca --- /dev/null +++ b/[core]/esx_multicharacter/web/src/components/CharacterInfo.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Info, Cake, User2, Briefcase, Trash2 } from 'lucide-react'; +import { Character, Locale } from '../types/Character'; +import { Icon } from '@iconify/react'; + +interface CharacterInfoProps { + character: Character; + onClose: () => void; + isAllowedtoDelete: boolean; + PlayCharacter : () => void; + handleDelete: () => void; + locale: Locale; +} + +const CharacterInfo: React.FC = ({ character, onClose, isAllowedtoDelete , PlayCharacter, handleDelete, locale}) => { + if (!character) return null; + + return ( +
+
+
+

+ {locale.char_info_title} +

+ +
+
+ +
+ {character.birthDate} +
+
+
+ +
+ {character.gender} +
+
+
+ +
+ {character.occupation} +
+
+
+
+ +
+ + {isAllowedtoDelete && ( + + )} +
+
+
+ ); +}; + +export default CharacterInfo; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/components/CharacterSelection.tsx b/[core]/esx_multicharacter/web/src/components/CharacterSelection.tsx new file mode 100644 index 00000000..aaf8140c --- /dev/null +++ b/[core]/esx_multicharacter/web/src/components/CharacterSelection.tsx @@ -0,0 +1,119 @@ +import React, { useState } from 'react'; +import { Plus } from 'lucide-react'; +import { Character, Locale } from '../types/Character'; +import CharacterCard from './CharacterCard'; +import CharacterInfo from './CharacterInfo'; +import Logo from '../assets/esxLogo.png'; +import { fetchNui } from '../utils/fetchNui'; + +interface CharacterSelectionProps { + initialCharacters: Character[]; + Candelete: boolean; + MaxAllowedSlot : number; + locale : Locale; +} + +const CharacterSelection: React.FC = ({ initialCharacters, Candelete, MaxAllowedSlot, locale }) => { + const [characters, setCharacters] = useState(initialCharacters); + const [showInfo, setShowInfo] = useState(null); + const [selectedCharacter, setSelectedCharacter] = useState( + characters.find(char => char.isActive) || null + ); + + const handleSelectCharacter = (id: string) => { + if (selectedCharacter?.id === id) return; + + const updatedCharacters = characters.map(char => ({ + ...char, + isActive: char.id === id + })); + + setCharacters(updatedCharacters); + setSelectedCharacter(updatedCharacters.find(char => char.id === id) || null); + setShowInfo(null); + fetchNui('SelectCharacter', {id : id}) + }; + + const toggleInfo = (id: string) => { + setShowInfo(showInfo === id ? null : id); + }; + + const PlayCharacter = () => { + fetchNui('PlayCharacter') + } + + const handleCreateCharacter = () => { + fetchNui('CreateCharacter') + } + + const handleDeleteCharacter = () => { + if (!selectedCharacter) return; + + const updatedCharactersRaw = characters.filter(char => char.id !== selectedCharacter.id); + + fetchNui('DeleteCharacter'); + + if (updatedCharactersRaw.length > 0) { + const updatedCharacters = updatedCharactersRaw.map((char, index) => ({ + ...char, + isActive: index === 0 + })); + + setCharacters(updatedCharacters); + setSelectedCharacter(updatedCharacters[0]); + setShowInfo(null); + } else { + setCharacters([]); + setSelectedCharacter(null); + setShowInfo(null); + handleCreateCharacter(); + } + }; + + return ( +
+
+
+ Logo +

{locale.title}

+
+ +
+ {characters.map(character => ( +
+ + {character.isActive && showInfo === character.id && ( + setShowInfo(null)} + isAllowedtoDelete={Candelete} + PlayCharacter={PlayCharacter} + handleDelete={handleDeleteCharacter} + locale={locale} + /> + )} +
+ ))} +
+
+ +
+ +
+
+ ); +}; + +export default CharacterSelection; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/index.css b/[core]/esx_multicharacter/web/src/index.css new file mode 100644 index 00000000..ab4f4b38 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/index.css @@ -0,0 +1,58 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --primary: #ff9800; + --primary-dark: #f57c00; + --dark-bg: #1c1c1c; + --card-bg: #262626; +} + +body { + margin: 0; + font-family: "Poppins", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: white; +} + +@keyframes slideIn { + from { + transform: translateX(-100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideDown { + from { + max-height: 0; + opacity: 0; + transform: translateY(-10px); + } + to { + max-height: 500px; + opacity: 1; + transform: translateY(0); + } +} + +.animate-slideIn { + animation: slideIn 0.5s ease-out forwards; +} + +.animate-slideDown { + animation: slideDown 0.3s ease-out forwards; + overflow: hidden; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 300ms; +} \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/main.tsx b/[core]/esx_multicharacter/web/src/main.tsx new file mode 100644 index 00000000..f02e9eef --- /dev/null +++ b/[core]/esx_multicharacter/web/src/main.tsx @@ -0,0 +1,7 @@ +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; + +createRoot(document.getElementById('root')!).render( + +); diff --git a/[core]/esx_multicharacter/web/src/types/Character.ts b/[core]/esx_multicharacter/web/src/types/Character.ts new file mode 100644 index 00000000..813b4c88 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/types/Character.ts @@ -0,0 +1,15 @@ +export interface Character { + id: string; + name: string; + birthDate: string; + gender: 'MALE' | 'FEMALE'; + occupation: string; + isActive?: boolean; + disabled?: boolean; +} + +export interface Locale { + char_info_title: string; + play : string; + title : string; +} \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/utils/fetchNui.ts b/[core]/esx_multicharacter/web/src/utils/fetchNui.ts new file mode 100644 index 00000000..67ee2276 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/utils/fetchNui.ts @@ -0,0 +1,39 @@ +import { isEnvBrowser } from "./misc"; + +/** + * Simple wrapper around fetch API tailored for CEF/NUI use. This abstraction + * can be extended to include AbortController if needed or if the response isn't + * JSON. Tailor it to your needs. + * + * @param eventName - The endpoint eventname to target + * @param data - Data you wish to send in the NUI Callback + * @param mockData - Mock data to be returned if in the browser + * + * @return returnData - A promise for the data sent back by the NuiCallbacks CB argument + */ + +export async function fetchNui( + eventName: string, + data?: unknown, + mockData?: T, +): Promise { + const options = { + method: "post", + headers: { + "Content-Type": "application/json; charset=UTF-8", + }, + body: JSON.stringify(data), + }; + + if (isEnvBrowser() && mockData) return mockData; + + const resourceName = (window as any).GetParentResourceName + ? (window as any).GetParentResourceName() + : "nui-frame-app"; + + const resp = await fetch(`https://${resourceName}/${eventName}`, options); + + const respFormatted = await resp.json(); + + return respFormatted; +} \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/utils/misc.ts b/[core]/esx_multicharacter/web/src/utils/misc.ts new file mode 100644 index 00000000..3b4b74bc --- /dev/null +++ b/[core]/esx_multicharacter/web/src/utils/misc.ts @@ -0,0 +1,6 @@ +// Will return whether the current environment is in a regular browser +// and not CEF +export const isEnvBrowser = (): boolean => !(window as any).invokeNative; + +// Basic no operation function +export const noop = () => {}; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/utils/useNuiEvent.tsx b/[core]/esx_multicharacter/web/src/utils/useNuiEvent.tsx new file mode 100644 index 00000000..9983da66 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/utils/useNuiEvent.tsx @@ -0,0 +1,49 @@ +import { MutableRefObject, useEffect, useRef } from "react"; +import { noop } from "./misc"; + +interface NuiMessageData { + action: string; + data: T; +} + +type NuiHandlerSignature = (data: T) => void; + +/** + * A hook that manage events listeners for receiving data from the client scripts + * @param action The specific `action` that should be listened for. + * @param handler The callback function that will handle data relayed by this hook + * + * @example + * useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => { + * // whatever logic you want + * }) + * + **/ + +export const useNuiEvent = ( + action: string, + handler: (data: T) => void, +) => { + const savedHandler: MutableRefObject> = useRef(noop); + + // Make sure we handle for a reactive handler + useEffect(() => { + savedHandler.current = handler; + }, [handler]); + + useEffect(() => { + const eventListener = (event: MessageEvent>) => { + const { action: eventAction, data } = event.data; + + if (savedHandler.current) { + if (eventAction === action) { + savedHandler.current(data); + } + } + }; + + window.addEventListener("message", eventListener); + // Remove Event Listener on component cleanup + return () => window.removeEventListener("message", eventListener); + }, [action]); +}; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/src/vite-env.d.ts b/[core]/esx_multicharacter/web/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/[core]/esx_multicharacter/web/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/[core]/esx_multicharacter/web/tailwind.config.js b/[core]/esx_multicharacter/web/tailwind.config.js new file mode 100644 index 00000000..0b6cd34a --- /dev/null +++ b/[core]/esx_multicharacter/web/tailwind.config.js @@ -0,0 +1,33 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + theme: { + extend: { + colors: { + orange: { + 500: '#FF9800', + 600: '#F57C00', + }, + neutral: { + 800: '#262626', + 900: '#1c1c1c', + } + }, + animation: { + 'slide-in': 'slideIn 0.5s ease-out forwards', + 'slide-down': 'slideDown 0.3s ease-out forwards', + }, + keyframes: { + slideIn: { + '0%': { transform: 'translateX(-100%)', opacity: 0 }, + '100%': { transform: 'translateX(0)', opacity: 1 }, + }, + slideDown: { + '0%': { maxHeight: '0', opacity: 0, transform: 'translateY(-10px)' }, + '100%': { maxHeight: '500px', opacity: 1, transform: 'translateY(0)' }, + }, + }, + }, + }, + plugins: [], +}; \ No newline at end of file diff --git a/[core]/esx_multicharacter/web/tsconfig.app.json b/[core]/esx_multicharacter/web/tsconfig.app.json new file mode 100644 index 00000000..f0a23505 --- /dev/null +++ b/[core]/esx_multicharacter/web/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/[core]/esx_multicharacter/web/tsconfig.json b/[core]/esx_multicharacter/web/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/[core]/esx_multicharacter/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/[core]/esx_multicharacter/web/tsconfig.node.json b/[core]/esx_multicharacter/web/tsconfig.node.json new file mode 100644 index 00000000..0d3d7144 --- /dev/null +++ b/[core]/esx_multicharacter/web/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/[core]/esx_multicharacter/web/vite.config.ts b/[core]/esx_multicharacter/web/vite.config.ts new file mode 100644 index 00000000..d3cf25a6 --- /dev/null +++ b/[core]/esx_multicharacter/web/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + base: './', + plugins: [react()], + optimizeDeps: { + exclude: ['lucide-react'], + }, + build: { + outDir: 'build', + }, +}); From 4bd80ca3e649590068a3e2285449ce15ad50afd4 Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Thu, 22 May 2025 19:10:31 +0800 Subject: [PATCH 106/132] feat(es_extended/server/main): add new handler 'esx:playerCrashed' for unloading players stale --- [core]/es_extended/server/main.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 993e99ec..a4d5a07d 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -368,6 +368,25 @@ AddEventHandler("esx:playerLogout", function(playerId, cb) TriggerClientEvent("esx:onPlayerLogout", playerId) end) +AddEventHandler('esx:playerCrashed', function(identifier, cb) + local xPlayer = ESX.GetPlayerFromIdentifier(identifier) + + if xPlayer then + local playerId = xPlayer.playerId + local isExist = DoesPlayerExist(playerId --[[@as string]]) + + if playerId and isExist ~= 0 then + TriggerEvent('esx:playerDropped', playerId) + GlobalState["playerCount"] = GlobalState["playerCount"] - 1 + Core.playersByIdentifier[identifier] = nil + ESX.Players[playerId] = nil + if cb then + cb() + end + end + end +end) + if not Config.CustomInventory then RegisterNetEvent("esx:updateWeaponAmmo", function(weaponName, ammoCount) local xPlayer = ESX.GetPlayerFromId(source) From af52ca38519075ce64a83fafd2d9485734c8dc02 Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Thu, 22 May 2025 19:13:42 +0800 Subject: [PATCH 107/132] refactor(es_extended/server/main): use 'esx:playerCrashed' for consistency --- [core]/es_extended/server/main.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index a4d5a07d..95626930 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -115,17 +115,15 @@ if not Config.Multichar then return deferrals.done("[ESX] OxMySQL Was Unable To Connect to your database. Please make sure it is turned on and correctly configured in your server.cfg") end - if identifier then - if not playerId and ESX.GetPlayerFromIdentifier(identifier) then - return deferrals.done( - ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) - ) - else - return deferrals.done() - end - else + if not identifier then return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end + + TriggerEvent('esx:playerCrashed', identifier, function() + deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) + end) + + return deferrals.done() end) end From 2cb6531f823bb01f9b97c59c8b22e84409f9c1bb Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Thu, 22 May 2025 19:17:24 +0800 Subject: [PATCH 108/132] refactor(esx_multicharacter/server/modules/functions): use 'esx:playerCrashed' for consistency --- .../server/modules/functions.lua | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 6c816571..b46dd43a 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -45,19 +45,17 @@ function Server:OnConnecting(source, deferrals) deferrals.done("[ESX Multicharacter] OxMySQL Was Unable To Connect to your database. Please make sure it is turned on and correctly configured in your server.cfg") end - if identifier then - if not ESX.GetConfig().EnableDebug then - if not source and ESX.Players[identifier] then - deferrals.done(("[ESX Multicharacter] A player is already connected to the server with this identifier.\nYour identifier: %s:%s"):format(Server.identifierType, identifier)) - else - deferrals.done() - end - else - deferrals.done() - end - else - deferrals.done(("[ESX Multicharacter] Unable to retrieve player identifier.\nIdentifier type: %s"):format(Server.identifierType)) - end + if not identifier then return deferrals.done(("[ESX Multicharacter] Unable to retrieve player identifier.\nIdentifier type: %s"):format(Server.identifierType)) end + + if ESX.GetConfig().EnableDebug or not ESX.Players[identifier] then deferrals.done() end + + TriggerEvent('esx:playerCrashed', identifier, function() + ESX.Players[identifier] = nil + deferrals.update(('[ESX] Cleaning old Player entry for [%s] (player invalid)'):format(identifier)) + end) + + deferrals.done() -- proceed end + Server:ResetPlayers() From 7d3fd2767600de15c3a6b73ddb31020c4f20d83d Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Thu, 22 May 2025 19:22:52 +0800 Subject: [PATCH 109/132] refactor(es_extended/server/main): use 'esx:playerCrashed' for consistency --- [core]/es_extended/server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 95626930..e593608c 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -50,7 +50,7 @@ local function onPlayerJoined(playerId) return DropPlayer(playerId, "there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - if not playerId and ESX.GetPlayerFromIdentifier(identifier) then + if ESX.GetPlayerFromIdentifier(identifier) then DropPlayer( playerId, ("there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s"):format( From 87e13f38351d7393ac5f61cd904ff877a2534b43 Mon Sep 17 00:00:00 2001 From: Muhammed Yaseen Date: Fri, 23 May 2025 08:47:56 +0530 Subject: [PATCH 110/132] Locales --- [core]/esx_multicharacter/locales/cs.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/da.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/de.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/es.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/fi.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/fr.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/gr.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/he.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/hu.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/it.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/nl.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/pl.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/pt.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/sl.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/sr.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/sv.lua | 22 +++++++++++++++++++++ [core]/esx_multicharacter/locales/zh-cn.lua | 22 +++++++++++++++++++++ 17 files changed, 374 insertions(+) create mode 100644 [core]/esx_multicharacter/locales/cs.lua create mode 100644 [core]/esx_multicharacter/locales/da.lua create mode 100644 [core]/esx_multicharacter/locales/de.lua create mode 100644 [core]/esx_multicharacter/locales/es.lua create mode 100644 [core]/esx_multicharacter/locales/fi.lua create mode 100644 [core]/esx_multicharacter/locales/fr.lua create mode 100644 [core]/esx_multicharacter/locales/gr.lua create mode 100644 [core]/esx_multicharacter/locales/he.lua create mode 100644 [core]/esx_multicharacter/locales/hu.lua create mode 100644 [core]/esx_multicharacter/locales/it.lua create mode 100644 [core]/esx_multicharacter/locales/nl.lua create mode 100644 [core]/esx_multicharacter/locales/pl.lua create mode 100644 [core]/esx_multicharacter/locales/pt.lua create mode 100644 [core]/esx_multicharacter/locales/sl.lua create mode 100644 [core]/esx_multicharacter/locales/sr.lua create mode 100644 [core]/esx_multicharacter/locales/sv.lua create mode 100644 [core]/esx_multicharacter/locales/zh-cn.lua diff --git a/[core]/esx_multicharacter/locales/cs.lua b/[core]/esx_multicharacter/locales/cs.lua new file mode 100644 index 00000000..083f05af --- /dev/null +++ b/[core]/esx_multicharacter/locales/cs.lua @@ -0,0 +1,22 @@ +Locales["cs"] = { + ["male"] = "Muž", + ["female"] = "Žena", + ["command_setslots"] = "Nastavit počet multicharakterových slotů hráče", + ["command_remslots"] = "Odebrat počet multicharakterových slotů hráče", + ["command_enablechar"] = "Povolit danou postavu hráče", + ["command_disablechar"] = "Zakázat danou postavu hráče", + ["command_charslot"] = "Číslo slotu postavy", + ["command_identifier"] = "Identifikátor hráče", + ["command_slots"] = "Počet slotů", + ["slotsadd"] = "Nastavil jsi %s slotů pro %s", + ["slotsrem"] = "Odebral jsi sloty pro %s", + ["charenabled"] = "Povolil jsi postavu #%s hráče %s", + ["chardisabled"] = "Zakázal jsi postavu #%s hráče %s", + ["charnotfound"] = "Postava #%s hráče %s neexistuje", + + UI = { + ["title"] = "VÝBĚR POSTAVY", + ["char_info_title"] = "Informace o postavě", + ["play"] = "HRÁT", + } +} diff --git a/[core]/esx_multicharacter/locales/da.lua b/[core]/esx_multicharacter/locales/da.lua new file mode 100644 index 00000000..4155b61c --- /dev/null +++ b/[core]/esx_multicharacter/locales/da.lua @@ -0,0 +1,22 @@ +Locales["da"] = { + ["male"] = "Mand", + ["female"] = "Kvinde", + ["command_setslots"] = "Indstil antal multikarakter-slots for en spiller", + ["command_remslots"] = "Fjern antal multikarakter-slots for en spiller", + ["command_enablechar"] = "Aktivér en given karakter for en spiller", + ["command_disablechar"] = "Deaktivér en given karakter for en spiller", + ["command_charslot"] = "Karakterens slotnummer", + ["command_identifier"] = "Spillerens identifikator", + ["command_slots"] = "Antal slots", + ["slotsadd"] = "Du satte %s slots til %s", + ["slotsrem"] = "Du fjernede slots fra %s", + ["charenabled"] = "Du aktiverede karakter #%s for %s", + ["chardisabled"] = "Du deaktiverede karakter #%s for %s", + ["charnotfound"] = "Karakter #%s for %s findes ikke", + + UI = { + ["title"] = "KARAKTERVALG", + ["char_info_title"] = "Karakterinformation", + ["play"] = "SPIL", + } +} diff --git a/[core]/esx_multicharacter/locales/de.lua b/[core]/esx_multicharacter/locales/de.lua new file mode 100644 index 00000000..5f8e5d5d --- /dev/null +++ b/[core]/esx_multicharacter/locales/de.lua @@ -0,0 +1,22 @@ +Locales["de"] = { + ["male"] = "Männlich", + ["female"] = "Weiblich", + ["command_setslots"] = "Anzahl der Mehrcharakter-Slots eines Spielers festlegen", + ["command_remslots"] = "Anzahl der Mehrcharakter-Slots eines Spielers entfernen", + ["command_enablechar"] = "Einen bestimmten Charakter eines Spielers aktivieren", + ["command_disablechar"] = "Einen bestimmten Charakter eines Spielers deaktivieren", + ["command_charslot"] = "Slot-Nummer des Charakters", + ["command_identifier"] = "Spielerkennung", + ["command_slots"] = "Anzahl der Slots", + ["slotsadd"] = "Du hast %s Slots für %s festgelegt", + ["slotsrem"] = "Du hast Slots von %s entfernt", + ["charenabled"] = "Du hast Charakter #%s von %s aktiviert", + ["chardisabled"] = "Du hast Charakter #%s von %s deaktiviert", + ["charnotfound"] = "Charakter #%s von %s existiert nicht", + + UI = { + ["title"] = "CHARAKTERAUSWAHL", + ["char_info_title"] = "Charakterinformation", + ["play"] = "SPIELEN", + } +} diff --git a/[core]/esx_multicharacter/locales/es.lua b/[core]/esx_multicharacter/locales/es.lua new file mode 100644 index 00000000..59c94065 --- /dev/null +++ b/[core]/esx_multicharacter/locales/es.lua @@ -0,0 +1,22 @@ +Locales["es"] = { + ["male"] = "Hombre", + ["female"] = "Mujer", + ["command_setslots"] = "Establecer número de ranuras multicaracter para un jugador", + ["command_remslots"] = "Eliminar número de ranuras multicaracter para un jugador", + ["command_enablechar"] = "Habilitar un personaje específico de un jugador", + ["command_disablechar"] = "Deshabilitar un personaje específico de un jugador", + ["command_charslot"] = "Número de ranura del personaje", + ["command_identifier"] = "Identificador del jugador", + ["command_slots"] = "Número de ranuras", + ["slotsadd"] = "Has establecido %s ranuras para %s", + ["slotsrem"] = "Has eliminado ranuras para %s", + ["charenabled"] = "Has habilitado el personaje #%s de %s", + ["chardisabled"] = "Has deshabilitado el personaje #%s de %s", + ["charnotfound"] = "El personaje #%s de %s no existe", + + UI = { + ["title"] = "SELECCIÓN DE PERSONAJE", + ["char_info_title"] = "Información del personaje", + ["play"] = "JUGAR", + } +} diff --git a/[core]/esx_multicharacter/locales/fi.lua b/[core]/esx_multicharacter/locales/fi.lua new file mode 100644 index 00000000..5ac07734 --- /dev/null +++ b/[core]/esx_multicharacter/locales/fi.lua @@ -0,0 +1,22 @@ +Locales["fi"] = { + ["male"] = "Mies", + ["female"] = "Nainen", + ["command_setslots"] = "Aseta pelaajalle monihahmoslottien määrä", + ["command_remslots"] = "Poista pelaajalta monihahmoslotteja", + ["command_enablechar"] = "Ota käyttöön pelaajan tietty hahmo", + ["command_disablechar"] = "Poista käytöstä pelaajan tietty hahmo", + ["command_charslot"] = "Hahmon slottinumero", + ["command_identifier"] = "Pelaajan tunniste", + ["command_slots"] = "Slottien määrä", + ["slotsadd"] = "Asetit %s slottia pelaajalle %s", + ["slotsrem"] = "Poistit slottien määrän pelaajalta %s", + ["charenabled"] = "Otat käyttöön hahmon #%s pelaajalta %s", + ["chardisabled"] = "Poistit käytöstä hahmon #%s pelaajalta %s", + ["charnotfound"] = "Hahmo #%s pelaajalta %s ei ole olemassa", + + UI = { + ["title"] = "HAHMON VALINTA", + ["char_info_title"] = "Hahmon tiedot", + ["play"] = "PELAA", + } +} diff --git a/[core]/esx_multicharacter/locales/fr.lua b/[core]/esx_multicharacter/locales/fr.lua new file mode 100644 index 00000000..78bc7e5e --- /dev/null +++ b/[core]/esx_multicharacter/locales/fr.lua @@ -0,0 +1,22 @@ +Locales["fr"] = { + ["male"] = "Homme", + ["female"] = "Femme", + ["command_setslots"] = "Définir le nombre d'emplacements multicaractères d'un joueur", + ["command_remslots"] = "Retirer le nombre d'emplacements multicaractères d'un joueur", + ["command_enablechar"] = "Activer un personnage spécifique d'un joueur", + ["command_disablechar"] = "Désactiver un personnage spécifique d'un joueur", + ["command_charslot"] = "Numéro d'emplacement du personnage", + ["command_identifier"] = "Identifiant du joueur", + ["command_slots"] = "Nombre d'emplacements", + ["slotsadd"] = "Vous avez défini %s emplacements pour %s", + ["slotsrem"] = "Vous avez retiré des emplacements pour %s", + ["charenabled"] = "Vous avez activé le personnage #%s de %s", + ["chardisabled"] = "Vous avez désactivé le personnage #%s de %s", + ["charnotfound"] = "Le personnage #%s de %s n'existe pas", + + UI = { + ["title"] = "SÉLECTION DE PERSONNAGE", + ["char_info_title"] = "Infos du personnage", + ["play"] = "JOUER", + } +} diff --git a/[core]/esx_multicharacter/locales/gr.lua b/[core]/esx_multicharacter/locales/gr.lua new file mode 100644 index 00000000..cae6e2bc --- /dev/null +++ b/[core]/esx_multicharacter/locales/gr.lua @@ -0,0 +1,22 @@ +Locales["gr"] = { + ["male"] = "Άνδρας", + ["female"] = "Γυναίκα", + ["command_setslots"] = "Ορισμός αριθμού multicharacter θέσεων για έναν παίκτη", + ["command_remslots"] = "Αφαίρεση αριθμού multicharacter θέσεων από έναν παίκτη", + ["command_enablechar"] = "Ενεργοποίηση συγκεκριμένου χαρακτήρα ενός παίκτη", + ["command_disablechar"] = "Απενεργοποίηση συγκεκριμένου χαρακτήρα ενός παίκτη", + ["command_charslot"] = "Αριθμός θέσης χαρακτήρα", + ["command_identifier"] = "Αναγνωριστικό παίκτη", + ["command_slots"] = "Αριθμός θέσεων", + ["slotsadd"] = "Ορίσατε %s θέσεις για τον/την %s", + ["slotsrem"] = "Αφαιρέσατε θέσεις από τον/την %s", + ["charenabled"] = "Ενεργοποιήσατε τον χαρακτήρα #%s του/της %s", + ["chardisabled"] = "Απενεργοποιήσατε τον χαρακτήρα #%s του/της %s", + ["charnotfound"] = "Ο χαρακτήρας #%s του/της %s δεν υπάρχει", + + UI = { + ["title"] = "ΕΠΙΛΟΓΗ ΧΑΡΑΚΤΗΡΑ", + ["char_info_title"] = "Πληροφορίες Χαρακτήρα", + ["play"] = "ΠΑΙΞΕ", + } +} diff --git a/[core]/esx_multicharacter/locales/he.lua b/[core]/esx_multicharacter/locales/he.lua new file mode 100644 index 00000000..e9ec999c --- /dev/null +++ b/[core]/esx_multicharacter/locales/he.lua @@ -0,0 +1,22 @@ +Locales["he"] = { + ["male"] = "זכר", + ["female"] = "נקבה", + ["command_setslots"] = "הגדר מספר חריצי דמויות מרובות לשחקן", + ["command_remslots"] = "הסר מספר חריצי דמויות מרובות משחקן", + ["command_enablechar"] = "אפשר דמות מסוימת של שחקן", + ["command_disablechar"] = "נטרל דמות מסוימת של שחקן", + ["command_charslot"] = "מספר חריץ הדמות", + ["command_identifier"] = "מזהה שחקן", + ["command_slots"] = "מספר חריצים", + ["slotsadd"] = "הגדרת %s חריצים עבור %s", + ["slotsrem"] = "הסרת חריצים מ-%s", + ["charenabled"] = "איפשרת את הדמות #%s של %s", + ["chardisabled"] = "נטרלת את הדמות #%s של %s", + ["charnotfound"] = "הדמות #%s של %s לא קיימת", + + UI = { + ["title"] = "בחירת דמות", + ["char_info_title"] = "פרטי דמות", + ["play"] = "שחק", + } +} diff --git a/[core]/esx_multicharacter/locales/hu.lua b/[core]/esx_multicharacter/locales/hu.lua new file mode 100644 index 00000000..8693a119 --- /dev/null +++ b/[core]/esx_multicharacter/locales/hu.lua @@ -0,0 +1,22 @@ +Locales["hu"] = { + ["male"] = "Férfi", + ["female"] = "Nő", + ["command_setslots"] = "Többszereplős slotok számának beállítása egy játékos számára", + ["command_remslots"] = "Többszereplős slotok számának eltávolítása egy játékostól", + ["command_enablechar"] = "Egy adott karakter engedélyezése egy játékosnál", + ["command_disablechar"] = "Egy adott karakter letiltása egy játékosnál", + ["command_charslot"] = "A karakter slot száma", + ["command_identifier"] = "Játékos azonosító", + ["command_slots"] = "Slotok száma", + ["slotsadd"] = "%s slotot állítottál be %s játékosnak", + ["slotsrem"] = "Eltávolítottad a slotokat %s játékostól", + ["charenabled"] = "Engedélyezted a(z) #%s karaktert %s játékosnál", + ["chardisabled"] = "Letiltottad a(z) #%s karaktert %s játékosnál", + ["charnotfound"] = "A(z) #%s karakter %s játékosnál nem létezik", + + UI = { + ["title"] = "KARAKTER KIVÁLASZTÁSA", + ["char_info_title"] = "Karakterinformáció", + ["play"] = "JÁTÉK", + } +} diff --git a/[core]/esx_multicharacter/locales/it.lua b/[core]/esx_multicharacter/locales/it.lua new file mode 100644 index 00000000..8a06ca46 --- /dev/null +++ b/[core]/esx_multicharacter/locales/it.lua @@ -0,0 +1,22 @@ +Locales["it"] = { + ["male"] = "Maschio", + ["female"] = "Femmina", + ["command_setslots"] = "Imposta il numero di slot multicarattere per un giocatore", + ["command_remslots"] = "Rimuovi il numero di slot multicarattere da un giocatore", + ["command_enablechar"] = "Abilita un determinato personaggio di un giocatore", + ["command_disablechar"] = "Disabilita un determinato personaggio di un giocatore", + ["command_charslot"] = "Numero dello slot del personaggio", + ["command_identifier"] = "Identificatore del giocatore", + ["command_slots"] = "Numero di slot", + ["slotsadd"] = "Hai impostato %s slot per %s", + ["slotsrem"] = "Hai rimosso gli slot da %s", + ["charenabled"] = "Hai abilitato il personaggio #%s di %s", + ["chardisabled"] = "Hai disabilitato il personaggio #%s di %s", + ["charnotfound"] = "Il personaggio #%s di %s non esiste", + + UI = { + ["title"] = "SELEZIONE DEL PERSONAGGIO", + ["char_info_title"] = "Informazioni del personaggio", + ["play"] = "GIOCA", + } +} diff --git a/[core]/esx_multicharacter/locales/nl.lua b/[core]/esx_multicharacter/locales/nl.lua new file mode 100644 index 00000000..6e527644 --- /dev/null +++ b/[core]/esx_multicharacter/locales/nl.lua @@ -0,0 +1,22 @@ +Locales["nl"] = { + ["male"] = "Man", + ["female"] = "Vrouw", + ["command_setslots"] = "Stel het aantal multi-personage slots in voor een speler", + ["command_remslots"] = "Verwijder het aantal multi-personage slots van een speler", + ["command_enablechar"] = "Schakel een specifiek personage van een speler in", + ["command_disablechar"] = "Schakel een specifiek personage van een speler uit", + ["command_charslot"] = "Personage slotnummer", + ["command_identifier"] = "Speleridentificatie", + ["command_slots"] = "Aantal slots", + ["slotsadd"] = "Je hebt %s slots ingesteld voor %s", + ["slotsrem"] = "Je hebt slots verwijderd van %s", + ["charenabled"] = "Je hebt personage #%s van %s ingeschakeld", + ["chardisabled"] = "Je hebt personage #%s van %s uitgeschakeld", + ["charnotfound"] = "Personage #%s van %s bestaat niet", + + UI = { + ["title"] = "PERSONAGE SELECTIE", + ["char_info_title"] = "Personage Informatie", + ["play"] = "SPEEL", + } +} diff --git a/[core]/esx_multicharacter/locales/pl.lua b/[core]/esx_multicharacter/locales/pl.lua new file mode 100644 index 00000000..8c290171 --- /dev/null +++ b/[core]/esx_multicharacter/locales/pl.lua @@ -0,0 +1,22 @@ +Locales["pl"] = { + ["male"] = "Mężczyzna", + ["female"] = "Kobieta", + ["command_setslots"] = "Ustaw liczbę slotów multi-postaci dla gracza", + ["command_remslots"] = "Usuń liczbę slotów multi-postaci od gracza", + ["command_enablechar"] = "Włącz konkretną postać gracza", + ["command_disablechar"] = "Wyłącz konkretną postać gracza", + ["command_charslot"] = "Numer slotu postaci", + ["command_identifier"] = "Identyfikator gracza", + ["command_slots"] = "Liczba slotów", + ["slotsadd"] = "Ustawiłeś %s slotów dla %s", + ["slotsrem"] = "Usunąłeś sloty od %s", + ["charenabled"] = "Włączyłeś postać #%s dla %s", + ["chardisabled"] = "Wyłączyłeś postać #%s dla %s", + ["charnotfound"] = "Postać #%s dla %s nie istnieje", + + UI = { + ["title"] = "WYBÓR POSTACI", + ["char_info_title"] = "Informacje o postaci", + ["play"] = "GRAJ", + } +} diff --git a/[core]/esx_multicharacter/locales/pt.lua b/[core]/esx_multicharacter/locales/pt.lua new file mode 100644 index 00000000..23b2c201 --- /dev/null +++ b/[core]/esx_multicharacter/locales/pt.lua @@ -0,0 +1,22 @@ +Locales["pt"] = { + ["male"] = "Masculino", + ["female"] = "Feminino", + ["command_setslots"] = "Defina o número de slots de múltiplos personagens para um jogador", + ["command_remslots"] = "Remova o número de slots de múltiplos personagens de um jogador", + ["command_enablechar"] = "Ative um personagem específico de um jogador", + ["command_disablechar"] = "Desative um personagem específico de um jogador", + ["command_charslot"] = "Número do slot do personagem", + ["command_identifier"] = "Identificador do jogador", + ["command_slots"] = "Número de slots", + ["slotsadd"] = "Você definiu %s slots para %s", + ["slotsrem"] = "Você removeu slots de %s", + ["charenabled"] = "Você ativou o personagem #%s de %s", + ["chardisabled"] = "Você desativou o personagem #%s de %s", + ["charnotfound"] = "O personagem #%s de %s não existe", + + UI = { + ["title"] = "SELEÇÃO DE PERSONAGEM", + ["char_info_title"] = "Informações do personagem", + ["play"] = "JOGAR", + } +} diff --git a/[core]/esx_multicharacter/locales/sl.lua b/[core]/esx_multicharacter/locales/sl.lua new file mode 100644 index 00000000..1697e3b7 --- /dev/null +++ b/[core]/esx_multicharacter/locales/sl.lua @@ -0,0 +1,22 @@ +Locales["sl"] = { + ["male"] = "Moški", + ["female"] = "Ženska", + ["command_setslots"] = "Nastavi število večkarakternih slotov za igralca", + ["command_remslots"] = "Odstrani število večkarakternih slotov igralcu", + ["command_enablechar"] = "Omogoči določen lik igralca", + ["command_disablechar"] = "Onemogoči določen lik igralca", + ["command_charslot"] = "Številka slota lika", + ["command_identifier"] = "Identifikator igralca", + ["command_slots"] = "Število slotov", + ["slotsadd"] = "Nastavil si %s slotov za %s", + ["slotsrem"] = "Odstranil si slot(e) od %s", + ["charenabled"] = "Omogočil si lika #%s za %s", + ["chardisabled"] = "Onemogočil si lika #%s za %s", + ["charnotfound"] = "Lik #%s za %s ne obstaja", + + UI = { + ["title"] = "IZBIRA LIKA", + ["char_info_title"] = "Informacije o liku", + ["play"] = "IGRAJ", + } +} diff --git a/[core]/esx_multicharacter/locales/sr.lua b/[core]/esx_multicharacter/locales/sr.lua new file mode 100644 index 00000000..7ace5d91 --- /dev/null +++ b/[core]/esx_multicharacter/locales/sr.lua @@ -0,0 +1,22 @@ +Locales["sr"] = { + ["male"] = "Muški", + ["female"] = "Ženski", + ["command_setslots"] = "Postavi broj multi-karakternih slotova za igrača", + ["command_remslots"] = "Ukloni broj multi-karakternih slotova od igrača", + ["command_enablechar"] = "Omogući određeni lik igrača", + ["command_disablechar"] = "Onemogući određeni lik igrača", + ["command_charslot"] = "Broj slota lika", + ["command_identifier"] = "Identifikator igrača", + ["command_slots"] = "Broj slotova", + ["slotsadd"] = "Postavio si %s slotova za %s", + ["slotsrem"] = "Uklonio si slotove od %s", + ["charenabled"] = "Omogućio si lika #%s za %s", + ["chardisabled"] = "Onemogućio si lika #%s za %s", + ["charnotfound"] = "Lik #%s za %s ne postoji", + + UI = { + ["title"] = "IZBOR LIKA", + ["char_info_title"] = "Informacije o liku", + ["play"] = "IGRAJ", + } +} diff --git a/[core]/esx_multicharacter/locales/sv.lua b/[core]/esx_multicharacter/locales/sv.lua new file mode 100644 index 00000000..b7d3617e --- /dev/null +++ b/[core]/esx_multicharacter/locales/sv.lua @@ -0,0 +1,22 @@ +Locales["sv"] = { + ["male"] = "Man", + ["female"] = "Kvinna", + ["command_setslots"] = "Sätt antalet multikaraktärsplatser för en spelare", + ["command_remslots"] = "Ta bort antalet multikaraktärsplatser från en spelare", + ["command_enablechar"] = "Aktivera en specifik karaktär för en spelare", + ["command_disablechar"] = "Inaktivera en specifik karaktär för en spelare", + ["command_charslot"] = "Karaktärsplatsnummer", + ["command_identifier"] = "Spelaridentifierare", + ["command_slots"] = "Antal platser", + ["slotsadd"] = "Du har satt %s platser för %s", + ["slotsrem"] = "Du har tagit bort platser från %s", + ["charenabled"] = "Du har aktiverat karaktär #%s för %s", + ["chardisabled"] = "Du har inaktiverat karaktär #%s för %s", + ["charnotfound"] = "Karaktär #%s för %s finns inte", + + UI = { + ["title"] = "KARAKTÄRSVAL", + ["char_info_title"] = "Karaktärsinformation", + ["play"] = "SPELA", + } +} diff --git a/[core]/esx_multicharacter/locales/zh-cn.lua b/[core]/esx_multicharacter/locales/zh-cn.lua new file mode 100644 index 00000000..4d4e5fce --- /dev/null +++ b/[core]/esx_multicharacter/locales/zh-cn.lua @@ -0,0 +1,22 @@ +Locales["zh-cn"] = { + ["male"] = "男性", + ["female"] = "女性", + ["command_setslots"] = "为玩家设置多角色槽位数量", + ["command_remslots"] = "移除玩家的多角色槽位数量", + ["command_enablechar"] = "启用玩家的指定角色", + ["command_disablechar"] = "禁用玩家的指定角色", + ["command_charslot"] = "角色槽位编号", + ["command_identifier"] = "玩家标识", + ["command_slots"] = "槽位数量", + ["slotsadd"] = "你已为 %s 设置了 %s 个槽位", + ["slotsrem"] = "你已移除了 %s 的槽位", + ["charenabled"] = "你已启用了 %s 的角色 #%s", + ["chardisabled"] = "你已禁用了 %s 的角色 #%s", + ["charnotfound"] = "%s 的角色 #%s 不存在", + + UI = { + ["title"] = "角色选择", + ["char_info_title"] = "角色信息", + ["play"] = "开始游戏", + } +} From 62701bd4ec27f75ceee3553c455440b612b1b530 Mon Sep 17 00:00:00 2001 From: iSentrie Date: Fri, 23 May 2025 19:43:37 +0300 Subject: [PATCH 111/132] fix(es_extended): text color formatting in warning message added ^0 to ensure proper color reset at the end of the string in console output for command warnings --- [core]/es_extended/server/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 5ba47867..c80bf48a 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -62,7 +62,7 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) local command = Core.RegisteredCommands[name] if not command.allowConsole and playerId == 0 then - print(("[^3WARNING^7] ^5%s"):format(TranslateCap("commanderror_console"))) + print(("[^3WARNING^7] ^5%s^0"):format(TranslateCap("commanderror_console"))) else local xPlayer, error = ESX.Players[playerId], nil From 1f0cb4a466f35e3e45c4fee15e0c700ceb9a5444 Mon Sep 17 00:00:00 2001 From: "Arctos." <116841243+Arctos2win@users.noreply.github.com> Date: Tue, 27 May 2025 10:15:16 +0200 Subject: [PATCH 112/132] =?UTF-8?q?chore(.github/ISSUE=5FTEMPLATE):=20=20?= =?UTF-8?q?=F0=9F=93=A5=20remove=20me=20from=20default=20assignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 58408965..c087e007 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve title: "[Bug] - esx_script - Issue" labels: bug -assignees: Arctos2win, Kenshiin13 +assignees: Kenshiin13 --- From c95d1769b47717041c2fb8e64ab28e5b3fefba64 Mon Sep 17 00:00:00 2001 From: "Arctos." <116841243+Arctos2win@users.noreply.github.com> Date: Tue, 27 May 2025 10:16:10 +0200 Subject: [PATCH 113/132] =?UTF-8?q?chore(.github/ISSUE=5FTEMPLATE/feature?= =?UTF-8?q?=5Frequest.md):=20=20=F0=9F=93=A5=20remove=20me=20from=20defaul?= =?UTF-8?q?t=20assignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 305b3f1b..d635b2bd 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature Request about: Help us improve esx with your ideas title: "[Feature Request] - esx_script - Add better configuration" labels: enhancement -assignees: Arctos2win, Kenshiin13 +assignees: Kenshiin13 --- From 20e36fa15a20da0b5f273d4836bcb18447b7970b Mon Sep 17 00:00:00 2001 From: Tabby Dev <88893827+Tabby-Labs@users.noreply.github.com> Date: Fri, 30 May 2025 21:20:04 +0800 Subject: [PATCH 114/132] undo(es_extended/server/main.lua): revert 'playerConnecting' stale data removing logic --- [core]/es_extended/server/main.lua | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index e593608c..d0bbfed5 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -119,9 +119,24 @@ if not Config.Multichar then return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - TriggerEvent('esx:playerCrashed', identifier, function() - deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) - end) + local xPlayer = ESX.GetPlayerFromIdentifier(identifier) + + if xPlayer and ESX.Players[xPlayer.playerId] then + local xPlayerId = xPlayer.playerId + local isExist = DoesPlayerExist(xPlayerId --[[@as string]]) + + if isExist ~= 0 then + deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) + TriggerEvent('esx:playerDropped', xPlayerId) + ESX.Players[xPlayerId] = nil + Core.playersByIdentifier[identifier] = nil + GlobalState['playerCount'] = GlobalState['playerCount'] - 1 + else + return deferrals.done( + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) + end + end return deferrals.done() end) From 2bbc07a0525818bf0ed7ff0350d56e43fc7abb5b Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:20:35 +0200 Subject: [PATCH 115/132] fix(es_extended/locales/es): add missing comma --- [core]/es_extended/locales/es.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/locales/es.lua b/[core]/es_extended/locales/es.lua index 8ba92c14..7cae9861 100644 --- a/[core]/es_extended/locales/es.lua +++ b/[core]/es_extended/locales/es.lua @@ -188,7 +188,7 @@ Locales["es"] = { ["weapon_assaultsmg"] = "Subfusil de asalto", ["weapon_combatmg"] = "Ametralladora de combate", ["weapon_combatmg_mk2"] = "Ametralladora de combate Mk2", - ["weapon_combatpdw"] = "PDW de combate" + ["weapon_combatpdw"] = "PDW de combate", ["weapon_gusenberg"] = "Subfusil Gusenberg", ["weapon_machinepistol"] = "Pistola ametralladora", ["weapon_mg"] = "Ametralladora", From 911c997f4d81c77669aedeb36f3ce769d3e53cd4 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:22:57 +0200 Subject: [PATCH 116/132] fix(skinchanger/locales/de): update wrong translation keys --- [core]/skinchanger/locales/de.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/[core]/skinchanger/locales/de.lua b/[core]/skinchanger/locales/de.lua index 54e1f1e7..2347ffc0 100644 --- a/[core]/skinchanger/locales/de.lua +++ b/[core]/skinchanger/locales/de.lua @@ -90,8 +90,8 @@ Locales["de"] = { ["complexion_1"] = "Teintstärke", ["sun"] = "Sonne", ["sun_1"] = "Sonnenstärke", - ["freckles_1"] = "Sommersprossen", - ["freckles_2"] = "Sommersprossenstärke", + ["freckles"] = "Sommersprossen", + ["freckles_1"] = "Sommersprossenstärke", ["chest_hair"] = "Brusthaar", ["chest_hair_1"] = "Brusthaarstärke", ["chest_color"] = "Brusthaarfarbe", From 5e6fbd1661e7f092d658018c199e7e1bd2adbf5c Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:39:44 +0200 Subject: [PATCH 117/132] fix(es_extended/server/main): player doesnt have to be in both tables. --- [core]/es_extended/server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index d0bbfed5..c24b03f2 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -121,7 +121,7 @@ if not Config.Multichar then local xPlayer = ESX.GetPlayerFromIdentifier(identifier) - if xPlayer and ESX.Players[xPlayer.playerId] then + if xPlayer then local xPlayerId = xPlayer.playerId local isExist = DoesPlayerExist(xPlayerId --[[@as string]]) From 85881a4fde0e2244dba0dc1d622b9f4faee7e809 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:40:27 +0200 Subject: [PATCH 118/132] refactor(es_extended/server/main): early return --- [core]/es_extended/server/main.lua | 33 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index c24b03f2..2f6be680 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -121,24 +121,25 @@ if not Config.Multichar then local xPlayer = ESX.GetPlayerFromIdentifier(identifier) - if xPlayer then - local xPlayerId = xPlayer.playerId - local isExist = DoesPlayerExist(xPlayerId --[[@as string]]) - - if isExist ~= 0 then - deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) - TriggerEvent('esx:playerDropped', xPlayerId) - ESX.Players[xPlayerId] = nil - Core.playersByIdentifier[identifier] = nil - GlobalState['playerCount'] = GlobalState['playerCount'] - 1 - else - return deferrals.done( - ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) - ) - end + if not xPlayer then + return deferrals.done() + end + + local xPlayerId = xPlayer.playerId + local isExist = DoesPlayerExist(xPlayerId --[[@as string]]) + + if isExist ~= 0 then + deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) + TriggerEvent('esx:playerDropped', xPlayerId) + ESX.Players[xPlayerId] = nil + Core.playersByIdentifier[identifier] = nil + GlobalState['playerCount'] = GlobalState['playerCount'] - 1 + else + return deferrals.done( + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) end - return deferrals.done() end) end From 0c096c634f23fa18e93e01ce925ca863517173ed Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:47:08 +0200 Subject: [PATCH 119/132] refactor(es_extended/server/main): early return & more readable --- [core]/es_extended/server/main.lua | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 2f6be680..f81c13e7 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -125,21 +125,18 @@ if not Config.Multichar then return deferrals.done() end - local xPlayerId = xPlayer.playerId - local isExist = DoesPlayerExist(xPlayerId --[[@as string]]) - - if isExist ~= 0 then - deferrals.update(("[ESX] Cleaning old Player entry for [%s] (player invalid)"):format(identifier)) - TriggerEvent('esx:playerDropped', xPlayerId) - ESX.Players[xPlayerId] = nil - Core.playersByIdentifier[identifier] = nil - GlobalState['playerCount'] = GlobalState['playerCount'] - 1 - else + if DoesPlayerExist(xPlayer.source --[[@as string]]) then return deferrals.done( - ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) - ) + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) end + deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier)) + TriggerEvent('esx:playerDropped', xPlayer.source) + ESX.Players[xPlayer.source] = nil + Core.playersByIdentifier[identifier] = nil + GlobalState['playerCount'] = GlobalState['playerCount'] - 1 + end) end From 5ed6bf3ebd33087336a3e2a834288a592c489e4c Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:52:16 +0200 Subject: [PATCH 120/132] refactor(es_extended/server/main): use wrapper fun for playerDropped --- [core]/es_extended/server/main.lua | 42 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index f81c13e7..a79d16a6 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -67,6 +67,29 @@ local function onPlayerJoined(playerId) end end +---@param playerId number +---@param reason string +local function onPlayerDropped(playerId, reason) + local xPlayer = ESX.GetPlayerFromId(playerId) + + if not xPlayer then + return + end + + TriggerEvent("esx:playerDropped", playerId, reason) + local job = xPlayer.getJob().name + local currentJob = Core.JobsPlayerCount[job] + Core.JobsPlayerCount[job] = ((currentJob and currentJob > 0) and currentJob or 1) - 1 + + GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job] + Core.playersByIdentifier[xPlayer.identifier] = nil + + Core.SavePlayer(xPlayer, function() + GlobalState["playerCount"] = GlobalState["playerCount"] - 1 + ESX.Players[playerId] = nil + end) +end + if Config.Multichar then AddEventHandler("esx:onPlayerJoined", function(src, char, data) while not next(ESX.Jobs) do @@ -322,24 +345,9 @@ AddEventHandler("chatMessage", function(playerId, _, message) end end) +---@param reason string AddEventHandler("playerDropped", function(reason) - local playerId = source - local xPlayer = ESX.GetPlayerFromId(playerId) - - if xPlayer then - TriggerEvent("esx:playerDropped", playerId, reason) - local job = xPlayer.getJob().name - local currentJob = Core.JobsPlayerCount[job] - Core.JobsPlayerCount[job] = ((currentJob and currentJob > 0) and currentJob or 1) - 1 - - GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job] - Core.playersByIdentifier[xPlayer.identifier] = nil - - Core.SavePlayer(xPlayer, function() - GlobalState["playerCount"] = GlobalState["playerCount"] - 1 - ESX.Players[playerId] = nil - end) - end + onPlayerDropped(source --[[@as number]], reason) end) AddEventHandler("esx:playerLoaded", function(_, xPlayer) From f28d29cc39976b772a44d8db64a4cecc47e760db Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:53:19 +0200 Subject: [PATCH 121/132] refactor(es_extended/server/main): call playerDropped wrapper for stale player obj --- [core]/es_extended/server/main.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index a79d16a6..3f0b9338 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -155,11 +155,7 @@ if not Config.Multichar then end deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier)) - TriggerEvent('esx:playerDropped', xPlayer.source) - ESX.Players[xPlayer.source] = nil - Core.playersByIdentifier[identifier] = nil - GlobalState['playerCount'] = GlobalState['playerCount'] - 1 - + onPlayerDropped(xPlayer.source, "esx_stale_player_obj") end) end From d892df079f5c311515d12057283c1c93cb71eb00 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:55:47 +0200 Subject: [PATCH 122/132] resolve deferrals after cleanup --- [core]/es_extended/server/main.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 3f0b9338..51001d9f 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -69,7 +69,7 @@ end ---@param playerId number ---@param reason string -local function onPlayerDropped(playerId, reason) +local function onPlayerDropped(playerId, reason, cb) local xPlayer = ESX.GetPlayerFromId(playerId) if not xPlayer then @@ -84,10 +84,14 @@ local function onPlayerDropped(playerId, reason) GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job] Core.playersByIdentifier[xPlayer.identifier] = nil + local p = promise:new() Core.SavePlayer(xPlayer, function() GlobalState["playerCount"] = GlobalState["playerCount"] - 1 ESX.Players[playerId] = nil + p:resolve() end) + + return Citizen.Await(p) end if Config.Multichar then @@ -156,6 +160,7 @@ if not Config.Multichar then deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier)) onPlayerDropped(xPlayer.source, "esx_stale_player_obj") + deferrals.done() end) end From b28cf8827388f431646f95d91d24064e92e1d169 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 20:57:37 +0200 Subject: [PATCH 123/132] refactor(es_extended/server/main): remove unused event --- [core]/es_extended/server/main.lua | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 51001d9f..69b42fe6 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -388,25 +388,6 @@ AddEventHandler("esx:playerLogout", function(playerId, cb) TriggerClientEvent("esx:onPlayerLogout", playerId) end) -AddEventHandler('esx:playerCrashed', function(identifier, cb) - local xPlayer = ESX.GetPlayerFromIdentifier(identifier) - - if xPlayer then - local playerId = xPlayer.playerId - local isExist = DoesPlayerExist(playerId --[[@as string]]) - - if playerId and isExist ~= 0 then - TriggerEvent('esx:playerDropped', playerId) - GlobalState["playerCount"] = GlobalState["playerCount"] - 1 - Core.playersByIdentifier[identifier] = nil - ESX.Players[playerId] = nil - if cb then - cb() - end - end - end -end) - if not Config.CustomInventory then RegisterNetEvent("esx:updateWeaponAmmo", function(weaponName, ammoCount) local xPlayer = ESX.GetPlayerFromId(source) From 3e0c89d9b20f366b6241240b0b95091566871e6f Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:05:51 +0200 Subject: [PATCH 124/132] feat(es_extended/server/main): add optional cb fun for playerDropped wrapper --- [core]/es_extended/server/main.lua | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/[core]/es_extended/server/main.lua b/[core]/es_extended/server/main.lua index 69b42fe6..9395d9d6 100644 --- a/[core]/es_extended/server/main.lua +++ b/[core]/es_extended/server/main.lua @@ -69,6 +69,7 @@ end ---@param playerId number ---@param reason string +---@param cb function? local function onPlayerDropped(playerId, reason, cb) local xPlayer = ESX.GetPlayerFromId(playerId) @@ -84,15 +85,27 @@ local function onPlayerDropped(playerId, reason, cb) GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job] Core.playersByIdentifier[xPlayer.identifier] = nil - local p = promise:new() + local p = not cb and promise:new() + local function resolve() + if cb then + return cb() + elseif(p) then + return p:resolve() + end + end + Core.SavePlayer(xPlayer, function() GlobalState["playerCount"] = GlobalState["playerCount"] - 1 ESX.Players[playerId] = nil - p:resolve() + resolve() end) - return Citizen.Await(p) + if p then + return Citizen.Await(p) + end end +AddEventHandler("esx:onPlayerDropped", onPlayerDropped) + if Config.Multichar then AddEventHandler("esx:onPlayerJoined", function(src, char, data) @@ -147,7 +160,6 @@ if not Config.Multichar then end local xPlayer = ESX.GetPlayerFromIdentifier(identifier) - if not xPlayer then return deferrals.done() end From 130754dcd2ff014526cd112ff0e0c3eba0215080 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:06:19 +0200 Subject: [PATCH 125/132] refactor(esx_multicharacter/server/modules/functions): properly cleanup stale player obj --- .../server/modules/functions.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index b46dd43a..78ef5516 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -49,12 +49,21 @@ function Server:OnConnecting(source, deferrals) if ESX.GetConfig().EnableDebug or not ESX.Players[identifier] then deferrals.done() end - TriggerEvent('esx:playerCrashed', identifier, function() - ESX.Players[identifier] = nil - deferrals.update(('[ESX] Cleaning old Player entry for [%s] (player invalid)'):format(identifier)) - end) + local xPlayer = ESX.GetPlayerFromIdentifier(identifier) + if not xPlayer then + return deferrals.done() + end - deferrals.done() -- proceed + if DoesPlayerExist(xPlayer.source --[[@as string]]) then + return deferrals.done( + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) + end + + deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier)) + TriggerEvent("esx:onPlayerDropped", xPlayer.source, "esx_stale_player_obj", function() + deferrals.done() + end) end From 4e8da192177c806958ce7b27300e593c3e54314a Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:15:11 +0200 Subject: [PATCH 126/132] refactor(es_extended/server/functions): fix diagnostics --- [core]/es_extended/server/functions.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 9b20c92f..920b25ab 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -387,15 +387,14 @@ end ---@param model string|number ---@param player number ---@param cb function? ----@diagnostic disable-next-line: duplicate-set-field ---@return string? +---@diagnostic disable-next-line: duplicate-set-field function ESX.GetVehicleType(model, player, cb) if cb and not ESX.IsFunctionReference(cb) then error("Invalid callback function") end local promise = not cb and promise.new() - local function resolve(result) if promise then promise:resolve(result) From e43d694c36ddba363c77c591af3047538c45e2cc Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 22:12:37 +0200 Subject: [PATCH 127/132] fix(es_extended/server/modules/onesync): only await net owner if valid player is nearby --- [core]/es_extended/server/modules/onesync.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index 8c5c76d3..c807f4f8 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -110,8 +110,8 @@ function ESX.OneSync.SpawnVehicle(vehicleModel, coords, heading, vehicleProperti end CreateThread(function() + local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) if not vehicleType then - local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) if not xPlayer then return reject("No players found nearby to check vehicle type!") end @@ -125,7 +125,9 @@ function ESX.OneSync.SpawnVehicle(vehicleModel, coords, heading, vehicleProperti local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) local tries = 0 - while not createdVehicle or createdVehicle == 0 or NetworkGetEntityOwner(createdVehicle) == -1 do + while not createdVehicle or createdVehicle == 0 + or (xPlayer and NetworkGetEntityOwner(createdVehicle) == -1) + or (not xPlayer and not DoesEntityExist(createdVehicle)) do Wait(200) tries = tries + 1 if tries > 40 then From ae1a3faa2d98d1c6afedbb384a736ccac9782cc9 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sat, 14 Jun 2025 22:39:09 +0200 Subject: [PATCH 128/132] fix(es_extended/server/modules/onesync): dont await ent ownership if no valid client nearby --- [core]/es_extended/server/modules/onesync.lua | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index c807f4f8..14c09698 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -110,24 +110,21 @@ function ESX.OneSync.SpawnVehicle(vehicleModel, coords, heading, vehicleProperti end CreateThread(function() - local xPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) - if not vehicleType then - if not xPlayer then - return reject("No players found nearby to check vehicle type!") - end - vehicleType = ESX.GetVehicleType(vehicleModel, xPlayer.id) - end + local closestPlayer = ESX.OneSync.GetClosestPlayer(coords, 300) + local closestPlayerFound = next(closestPlayer) ~= nil + + vehicleType = vehicleType or (closestPlayerFound and ESX.GetVehicleType(vehicleModel, closestPlayer.id) or nil) if not vehicleType then - return reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(vehicleModel)) + return reject("No players found nearby to check vehicle type! Alternatively, you can specify the vehicle type manually.") end local createdVehicle = CreateVehicleServerSetter(vehicleModel, vehicleType, coords.x, coords.y, coords.z, heading) local tries = 0 while not createdVehicle or createdVehicle == 0 - or (xPlayer and NetworkGetEntityOwner(createdVehicle) == -1) - or (not xPlayer and not DoesEntityExist(createdVehicle)) do + or (closestPlayerFound and NetworkGetEntityOwner(createdVehicle) == -1) + or (not closestPlayerFound and not DoesEntityExist(createdVehicle)) do Wait(200) tries = tries + 1 if tries > 40 then From 822dbf1dae6f9a59aa02d8e9e78e58cc5cd76c76 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 15 Jun 2025 12:57:17 +0200 Subject: [PATCH 129/132] refactor(esx_multicharacter/server/modules/functions): fix deferral prefix --- [core]/esx_multicharacter/server/modules/functions.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 78ef5516..1d5a81a6 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -30,7 +30,7 @@ function Server:OnConnecting(source, deferrals) -- luacheck: ignore if not SetEntityOrphanMode then - return deferrals.done(("[ESX] ESX Requires a minimum Artifact version of 10188, Please update your server.")) + return deferrals.done(("[ESX Multicharacter] ESX Requires a minimum Artifact version of 10188, Please update your server.")) end if Server.oneSync == "off" or Server.oneSync == "legacy" then @@ -56,11 +56,11 @@ function Server:OnConnecting(source, deferrals) if DoesPlayerExist(xPlayer.source --[[@as string]]) then return deferrals.done( - ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ("[ESX Multicharacter] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) ) end - deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier)) + deferrals.update(("[ESX Multicharacter] Cleaning stale player entry..."):format(identifier)) TriggerEvent("esx:onPlayerDropped", xPlayer.source, "esx_stale_player_obj", function() deferrals.done() end) From 834e6d5cbae2afc86d866b2e389beaca4708d0f5 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 15 Jun 2025 13:02:47 +0200 Subject: [PATCH 130/132] fix(esx_multicharacter/server/modules/functions): store formatted identifier --- [core]/esx_multicharacter/server/modules/functions.lua | 10 ++++++++-- .../server/modules/multicharacter.lua | 10 ++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 1d5a81a6..8f9ccd4c 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -16,7 +16,7 @@ function Server:ResetPlayers() table.wipe(ESX.Players) for _, v in pairs(players) do - ESX.Players[self:GetIdentifier(v.source)] = true + ESX.Players[self:GetIdentifier(v.source)] = v.identifier end else ESX.Players = {} @@ -49,7 +49,13 @@ function Server:OnConnecting(source, deferrals) if ESX.GetConfig().EnableDebug or not ESX.Players[identifier] then deferrals.done() end - local xPlayer = ESX.GetPlayerFromIdentifier(identifier) + if ESX.Players[identifier] == true then + return deferrals.done( + ("[ESX Multicharacter] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) + end + + local xPlayer = ESX.GetPlayerFromIdentifier(ESX.Players[identifier]) if not xPlayer then return deferrals.done() end diff --git a/[core]/esx_multicharacter/server/modules/multicharacter.lua b/[core]/esx_multicharacter/server/modules/multicharacter.lua index c43be581..25165c3b 100644 --- a/[core]/esx_multicharacter/server/modules/multicharacter.lua +++ b/[core]/esx_multicharacter/server/modules/multicharacter.lua @@ -78,18 +78,20 @@ function Multicharacter:CharacterChosen(source, charid, isNew) end end - TriggerEvent("esx:onPlayerJoined", source, ("%s%s"):format(Server.prefix, charid)) - ESX.Players[Server:GetIdentifier(source)] = true + local charIdentifier = ("%s%s"):format(Server.prefix, charid) + TriggerEvent("esx:onPlayerJoined", source, charIdentifier) + ESX.Players[Server:GetIdentifier(source)] = charIdentifier end end function Multicharacter:RegistrationComplete(source, data) local charId = self.awaitingRegistration[source] + local charIdentifier = ("%s%s"):format(Server.prefix, charId) self.awaitingRegistration[source] = nil - ESX.Players[Server:GetIdentifier(source)] = true + ESX.Players[Server:GetIdentifier(source)] = charIdentifier SetPlayerRoutingBucket(source, 0) - TriggerEvent("esx:onPlayerJoined", source, ("%s%s"):format(Server.prefix, charId), data) + TriggerEvent("esx:onPlayerJoined", source, charIdentifier, data) end function Multicharacter:PlayerDropped(player) From e524ed13af1c7eadea575595de001918359cddb0 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 15 Jun 2025 13:34:18 +0200 Subject: [PATCH 131/132] fix(esx_multicharacter/server/modules/functions): mark palyer as connected on cleanup --- [core]/esx_multicharacter/server/modules/functions.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 8f9ccd4c..6afd966e 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -68,6 +68,7 @@ function Server:OnConnecting(source, deferrals) deferrals.update(("[ESX Multicharacter] Cleaning stale player entry..."):format(identifier)) TriggerEvent("esx:onPlayerDropped", xPlayer.source, "esx_stale_player_obj", function() + ESX.Players[identifier] = true deferrals.done() end) end From e1597110dbb8c10c426e05ae861eb386510e1ce8 Mon Sep 17 00:00:00 2001 From: Kenshin13 <63159154+Kenshiin13@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:05:10 +0200 Subject: [PATCH 132/132] fix(esx_multicharacter/server/modules/functions): build proper identifier --- [core]/esx_multicharacter/server/modules/functions.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/[core]/esx_multicharacter/server/modules/functions.lua b/[core]/esx_multicharacter/server/modules/functions.lua index 6afd966e..a5578575 100644 --- a/[core]/esx_multicharacter/server/modules/functions.lua +++ b/[core]/esx_multicharacter/server/modules/functions.lua @@ -47,7 +47,10 @@ function Server:OnConnecting(source, deferrals) if not identifier then return deferrals.done(("[ESX Multicharacter] Unable to retrieve player identifier.\nIdentifier type: %s"):format(Server.identifierType)) end - if ESX.GetConfig().EnableDebug or not ESX.Players[identifier] then deferrals.done() end + if ESX.GetConfig().EnableDebug or not ESX.Players[identifier] then + ESX.Players[identifier] = true + return deferrals.done() + end if ESX.Players[identifier] == true then return deferrals.done( @@ -55,8 +58,9 @@ function Server:OnConnecting(source, deferrals) ) end - local xPlayer = ESX.GetPlayerFromIdentifier(ESX.Players[identifier]) + local xPlayer = ESX.GetPlayerFromIdentifier(("%s:%s"):format(ESX.Players[identifier], identifier)) if not xPlayer then + ESX.Players[identifier] = true return deferrals.done() end