Merge pull request #1398 from esx-framework/dev

📦 1.10.8 Release package
This commit is contained in:
Gellipapa
2024-10-10 18:11:28 +02:00
committed by GitHub
23 changed files with 901 additions and 965 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ TriggerEvent('cron:runAt', 18, 30, CronTask)
cron - run tasks at specific intervals! cron - run tasks at specific intervals!
Copyright (C) 2015-2023 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 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.
+13
View File
@@ -0,0 +1,13 @@
<h1 align='center'>es_extended</a></h1><p align='center'><b><a href='https://discord.esx-framework.org/'>Discord</a> - <a href='https://documentation.esx-framework.org/legacy/installation'>Documentation</a></b></h5>
## Legal
es_extended
Copyright (C) 2015-2024 Jérémie N'gadi
This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version.
This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details.
You should have received a copy Of the GNU General Public License along with this program. If Not, see <http://www.gnu.org/licenses/>.
+84 -103
View File
@@ -24,19 +24,38 @@ function ESX.GetPlayerData()
return ESX.PlayerData return ESX.PlayerData
end end
local addonResourcesState = {
['esx_progressbar'] = GetResourceState('esx_progressbar') ~= 'missing',
['esx_notify'] = GetResourceState('esx_notify') ~= 'missing',
['esx_textui'] = GetResourceState('esx_textui') ~= 'missing',
['esx_context'] = GetResourceState('esx_context') ~= 'missing'
}
local function IsResourceFound(resource)
return addonResourcesState[resource] or print(('[^1ERROR^7] ^5%s^7 is Missing!'):format(resource))
end
function ESX.SearchInventory(items, count) function ESX.SearchInventory(items, count)
items = type(items) == "string" and { items } or items local item
if type(items) == 'string' then
item, items = items, {items}
end
local data = {} local data = {}
for i = 1, #items do for i = 1, #ESX.PlayerData.inventory do
for c = 1, #ESX.PlayerData.inventory do local e = ESX.PlayerData.inventory[i]
if ESX.PlayerData.inventory[c].name == items[i] then for ii = 1, #items do
data[items[i]] = (count and ESX.PlayerData.inventory[c].count) or ESX.PlayerData.inventory[c] if e.name == items[ii] then
data[table.remove(items, ii)] = count and e.count or e
break
end end
end end
if #items == 0 then
break
end
end end
return #items == 1 and data[items[1]] or data return not item and data or data[item]
end end
function ESX.SetPlayerData(key, val) function ESX.SetPlayerData(key, val)
@@ -49,62 +68,39 @@ function ESX.SetPlayerData(key, val)
end end
end end
function ESX.Progressbar(message, length, Options) function ESX.Progressbar(...)
if GetResourceState("esx_progressbar") ~= "missing" then return IsResourceFound('esx_progressbar') and exports['esx_progressbar']:Progressbar(...)
return exports["esx_progressbar"]:Progressbar(message, length, Options)
end
print("[^1ERROR^7] ^5ESX Progressbar^7 is Missing!")
end end
function ESX.ShowNotification(message, notifyType, length) function ESX.ShowNotification(message, notifyType, length)
if GetResourceState("esx_notify") ~= "missing" then return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message)
return exports["esx_notify"]:Notify(notifyType, length, message)
end
print("[^1ERROR^7] ^5ESX Notify^7 is Missing!")
end end
function ESX.TextUI(message, notifyType) function ESX.TextUI(...)
if GetResourceState("esx_textui") ~= "missing" then return IsResourceFound('esx_textui') and exports['esx_textui']:TextUI(...)
return exports["esx_textui"]:TextUI(message, notifyType)
end
print("[^1ERROR^7] ^5ESX TextUI^7 is Missing!")
end end
function ESX.HideUI() function ESX.HideUI()
if GetResourceState("esx_textui") ~= "missing" then return IsResourceFound('esx_textui') and exports['esx_textui']:HideUI()
return exports["esx_textui"]:HideUI()
end
print("[^1ERROR^7] ^5ESX TextUI^7 is Missing!")
end end
function ESX.ShowAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex) function ESX.ShowAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
if saveToBrief == nil then
saveToBrief = true
end
AddTextEntry("esxAdvancedNotification", msg) AddTextEntry("esxAdvancedNotification", msg)
BeginTextCommandThefeedPost("esxAdvancedNotification") BeginTextCommandThefeedPost("esxAdvancedNotification")
if hudColorIndex then if hudColorIndex then
ThefeedSetNextPostBackgroundColor(hudColorIndex) ThefeedSetNextPostBackgroundColor(hudColorIndex)
end end
EndTextCommandThefeedPostMessagetext(textureDict, textureDict, false, iconType, sender, subject) EndTextCommandThefeedPostMessagetext(textureDict, textureDict, false, iconType, sender, subject)
EndTextCommandThefeedPostTicker(flash or false, saveToBrief) EndTextCommandThefeedPostTicker(flash, saveToBrief == nil or saveToBrief)
end end
function ESX.ShowHelpNotification(msg, thisFrame, beep, duration) function ESX.ShowHelpNotification(msg, thisFrame, beep, duration)
AddTextEntry("esxHelpNotification", msg) AddTextEntry("esxHelpNotification", msg)
if thisFrame then if thisFrame then
DisplayHelpTextThisFrame("esxHelpNotification", false) DisplayHelpTextThisFrame("esxHelpNotification")
else else
if beep == nil then
beep = true
end
BeginTextCommandDisplayHelp("esxHelpNotification") BeginTextCommandDisplayHelp("esxHelpNotification")
EndTextCommandDisplayHelp(0, false, beep, duration or -1) EndTextCommandDisplayHelp(0, false, beep == nil or beep, duration or -1)
end end
end end
@@ -116,42 +112,40 @@ function ESX.ShowFloatingHelpNotification(msg, coords)
EndTextCommandDisplayHelp(2, false, false, -1) EndTextCommandDisplayHelp(2, false, false, -1)
end end
ESX.HashString = function(str) function ESX.DrawMissionText(msg, time)
local format = string.format ClearPrints()
local upper = string.upper BeginTextCommandPrint('STRING')
local gsub = string.gsub AddTextComponentSubstringPlayerName(msg)
local hash = joaat(str) EndTextCommandPrint(time, true)
local input_map = format("~INPUT_%s~", upper(format("%x", hash)))
input_map = gsub(input_map, "FFFFFFFF", "")
return input_map
end end
local contextAvailable = GetResourceState("esx_context") ~= "missing" function ESX.HashString(str)
return ('~INPUT_%s~'):format(('%x'):format(joaat(str)):upper())
end
function ESX.OpenContext(...) function ESX.OpenContext(...)
return contextAvailable and exports["esx_context"]:Open(...) or not contextAvailable and print("[^1ERROR^7] Tried to ^5open^7 context menu, but ^5esx_context^7 is missing!") return IsResourceFound('esx_context') and exports['esx_context']:Open(...)
end end
function ESX.PreviewContext(...) function ESX.PreviewContext(...)
return contextAvailable and exports["esx_context"]:Preview(...) or not contextAvailable and print("[^1ERROR^7] Tried to ^5preview^7 context menu, but ^5esx_context^7 is missing!") return IsResourceFound('esx_context') and exports['esx_context']:Preview(...)
end end
function ESX.CloseContext(...) function ESX.CloseContext(...)
return contextAvailable and exports["esx_context"]:Close(...) or not contextAvailable and print("[^1ERROR^7] Tried to ^5close^7 context menu, but ^5esx_context^7 is missing!") return IsResourceFound('esx_context') and exports['esx_context']:Close(...)
end end
function ESX.RefreshContext(...) function ESX.RefreshContext(...)
return contextAvailable and exports["esx_context"]:Refresh(...) or not contextAvailable and print("[^1ERROR^7] Tried to ^5Refresh^7 context menu, but ^5esx_context^7 is missing!") return IsResourceFound('esx_context') and exports['esx_context']:Refresh(...)
end end
ESX.RegisterInput = function(command_name, label, input_group, key, on_press, on_release) function ESX.RegisterInput(command_name, label, input_group, key, on_press, on_release)
RegisterCommand(on_release ~= nil and "+" .. command_name or command_name, on_press) RegisterCommand("+" .. command_name, on_press)
Core.Input[command_name] = on_release ~= nil and ESX.HashString("+" .. command_name) or ESX.HashString(command_name) Core.Input[command_name] = ESX.HashString("+" .. command_name)
if on_release then if on_release then
RegisterCommand("-" .. command_name, on_release) RegisterCommand("-" .. command_name, on_release)
end end
RegisterKeyMapping(on_release ~= nil and "+" .. command_name or command_name, label, input_group, key) RegisterKeyMapping("+" .. command_name, label or '', input_group or 'keyboard', key or '')
end end
function ESX.UI.Menu.RegisterType(menuType, open, close) function ESX.UI.Menu.RegisterType(menuType, open, close)
@@ -276,9 +270,7 @@ function ESX.UI.Menu.GetOpenedMenus()
return ESX.UI.Menu.Opened return ESX.UI.Menu.Opened
end end
function ESX.UI.Menu.IsOpen(menuType, namespace, name) ESX.UI.Menu.IsOpen = ESX.UI.Menu.GetOpened
return ESX.UI.Menu.GetOpened(menuType, namespace, name) ~= nil
end
function ESX.UI.ShowInventoryItemNotification(add, item, count) function ESX.UI.ShowInventoryItemNotification(add, item, count)
SendNUIMessage({ SendNUIMessage({
@@ -321,18 +313,8 @@ function ESX.Game.Teleport(entity, coords, cb)
end end
function ESX.Game.SpawnObject(object, coords, cb, networked) function ESX.Game.SpawnObject(object, coords, cb, networked)
networked = networked == nil and true or networked local obj = CreateObject(ESX.Streaming.RequestModel(object), coords.x, coords.y. coords.z, networked == nil or networked, false, true)
return cb and cb(obj) or obj
local model = type(object) == "number" and object or joaat(object)
local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z)
CreateThread(function()
ESX.Streaming.RequestModel(model)
local obj = CreateObject(model, vector.xyz, networked, false, true)
if cb then
cb(obj)
end
end)
end end
function ESX.Game.SpawnLocalObject(object, coords, cb) function ESX.Game.SpawnLocalObject(object, coords, cb)
@@ -396,10 +378,7 @@ function ESX.Game.SpawnLocalVehicle(vehicle, coords, heading, cb)
end end
function ESX.Game.IsVehicleEmpty(vehicle) function ESX.Game.IsVehicleEmpty(vehicle)
local passengers = GetVehicleNumberOfPassengers(vehicle) return GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1)
local driverSeatFree = IsVehicleSeatFree(vehicle, -1)
return passengers == 0 and driverSeatFree
end end
function ESX.Game.GetObjects() -- Leave the function for compatibility function ESX.Game.GetObjects() -- Leave the function for compatibility
@@ -495,6 +474,19 @@ function ESX.Game.IsSpawnPointClear(coords, maxDistance)
return #ESX.Game.GetVehiclesInArea(coords, maxDistance) == 0 return #ESX.Game.GetVehiclesInArea(coords, maxDistance) == 0
end end
function ESX.Game.GetShapeTestResultSync(shape)
local handle, hit, coords, normal, material, entity
repeat handle, hit, coords, normal, material, entity = GetShapeTestResultIncludingMaterial(shape)
until handle ~= 1 or Wait()
return hit, coords, normal, material, entity
end
function ESX.Game.RaycastScreen(depth, ...)
local world, normal = GetWorldCoordFromScreenCoord(.5, .5)
local target = world + normal * depth
return target, ESX.Game.GetShapeTestResultAsync(StartShapeTestLosProbe(world + normal, target, ...))
end
function ESX.Game.GetClosestEntity(entities, isPlayerEntities, coords, modelFilter) function ESX.Game.GetClosestEntity(entities, isPlayerEntities, coords, modelFilter)
local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil
@@ -527,18 +519,10 @@ function ESX.Game.GetClosestEntity(entities, isPlayerEntities, coords, modelFilt
end end
function ESX.Game.GetVehicleInDirection() function ESX.Game.GetVehicleInDirection()
local playerPed = ESX.PlayerData.ped local _, hit, coords, _, _, entity = ESX.Game.RaycastScreen(5, 10, ESX.PlayerData.ped)
local playerCoords = GetEntityCoords(playerPed) if hit and IsEntityAVehicle(entity) then
local inDirection = GetOffsetFromEntityInWorldCoords(playerPed, 0.0, 5.0, 0.0) return entity, coords
local rayHandle = StartExpensiveSynchronousShapeTestLosProbe(playerCoords, inDirection, 10, playerPed, 0)
local _, hit, _, _, entityHit = GetShapeTestResult(rayHandle)
if hit == 1 and GetEntityType(entityHit) == 2 then
local entityCoords = GetEntityCoords(entityHit)
return entityHit, entityCoords
end end
return nil
end end
function ESX.Game.GetVehicleProperties(vehicle) function ESX.Game.GetVehicleProperties(vehicle)
@@ -576,15 +560,21 @@ function ESX.Game.GetVehicleProperties(vehicle)
end end
local doorsBroken, windowsBroken, tyreBurst = {}, {}, {} local doorsBroken, windowsBroken, tyreBurst = {}, {}, {}
local numWheels = tostring(GetVehicleNumberOfWheels(vehicle))
local TyresIndex = { -- Wheel index list according to the number of vehicle wheels.
["2"] = { 0, 4 }, -- Bike and cycle.
["3"] = { 0, 1, 4, 5 }, -- Vehicle with 3 wheels (get for wheels because some 3 wheels vehicles have 2 wheels on front and one rear or the reverse).
["4"] = { 0, 1, 4, 5 }, -- Vehicle with 4 wheels.
["6"] = { 0, 1, 2, 3, 4, 5 }, -- Vehicle with 6 wheels.
}
local wheel_count = GetVehicleNumberOfWheels(vehicle); if TyresIndex[numWheels] then
for _, idx in pairs(TyresIndex[numWheels]) do
for wheel_index = 0, wheel_count - 1 do tyreBurst[tostring(idx)] = IsVehicleTyreBurst(vehicle, idx, false)
tyreBurst[tostring(wheel_index)] = IsVehicleTyreBurst(vehicle, wheel_index, false) end
end end
for windowId = 0, 7 do -- 13 for windowId = 0, 7 do -- 13
RollUpWindow(vehicle, windowId) --fix when you put the car away with the window down RollUpWindow(vehicle, windowId) --fix when you put the car away with the window down
windowsBroken[tostring(windowId)] = not IsVehicleWindowIntact(vehicle, windowId) windowsBroken[tostring(windowId)] = not IsVehicleWindowIntact(vehicle, windowId)
@@ -1256,20 +1246,11 @@ function ESX.ShowInventory()
end) end)
end end
RegisterNetEvent("esx:showNotification") RegisterNetEvent('esx:showNotification', ESX.ShowNotification)
AddEventHandler("esx:showNotification", function(msg, notifyType, length)
ESX.ShowNotification(msg, notifyType, length)
end)
RegisterNetEvent("esx:showAdvancedNotification") RegisterNetEvent('esx:showAdvancedNotification', ESX.ShowAdvancedNotification)
AddEventHandler("esx:showAdvancedNotification", function(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
ESX.ShowAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
end)
RegisterNetEvent("esx:showHelpNotification") RegisterNetEvent('esx:showHelpNotification', ESX.ShowHelpNotification)
AddEventHandler("esx:showHelpNotification", function(msg, thisFrame, beep, duration)
ESX.ShowHelpNotification(msg, thisFrame, beep, duration)
end)
AddEventHandler("onResourceStop", function(resourceName) AddEventHandler("onResourceStop", function(resourceName)
for i = 1, #ESX.UI.Menu.Opened, 1 do for i = 1, #ESX.UI.Menu.Opened, 1 do
+15 -5
View File
@@ -25,14 +25,19 @@ function ESX.SpawnPlayer(skin, coords, cb)
end) end)
Citizen.Await(p) Citizen.Await(p)
RequestCollisionAtCoord(coords.x, coords.y, coords.z)
local playerPed = PlayerPedId() local playerPed = PlayerPedId()
local timer = GetGameTimer()
FreezeEntityPosition(playerPed, true) FreezeEntityPosition(playerPed, true)
SetEntityCoordsNoOffset(playerPed, coords.x, coords.y, coords.z, false, false, false, true) SetEntityCoordsNoOffset(playerPed, coords.x, coords.y, coords.z, false, false, false, true)
SetEntityHeading(playerPed, coords.heading) SetEntityHeading(playerPed, coords.heading)
while not HasCollisionLoadedAroundEntity(playerPed) do
while not HasCollisionLoadedAroundEntity(playerPed) and (GetGameTimer() - timer) < 5000 do
Wait(0) Wait(0)
end end
FreezeEntityPosition(playerPed, false)
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, coords.heading, true, true, false) NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, coords.heading, true, true, false)
TriggerEvent('playerSpawned', coords) TriggerEvent('playerSpawned', coords)
cb() cb()
@@ -140,6 +145,7 @@ AddEventHandler("esx:playerLoaded", function(xPlayer, _, skin)
for i = 1, 15 do for i = 1, 15 do
EnableDispatchService(i, false) EnableDispatchService(i, false)
end end
SetAudioFlag('PoliceScannerDisabled', true)
end end
-- Disable Scenarios -- Disable Scenarios
@@ -203,6 +209,10 @@ AddEventHandler("esx:playerLoaded", function(xPlayer, _, skin)
end end
end end
if not Config.Multichar then
FreezeEntityPosition(ESX.PlayerData.ped, false)
end
if IsScreenFadedOut() then if IsScreenFadedOut() then
DoScreenFadeIn(500) DoScreenFadeIn(500)
end end
@@ -709,6 +719,6 @@ ESX.RegisterClientCallback("esx:GetVehicleType", function(cb, model)
cb(ESX.GetVehicleType(model)) cb(ESX.GetVehicleType(model))
end) end)
AddStateBagChangeHandler("metadata", "player:" .. tostring(GetPlayerServerId(PlayerId())), function(_, key, val) RegisterNetEvent('esx:updatePlayerData', function(key, val)
ESX.SetPlayerData(key, val) ESX.SetPlayerData(key, val)
end) end)
@@ -58,7 +58,7 @@ CreateThread(function()
inPauseMenu = false inPauseMenu = false
TriggerEvent("esx:pauseMenuActive", inPauseMenu) TriggerEvent("esx:pauseMenuActive", inPauseMenu)
end end
if not isInVehicle and not IsPlayerDead(PlayerId()) then if not isInVehicle and not IsPlayerDead(PlayerId()) then
if DoesEntityExist(GetVehiclePedIsTryingToEnter(playerPed)) and not isEnteringVehicle then if DoesEntityExist(GetVehiclePedIsTryingToEnter(playerPed)) and not isEnteringVehicle then
-- trying to enter a vehicle! -- trying to enter a vehicle!
@@ -89,7 +89,7 @@ CreateThread(function()
ToggleVehicleStatus(current.vehicle, current.seat) ToggleVehicleStatus(current.vehicle, current.seat)
end end
elseif isInVehicle then elseif isInVehicle then
if not IsPedInAnyVehicle(playerPed, false) or IsPlayerDead(PlayerId()) then if (current.vehicle ~= GetVehiclePedIsUsing(playerPed)) or IsPlayerDead(PlayerId()) then
-- bye, vehicle -- bye, vehicle
TriggerEvent("esx:exitedVehicle", current.vehicle, current.plate, current.seat, current.displayName, current.netId) TriggerEvent("esx:exitedVehicle", current.vehicle, current.plate, current.seat, current.displayName, current.netId)
TriggerServerEvent("esx:exitedVehicle", current.plate, current.seat, current.displayName, current.netId) TriggerServerEvent("esx:exitedVehicle", current.plate, current.seat, current.displayName, current.netId)
+20 -68
View File
@@ -1,85 +1,37 @@
function ESX.Streaming.RequestModel(modelHash, cb) function ESX.Streaming.RequestModel(modelHash, cb)
modelHash = (type(modelHash) == "number" and modelHash or joaat(modelHash)) modelHash = type(modelHash) == "number" and modelHash or joaat(modelHash)
if not IsModelInCdimage(modelHash) then return end
if not HasModelLoaded(modelHash) and IsModelInCdimage(modelHash) then RequestModel(modelHash)
RequestModel(modelHash) while not HasModelLoaded(modelHash) do Wait() end
return cb and cb(modelHash) or modelHash
while not HasModelLoaded(modelHash) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
function ESX.Streaming.RequestStreamedTextureDict(textureDict, cb) function ESX.Streaming.RequestStreamedTextureDict(textureDict, cb)
if not HasStreamedTextureDictLoaded(textureDict) then RequestStreamedTextureDict(textureDict)
RequestStreamedTextureDict(textureDict) while not HasStreamedTextureDictLoaded(textureDict) do Wait() end
return cb and cb(textureDict) or textureDict
while not HasStreamedTextureDictLoaded(textureDict) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
function ESX.Streaming.RequestNamedPtfxAsset(assetName, cb) function ESX.Streaming.RequestNamedPtfxAsset(assetName, cb)
if not HasNamedPtfxAssetLoaded(assetName) then RequestNamedPtfxAsset(assetName)
RequestNamedPtfxAsset(assetName) while not HasNamedPtfxAssetLoaded(assetName) do Wait() end
return cb and cb(assetName) or assetName
while not HasNamedPtfxAssetLoaded(assetName) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
function ESX.Streaming.RequestAnimSet(animSet, cb) function ESX.Streaming.RequestAnimSet(animSet, cb)
if not HasAnimSetLoaded(animSet) then RequestAnimSet(animSet)
RequestAnimSet(animSet) while not HasAnimSetLoaded(animSet) do Wait() end
return cb and cb(animSet) or animSet
while not HasAnimSetLoaded(animSet) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
function ESX.Streaming.RequestAnimDict(animDict, cb) function ESX.Streaming.RequestAnimDict(animDict, cb)
if not HasAnimDictLoaded(animDict) then RequestAnimDict(animDict)
RequestAnimDict(animDict) while not HasAnimDictLoaded(animDict) do Wait() end
return cb and cb(animDict) or animDict
while not HasAnimDictLoaded(animDict) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
function ESX.Streaming.RequestWeaponAsset(weaponHash, cb) function ESX.Streaming.RequestWeaponAsset(weaponHash, cb)
if not HasWeaponAssetLoaded(weaponHash) then RequestWeaponAsset(weaponHash)
RequestWeaponAsset(weaponHash) while not HasWeaponAssetLoaded(weaponHash) do Wait() end
return cb and cb(weaponHash) or weaponHash
while not HasWeaponAssetLoaded(weaponHash) do
Wait(0)
end
end
if cb ~= nil then
cb()
end
end end
+1 -1
View File
@@ -305,7 +305,7 @@ Locales["de"] = {
["component_camo_slide_finish10"] = "BOOM Rutschen Camouflage", ["component_camo_slide_finish10"] = "BOOM Rutschen Camouflage",
["component_camo_slide_finish11"] = "Patriotisch Rutschen Camouflage", ["component_camo_slide_finish11"] = "Patriotisch Rutschen Camouflage",
["component_clip_default"] = "Standart Magazin", ["component_clip_default"] = "Standard Magazin",
["component_clip_extended"] = "Erweiterters Magazin", ["component_clip_extended"] = "Erweiterters Magazin",
["component_clip_drum"] = "Trommelmagazin", ["component_clip_drum"] = "Trommelmagazin",
["component_clip_box"] = "Kastenmagazin", ["component_clip_box"] = "Kastenmagazin",
+9 -11
View File
@@ -52,12 +52,11 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
_ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.license, self.group)) _ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.license, self.group))
local stateBag = Player(self.source).state local stateBag = Player(self.source).state
stateBag:set("identifier", self.identifier, true) stateBag:set("identifier", self.identifier, false)
stateBag:set("license", self.license, true) stateBag:set("license", self.license, false)
stateBag:set("job", self.job, true) stateBag:set("job", self.job, true)
stateBag:set("group", self.group, true) stateBag:set("group", self.group, true)
stateBag:set("name", self.name, true) stateBag:set("name", self.name, true)
stateBag:set("metadata", self.metadata, true)
---@param eventName string ---@param eventName string
---@param ... any ---@param ... any
@@ -77,7 +76,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
end end
---@param vector boolean ---@param vector boolean
---@param heading boolean ---@param heading boolean
---@return vector3 | vector4 | table ---@return vector3 | vector4 | table
function self.getCoords(vector, heading) function self.getCoords(vector, heading)
local ped <const> = _GetPlayerPed(self.source) local ped <const> = _GetPlayerPed(self.source)
@@ -163,7 +162,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
---@return void ---@return void
function self.set(k, v) function self.set(k, v)
self.variables[k] = v self.variables[k] = v
Player(self.source).state:set(k, v, true)
end end
---@param k string ---@param k string
@@ -191,8 +189,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
---@param account string ---@param account string
---@return table | nil ---@return table | nil
function self.getAccount(account) function self.getAccount(account)
account = string.lower(account)
for i = 1, #self.accounts do for i = 1, #self.accounts do
if self.accounts[i].name == account then local accountName = string.lower(self.accounts[i].name)
if accountName == account then
return self.accounts[i] return self.accounts[i]
end end
end end
@@ -476,7 +476,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
local lastJob = self.job local lastJob = self.job
if not ESX.DoesJobExist(newJob, grade) then if not ESX.DoesJobExist(newJob, grade) then
return print(("[es_extended] [^3WARNING^7] Ignoring invalid ^5.setJob()^7 usage for ID: ^5%s^7, Job: ^5%s^7"):format(self.source, job)) return print(("[es_extended] [^3WARNING^7] Ignoring invalid ^5.setJob()^7 usage for ID: ^5%s^7, Job: ^5%s^7"):format(self.source, newJob))
end end
local jobObject, gradeObject = ESX.Jobs[newJob], ESX.Jobs[newJob].grades[grade] local jobObject, gradeObject = ESX.Jobs[newJob], ESX.Jobs[newJob].grades[grade]
@@ -829,8 +829,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
self.metadata[index] = type(self.metadata[index]) == "table" and self.metadata[index] or {} self.metadata[index] = type(self.metadata[index]) == "table" and self.metadata[index] or {}
self.metadata[index][value] = subValue self.metadata[index][value] = subValue
end end
self.triggerEvent('esx:updatePlayerData', 'metadata', self.metadata)
Player(self.source).state:set("metadata", self.metadata, true)
end end
function self.clearMeta(index, subValues) function self.clearMeta(index, subValues)
@@ -874,8 +873,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
else else
return print(("[^1ERROR^7] xPlayer.clearMeta ^5subValues^7 should be ^5string^7 or ^5table^7, received ^5%s^7!"):format(type(subValues))) return print(("[^1ERROR^7] xPlayer.clearMeta ^5subValues^7 should be ^5string^7 or ^5table^7, received ^5%s^7!"):format(type(subValues)))
end end
self.triggerEvent('esx:updatePlayerData', 'metadata', self.metadata)
Player(self.source).state:set("metadata", self.metadata, true)
end end
for fnName, fn in pairs(targetOverrides) do for fnName, fn in pairs(targetOverrides) do
+6 -2
View File
@@ -129,6 +129,7 @@ function loadESXPlayer(identifier, playerId, isNew)
inventory = {}, inventory = {},
loadout = {}, loadout = {},
weight = 0, weight = 0,
name = GetPlayerName(playerId),
identifier = identifier, identifier = identifier,
firstName = "John", firstName = "John",
lastName = "Doe", lastName = "Doe",
@@ -257,9 +258,12 @@ function loadESXPlayer(identifier, playerId, isNew)
userData.firstName = result.firstname userData.firstName = result.firstname
userData.lastName = result.lastname userData.lastName = result.lastname
local name = ("%s %s"):format(result.firstname, result.lastname)
userData.name = name
xPlayer.set("firstName", result.firstname) xPlayer.set("firstName", result.firstname)
xPlayer.set("lastName", result.lastname) xPlayer.set("lastName", result.lastname)
xPlayer.setName(("%s %s"):format(result.firstname, result.lastname)) xPlayer.setName(name)
if result.dateofbirth then if result.dateofbirth then
userData.dateofbirth = result.dateofbirth userData.dateofbirth = result.dateofbirth
@@ -302,7 +306,7 @@ end
AddEventHandler("chatMessage", function(playerId, _, message) AddEventHandler("chatMessage", function(playerId, _, message)
local xPlayer = ESX.GetPlayerFromId(playerId) local xPlayer = ESX.GetPlayerFromId(playerId)
if message:sub(1, 1) == "/" and playerId > 0 then if xPlayer and message:sub(1, 1) == "/" and playerId > 0 then
CancelEvent() CancelEvent()
local commandName = message:sub(1):gmatch("%w+")() local commandName = message:sub(1):gmatch("%w+")()
xPlayer.showNotification(TranslateCap("commanderror_invalidcommand", commandName)) xPlayer.showNotification(TranslateCap("commanderror_invalidcommand", commandName))
+24 -32
View File
@@ -19,18 +19,6 @@ local function doesJobAndGradesExist(name, grades)
return true return true
end end
local function generateTransactionQueries(name,grades)
local queries = {}
for _, grade in ipairs(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, '{}', '{}'}
}
end
return queries
end
local function generateNewJobTable(name, label, grades) local function generateNewJobTable(name, label, grades)
local job = { name = name, label = label, grades = {} } local job = { name = name, label = label, grades = {} }
for _, v in pairs(grades) do for _, v in pairs(grades) do
@@ -60,43 +48,47 @@ function ESX.CreateJob(name, label, grades)
if not name or name == '' then if not name or name == '' then
notify("ERROR",currentResourceName, 'Missing argument `name`') notify("ERROR",currentResourceName, 'Missing argument `name`')
return return success
end end
if not label or label == '' then if not label or label == '' then
notify("ERROR",currentResourceName, 'Missing argument `label`') notify("ERROR",currentResourceName, 'Missing argument `label`')
return return success
end end
if not grades or not next(grades) then if not grades or not next(grades) then
notify("ERROR",currentResourceName, 'Missing argument `grades`') notify("ERROR",currentResourceName, 'Missing argument `grades`')
return return success
end end
local currentJobExist = doesJobAndGradesExist(name, grades) local currentJobExist = doesJobAndGradesExist(name, grades)
if currentJobExist then if currentJobExist then
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name) notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
return return success
end end
MySQL.insert('INSERT IGNORE INTO jobs (name, label) VALUES (?, ?)', {name, label}, function(jobId) local queries = {
if jobId == nil or jobId == 0 then { query = 'INSERT INTO jobs (name, label) VALUES (?, ?)', values = { name, label } }
notify("ERROR",currentResourceName, 'Failed to insert job: `%s`', name) }
return
end
local queries = generateTransactionQueries(name, grades) for _, grade in ipairs(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, '{}', '{}' }
}
end
MySQL.transaction(queries, function(results) success = exports.oxmysql:transaction_async(queries)
success = results
if not results then
notify("ERROR",currentResourceName, 'Failed to insert one or more grades for job: `%s`', name)
return
end
ESX.Jobs[name] = generateNewJobTable(name,label,grades) if not success then
notify("SUCCESS",currentResourceName, 'Job created successfully: `%s`', name) notify("ERROR", currentResourceName, 'Failed to insert one or more grades for job: `%s`', name)
end) return success
end) end
ESX.Jobs[name] = generateNewJobTable(name, label, grades)
notify("SUCCESS", currentResourceName, 'Job created successfully: `%s`', name)
return success return success
end end
+1 -1
View File
@@ -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! esx_identity - Make your Character a Person!
Copyright (C) 2015-2023 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 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.
+27 -30
View File
@@ -12,20 +12,34 @@ local function deleteIdentityFromDatabase(xPlayer)
end end
end end
function SetPlayerData(xPlayer, data)
local name = ("%s %s"):format(data.firstName, data.lastName)
xPlayer.setName(name)
xPlayer.set("firstName", data.firstName)
xPlayer.set("lastName", data.lastName)
xPlayer.set("dateofbirth", data.dateOfBirth)
xPlayer.set("sex", data.sex)
xPlayer.set("height", data.height)
local state = Player(xPlayer.source).state
state:set("name", name, true)
state:set("firstName", data.firstName, true)
state:set("lastName", data.lastName, true)
state:set("dateofbirth", data.dateOfBirth, true)
state:set("sex", data.sex, true)
state:set("height", data.height, true)
end
local function deleteIdentity(xPlayer) local function deleteIdentity(xPlayer)
if not alreadyRegistered[xPlayer.identifier] then if not alreadyRegistered[xPlayer.identifier] then
return return
end end
xPlayer.setName(("%s %s"):format(nil, nil)) SetPlayerData(xPlayer, {firstName = nil, lastName = nil, dateOfBirth = nil, sex = nil, height = nil})
xPlayer.set("firstName", nil)
xPlayer.set("lastName", nil)
xPlayer.set("dateofbirth", nil)
xPlayer.set("sex", nil)
xPlayer.set("height", nil)
deleteIdentityFromDatabase(xPlayer) deleteIdentityFromDatabase(xPlayer)
end end
local function saveIdentityToDatabase(identifier, identity) 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 }) 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 end
@@ -222,12 +236,8 @@ if Config.UseDeferrals then
end end
local currentIdentity = playerIdentity[xPlayer.identifier] local currentIdentity = playerIdentity[xPlayer.identifier]
xPlayer.setName(("%s %s"):format(currentIdentity.firstName, currentIdentity.lastName)) SetPlayerData(xPlayer, currentIdentity)
xPlayer.set("firstName", currentIdentity.firstName)
xPlayer.set("lastName", currentIdentity.lastName)
xPlayer.set("dateofbirth", currentIdentity.dateOfBirth)
xPlayer.set("sex", currentIdentity.sex)
xPlayer.set("height", currentIdentity.height)
if currentIdentity.saveToDatabase then if currentIdentity.saveToDatabase then
saveIdentityToDatabase(xPlayer.identifier, currentIdentity) saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
end end
@@ -243,13 +253,8 @@ else
return return
end end
local currentIdentity = playerIdentity[xPlayer.identifier] local currentIdentity = playerIdentity[xPlayer.identifier]
SetPlayerData(xPlayer, currentIdentity)
xPlayer.setName(("%s %s"):format(currentIdentity.firstName, currentIdentity.lastName))
xPlayer.set("firstName", currentIdentity.firstName)
xPlayer.set("lastName", currentIdentity.lastName)
xPlayer.set("dateofbirth", currentIdentity.dateOfBirth)
xPlayer.set("sex", currentIdentity.sex)
xPlayer.set("height", currentIdentity.height)
TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity)
if currentIdentity.saveToDatabase then if currentIdentity.saveToDatabase then
saveIdentityToDatabase(xPlayer.identifier, currentIdentity) saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
@@ -340,12 +345,8 @@ else
local currentIdentity = playerIdentity[xPlayer.identifier] local currentIdentity = playerIdentity[xPlayer.identifier]
if currentIdentity and alreadyRegistered[xPlayer.identifier] then if currentIdentity and alreadyRegistered[xPlayer.identifier] then
xPlayer.setName(("%s %s"):format(currentIdentity.firstName, currentIdentity.lastName)) SetPlayerData(xPlayer, currentIdentity)
xPlayer.set("firstName", currentIdentity.firstName)
xPlayer.set("lastName", currentIdentity.lastName)
xPlayer.set("dateofbirth", currentIdentity.dateOfBirth)
xPlayer.set("sex", currentIdentity.sex)
xPlayer.set("height", currentIdentity.height)
TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity)
if currentIdentity.saveToDatabase then if currentIdentity.saveToDatabase then
saveIdentityToDatabase(xPlayer.identifier, currentIdentity) saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
@@ -400,12 +401,8 @@ else
local currentIdentity = playerIdentity[xPlayer.identifier] local currentIdentity = playerIdentity[xPlayer.identifier]
xPlayer.setName(("%s %s"):format(currentIdentity.firstName, currentIdentity.lastName)) SetPlayerData(xPlayer, currentIdentity)
xPlayer.set("firstName", currentIdentity.firstName)
xPlayer.set("lastName", currentIdentity.lastName)
xPlayer.set("dateofbirth", currentIdentity.dateOfBirth)
xPlayer.set("sex", currentIdentity.sex)
xPlayer.set("height", currentIdentity.height)
TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity) TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity)
saveIdentityToDatabase(xPlayer.identifier, currentIdentity) saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
alreadyRegistered[xPlayer.identifier] = true alreadyRegistered[xPlayer.identifier] = true
+1 -1
View File
@@ -9,7 +9,7 @@ A default List type menu for ESX.
esx_menu_default - Default Menu! esx_menu_default - Default Menu!
Copyright (C) 2015-2023 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 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.
+1 -1
View File
@@ -12,7 +12,7 @@ start esx_menu_dialog
### License ### License
esx_menu_dialog - input dialog for ESX esx_menu_dialog - input dialog for ESX
Copyright (C) 2015-2024 ESX-Framework 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 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.
+1 -1
View File
@@ -5,7 +5,7 @@ Advanced menu inputs for ESX
### License ### License
esx_menu_list - advanced menu inputs for ESX esx_menu_list - advanced menu inputs for ESX
Copyright (C) 2015-2024 ESX-Framework 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 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.
+2 -1
View File
@@ -1,6 +1,6 @@
local Debug = ESX.GetConfig().EnableDebug local Debug = ESX.GetConfig().EnableDebug
---@param type string the notification type ---@param notificatonType string the notification type
---@param length number the length of the notification ---@param length number the length of the notification
---@param message any the message :D ---@param message any the message :D
local function Notify(notificatonType, length, message) local function Notify(notificatonType, length, message)
@@ -36,6 +36,7 @@ RegisterNetEvent("ESX:Notify", Notify)
if Debug then if Debug then
RegisterCommand("oldnotify", function() RegisterCommand("oldnotify", function()
---@diagnostic disable-next-line
ESX.ShowNotification("No Waypoint Set.", true, false, 140) ESX.ShowNotification("No Waypoint Set.", true, false, 140)
end) end)
+1 -1
View File
@@ -60,7 +60,7 @@ notification = (data) => {
const notification = $(` const notification = $(`
<div class="notify ${data.type}"> <div class="notify ${data.type}">
<div class="innerText"> <div class="innerText">
<span class="material-symbols-outlined icon">${types[data.type]["icon"]}</span> <span class="material-symbols-outlined icon">${types[data.type] ? types[data.type]["icon"] : types["info"]["icon"]}</span>
<p class="text">${data["message"]}</p> <p class="text">${data["message"]}</p>
</div> </div>
</div> </div>
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -16,7 +16,7 @@ local function Progressbar(message, length, Options)
end end
end end
if CurrentProgress.FreezePlayer then if CurrentProgress.FreezePlayer then
FreezeEntityPosition(PlayerPedId(), CurrentProgress.FreezePlayer) FreezeEntityPosition(ESX.PlayerData.ped, CurrentProgress.FreezePlayer)
end end
SendNUIMessage({ SendNUIMessage({
type = "Progressbar", type = "Progressbar",
@@ -30,7 +30,7 @@ local function Progressbar(message, length, Options)
else else
ClearPedTasks(ESX.PlayerData.ped) ClearPedTasks(ESX.PlayerData.ped)
if CurrentProgress.FreezePlayer then if CurrentProgress.FreezePlayer then
FreezeEntityPosition(PlayerPedId(), false) FreezeEntityPosition(ESX.PlayerData.ped, false)
end end
if CurrentProgress.onFinish then if CurrentProgress.onFinish then
CurrentProgress.onFinish() CurrentProgress.onFinish()
@@ -50,7 +50,7 @@ local function CancelProgressbar()
}) })
ClearPedTasks(ESX.PlayerData.ped) ClearPedTasks(ESX.PlayerData.ped)
if CurrentProgress.FreezePlayer then if CurrentProgress.FreezePlayer then
FreezeEntityPosition(PlayerPedId(), false) FreezeEntityPosition(ESX.PlayerData.ped, false)
end end
if CurrentProgress.onCancel then if CurrentProgress.onCancel then
CurrentProgress.onCancel() CurrentProgress.onCancel()
+13 -1
View File
@@ -63,4 +63,16 @@
--Code here --Code here
end end
}) })
``` ```
## Legal
esx_progressbar
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 <http://www.gnu.org/licenses/>.
+1 -1
View File
@@ -32,7 +32,7 @@ start esx_skin
### License ### License
esx_skin - skin selector for ESX esx_skin - skin selector for ESX
Copyright (C) 2015-2023 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 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.
+1 -1
View File
@@ -73,7 +73,7 @@ end)
skinchanger - Own your skin! skinchanger - Own your skin!
Copyright (C) 2015-2023 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 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.
-24
View File
@@ -18,29 +18,5 @@ Interested in helping us? [Take a look at our patreon](https://www.patreon.com/e
| CONGRESS KW - Michael Hein - Smery sitbon - daZepelin - CMF Community | | CONGRESS KW - Michael Hein - Smery sitbon - daZepelin - CMF Community |
------ ------
<br> <br>
<table><tr><td><h4 align='center'>Legal Notices</h4></tr></td>
<tr><td>
ESX Core (ESX-legacy)
Copyright (C) 2015-2024 [ESX-Framework](https://github.com/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 <https://www.gnu.org/licenses/>
</td></tr></table>
Powered by [Oxygenserv](https://www.oxygenserv.com/en/) Powered by [Oxygenserv](https://www.oxygenserv.com/en/)