mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-29 01:08:54 +00:00
Merge pull request #1398 from esx-framework/dev
📦 1.10.8 Release package
This commit is contained in:
@@ -28,7 +28,7 @@ TriggerEvent('cron:runAt', 18, 30, CronTask)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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/>.
|
||||
@@ -24,19 +24,38 @@ function ESX.GetPlayerData()
|
||||
return ESX.PlayerData
|
||||
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)
|
||||
items = type(items) == "string" and { items } or items
|
||||
local item
|
||||
if type(items) == 'string' then
|
||||
item, items = items, {items}
|
||||
end
|
||||
|
||||
local data = {}
|
||||
for i = 1, #items do
|
||||
for c = 1, #ESX.PlayerData.inventory do
|
||||
if ESX.PlayerData.inventory[c].name == items[i] then
|
||||
data[items[i]] = (count and ESX.PlayerData.inventory[c].count) or ESX.PlayerData.inventory[c]
|
||||
for i = 1, #ESX.PlayerData.inventory do
|
||||
local e = ESX.PlayerData.inventory[i]
|
||||
for ii = 1, #items do
|
||||
if e.name == items[ii] then
|
||||
data[table.remove(items, ii)] = count and e.count or e
|
||||
break
|
||||
end
|
||||
end
|
||||
if #items == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return #items == 1 and data[items[1]] or data
|
||||
return not item and data or data[item]
|
||||
end
|
||||
|
||||
function ESX.SetPlayerData(key, val)
|
||||
@@ -49,62 +68,39 @@ function ESX.SetPlayerData(key, val)
|
||||
end
|
||||
end
|
||||
|
||||
function ESX.Progressbar(message, length, Options)
|
||||
if GetResourceState("esx_progressbar") ~= "missing" then
|
||||
return exports["esx_progressbar"]:Progressbar(message, length, Options)
|
||||
end
|
||||
|
||||
print("[^1ERROR^7] ^5ESX Progressbar^7 is Missing!")
|
||||
function ESX.Progressbar(...)
|
||||
return IsResourceFound('esx_progressbar') and exports['esx_progressbar']:Progressbar(...)
|
||||
end
|
||||
|
||||
function ESX.ShowNotification(message, notifyType, length)
|
||||
if GetResourceState("esx_notify") ~= "missing" then
|
||||
return exports["esx_notify"]:Notify(notifyType, length, message)
|
||||
end
|
||||
|
||||
print("[^1ERROR^7] ^5ESX Notify^7 is Missing!")
|
||||
return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message)
|
||||
end
|
||||
|
||||
function ESX.TextUI(message, notifyType)
|
||||
if GetResourceState("esx_textui") ~= "missing" then
|
||||
return exports["esx_textui"]:TextUI(message, notifyType)
|
||||
end
|
||||
|
||||
print("[^1ERROR^7] ^5ESX TextUI^7 is Missing!")
|
||||
function ESX.TextUI(...)
|
||||
return IsResourceFound('esx_textui') and exports['esx_textui']:TextUI(...)
|
||||
end
|
||||
|
||||
function ESX.HideUI()
|
||||
if GetResourceState("esx_textui") ~= "missing" then
|
||||
return exports["esx_textui"]:HideUI()
|
||||
end
|
||||
|
||||
print("[^1ERROR^7] ^5ESX TextUI^7 is Missing!")
|
||||
return IsResourceFound('esx_textui') and exports['esx_textui']:HideUI()
|
||||
end
|
||||
|
||||
function ESX.ShowAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
|
||||
if saveToBrief == nil then
|
||||
saveToBrief = true
|
||||
end
|
||||
AddTextEntry("esxAdvancedNotification", msg)
|
||||
BeginTextCommandThefeedPost("esxAdvancedNotification")
|
||||
if hudColorIndex then
|
||||
ThefeedSetNextPostBackgroundColor(hudColorIndex)
|
||||
end
|
||||
EndTextCommandThefeedPostMessagetext(textureDict, textureDict, false, iconType, sender, subject)
|
||||
EndTextCommandThefeedPostTicker(flash or false, saveToBrief)
|
||||
EndTextCommandThefeedPostTicker(flash, saveToBrief == nil or saveToBrief)
|
||||
end
|
||||
|
||||
function ESX.ShowHelpNotification(msg, thisFrame, beep, duration)
|
||||
AddTextEntry("esxHelpNotification", msg)
|
||||
|
||||
if thisFrame then
|
||||
DisplayHelpTextThisFrame("esxHelpNotification", false)
|
||||
DisplayHelpTextThisFrame("esxHelpNotification")
|
||||
else
|
||||
if beep == nil then
|
||||
beep = true
|
||||
end
|
||||
BeginTextCommandDisplayHelp("esxHelpNotification")
|
||||
EndTextCommandDisplayHelp(0, false, beep, duration or -1)
|
||||
EndTextCommandDisplayHelp(0, false, beep == nil or beep, duration or -1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -116,42 +112,40 @@ function ESX.ShowFloatingHelpNotification(msg, coords)
|
||||
EndTextCommandDisplayHelp(2, false, false, -1)
|
||||
end
|
||||
|
||||
ESX.HashString = function(str)
|
||||
local format = string.format
|
||||
local upper = string.upper
|
||||
local gsub = string.gsub
|
||||
local hash = joaat(str)
|
||||
local input_map = format("~INPUT_%s~", upper(format("%x", hash)))
|
||||
input_map = gsub(input_map, "FFFFFFFF", "")
|
||||
|
||||
return input_map
|
||||
function ESX.DrawMissionText(msg, time)
|
||||
ClearPrints()
|
||||
BeginTextCommandPrint('STRING')
|
||||
AddTextComponentSubstringPlayerName(msg)
|
||||
EndTextCommandPrint(time, true)
|
||||
end
|
||||
|
||||
local contextAvailable = GetResourceState("esx_context") ~= "missing"
|
||||
function ESX.HashString(str)
|
||||
return ('~INPUT_%s~'):format(('%x'):format(joaat(str)):upper())
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
ESX.RegisterInput = function(command_name, label, input_group, key, on_press, on_release)
|
||||
RegisterCommand(on_release ~= nil and "+" .. command_name or command_name, on_press)
|
||||
Core.Input[command_name] = on_release ~= nil and ESX.HashString("+" .. command_name) or ESX.HashString(command_name)
|
||||
function ESX.RegisterInput(command_name, label, input_group, key, on_press, on_release)
|
||||
RegisterCommand("+" .. command_name, on_press)
|
||||
Core.Input[command_name] = ESX.HashString("+" .. command_name)
|
||||
if on_release then
|
||||
RegisterCommand("-" .. command_name, on_release)
|
||||
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
|
||||
|
||||
function ESX.UI.Menu.RegisterType(menuType, open, close)
|
||||
@@ -276,9 +270,7 @@ function ESX.UI.Menu.GetOpenedMenus()
|
||||
return ESX.UI.Menu.Opened
|
||||
end
|
||||
|
||||
function ESX.UI.Menu.IsOpen(menuType, namespace, name)
|
||||
return ESX.UI.Menu.GetOpened(menuType, namespace, name) ~= nil
|
||||
end
|
||||
ESX.UI.Menu.IsOpen = ESX.UI.Menu.GetOpened
|
||||
|
||||
function ESX.UI.ShowInventoryItemNotification(add, item, count)
|
||||
SendNUIMessage({
|
||||
@@ -321,18 +313,8 @@ function ESX.Game.Teleport(entity, coords, cb)
|
||||
end
|
||||
|
||||
function ESX.Game.SpawnObject(object, coords, cb, networked)
|
||||
networked = networked == nil and true or networked
|
||||
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
function ESX.Game.SpawnLocalObject(object, coords, cb)
|
||||
@@ -396,10 +378,7 @@ function ESX.Game.SpawnLocalVehicle(vehicle, coords, heading, cb)
|
||||
end
|
||||
|
||||
function ESX.Game.IsVehicleEmpty(vehicle)
|
||||
local passengers = GetVehicleNumberOfPassengers(vehicle)
|
||||
local driverSeatFree = IsVehicleSeatFree(vehicle, -1)
|
||||
|
||||
return passengers == 0 and driverSeatFree
|
||||
return GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1)
|
||||
end
|
||||
|
||||
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
|
||||
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)
|
||||
local closestEntity, closestEntityDistance, filteredEntities = -1, -1, nil
|
||||
|
||||
@@ -527,18 +519,10 @@ function ESX.Game.GetClosestEntity(entities, isPlayerEntities, coords, modelFilt
|
||||
end
|
||||
|
||||
function ESX.Game.GetVehicleInDirection()
|
||||
local playerPed = ESX.PlayerData.ped
|
||||
local playerCoords = GetEntityCoords(playerPed)
|
||||
local inDirection = GetOffsetFromEntityInWorldCoords(playerPed, 0.0, 5.0, 0.0)
|
||||
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
|
||||
local _, hit, coords, _, _, entity = ESX.Game.RaycastScreen(5, 10, ESX.PlayerData.ped)
|
||||
if hit and IsEntityAVehicle(entity) then
|
||||
return entity, coords
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function ESX.Game.GetVehicleProperties(vehicle)
|
||||
@@ -576,15 +560,21 @@ function ESX.Game.GetVehicleProperties(vehicle)
|
||||
end
|
||||
|
||||
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);
|
||||
|
||||
for wheel_index = 0, wheel_count - 1 do
|
||||
tyreBurst[tostring(wheel_index)] = IsVehicleTyreBurst(vehicle, wheel_index, false)
|
||||
if TyresIndex[numWheels] then
|
||||
for _, idx in pairs(TyresIndex[numWheels]) do
|
||||
tyreBurst[tostring(idx)] = IsVehicleTyreBurst(vehicle, idx, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for windowId = 0, 7 do -- 13
|
||||
RollUpWindow(vehicle, windowId) --fix when you put the car away with the window down
|
||||
windowsBroken[tostring(windowId)] = not IsVehicleWindowIntact(vehicle, windowId)
|
||||
@@ -1256,20 +1246,11 @@ function ESX.ShowInventory()
|
||||
end)
|
||||
end
|
||||
|
||||
RegisterNetEvent("esx:showNotification")
|
||||
AddEventHandler("esx:showNotification", function(msg, notifyType, length)
|
||||
ESX.ShowNotification(msg, notifyType, length)
|
||||
end)
|
||||
RegisterNetEvent('esx:showNotification', ESX.ShowNotification)
|
||||
|
||||
RegisterNetEvent("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:showAdvancedNotification', ESX.ShowAdvancedNotification)
|
||||
|
||||
RegisterNetEvent("esx:showHelpNotification")
|
||||
AddEventHandler("esx:showHelpNotification", function(msg, thisFrame, beep, duration)
|
||||
ESX.ShowHelpNotification(msg, thisFrame, beep, duration)
|
||||
end)
|
||||
RegisterNetEvent('esx:showHelpNotification', ESX.ShowHelpNotification)
|
||||
|
||||
AddEventHandler("onResourceStop", function(resourceName)
|
||||
for i = 1, #ESX.UI.Menu.Opened, 1 do
|
||||
|
||||
@@ -25,14 +25,19 @@ function ESX.SpawnPlayer(skin, coords, cb)
|
||||
end)
|
||||
Citizen.Await(p)
|
||||
|
||||
RequestCollisionAtCoord(coords.x, coords.y, coords.z)
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
local timer = GetGameTimer()
|
||||
|
||||
FreezeEntityPosition(playerPed, true)
|
||||
SetEntityCoordsNoOffset(playerPed, coords.x, coords.y, coords.z, false, false, false, true)
|
||||
SetEntityHeading(playerPed, coords.heading)
|
||||
while not HasCollisionLoadedAroundEntity(playerPed) do
|
||||
|
||||
while not HasCollisionLoadedAroundEntity(playerPed) and (GetGameTimer() - timer) < 5000 do
|
||||
Wait(0)
|
||||
end
|
||||
FreezeEntityPosition(playerPed, false)
|
||||
|
||||
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, coords.heading, true, true, false)
|
||||
TriggerEvent('playerSpawned', coords)
|
||||
cb()
|
||||
@@ -140,6 +145,7 @@ AddEventHandler("esx:playerLoaded", function(xPlayer, _, skin)
|
||||
for i = 1, 15 do
|
||||
EnableDispatchService(i, false)
|
||||
end
|
||||
SetAudioFlag('PoliceScannerDisabled', true)
|
||||
end
|
||||
|
||||
-- Disable Scenarios
|
||||
@@ -203,6 +209,10 @@ AddEventHandler("esx:playerLoaded", function(xPlayer, _, skin)
|
||||
end
|
||||
end
|
||||
|
||||
if not Config.Multichar then
|
||||
FreezeEntityPosition(ESX.PlayerData.ped, false)
|
||||
end
|
||||
|
||||
if IsScreenFadedOut() then
|
||||
DoScreenFadeIn(500)
|
||||
end
|
||||
@@ -709,6 +719,6 @@ ESX.RegisterClientCallback("esx:GetVehicleType", function(cb, model)
|
||||
cb(ESX.GetVehicleType(model))
|
||||
end)
|
||||
|
||||
AddStateBagChangeHandler("metadata", "player:" .. tostring(GetPlayerServerId(PlayerId())), function(_, key, val)
|
||||
ESX.SetPlayerData(key, val)
|
||||
end)
|
||||
RegisterNetEvent('esx:updatePlayerData', function(key, val)
|
||||
ESX.SetPlayerData(key, val)
|
||||
end)
|
||||
@@ -58,7 +58,7 @@ CreateThread(function()
|
||||
inPauseMenu = false
|
||||
TriggerEvent("esx:pauseMenuActive", inPauseMenu)
|
||||
end
|
||||
|
||||
|
||||
if not isInVehicle and not IsPlayerDead(PlayerId()) then
|
||||
if DoesEntityExist(GetVehiclePedIsTryingToEnter(playerPed)) and not isEnteringVehicle then
|
||||
-- trying to enter a vehicle!
|
||||
@@ -89,7 +89,7 @@ CreateThread(function()
|
||||
ToggleVehicleStatus(current.vehicle, current.seat)
|
||||
end
|
||||
elseif isInVehicle then
|
||||
if not IsPedInAnyVehicle(playerPed, false) or IsPlayerDead(PlayerId()) then
|
||||
if (current.vehicle ~= GetVehiclePedIsUsing(playerPed)) or IsPlayerDead(PlayerId()) then
|
||||
-- bye, vehicle
|
||||
TriggerEvent("esx:exitedVehicle", current.vehicle, current.plate, current.seat, current.displayName, current.netId)
|
||||
TriggerServerEvent("esx:exitedVehicle", current.plate, current.seat, current.displayName, current.netId)
|
||||
|
||||
@@ -1,85 +1,37 @@
|
||||
function ESX.Streaming.RequestModel(modelHash, cb)
|
||||
modelHash = (type(modelHash) == "number" and modelHash or joaat(modelHash))
|
||||
|
||||
if not HasModelLoaded(modelHash) and IsModelInCdimage(modelHash) then
|
||||
RequestModel(modelHash)
|
||||
|
||||
while not HasModelLoaded(modelHash) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
modelHash = type(modelHash) == "number" and modelHash or joaat(modelHash)
|
||||
if not IsModelInCdimage(modelHash) then return end
|
||||
RequestModel(modelHash)
|
||||
while not HasModelLoaded(modelHash) do Wait() end
|
||||
return cb and cb(modelHash) or modelHash
|
||||
end
|
||||
|
||||
function ESX.Streaming.RequestStreamedTextureDict(textureDict, cb)
|
||||
if not HasStreamedTextureDictLoaded(textureDict) then
|
||||
RequestStreamedTextureDict(textureDict)
|
||||
|
||||
while not HasStreamedTextureDictLoaded(textureDict) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
RequestStreamedTextureDict(textureDict)
|
||||
while not HasStreamedTextureDictLoaded(textureDict) do Wait() end
|
||||
return cb and cb(textureDict) or textureDict
|
||||
end
|
||||
|
||||
function ESX.Streaming.RequestNamedPtfxAsset(assetName, cb)
|
||||
if not HasNamedPtfxAssetLoaded(assetName) then
|
||||
RequestNamedPtfxAsset(assetName)
|
||||
|
||||
while not HasNamedPtfxAssetLoaded(assetName) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
RequestNamedPtfxAsset(assetName)
|
||||
while not HasNamedPtfxAssetLoaded(assetName) do Wait() end
|
||||
return cb and cb(assetName) or assetName
|
||||
end
|
||||
|
||||
function ESX.Streaming.RequestAnimSet(animSet, cb)
|
||||
if not HasAnimSetLoaded(animSet) then
|
||||
RequestAnimSet(animSet)
|
||||
|
||||
while not HasAnimSetLoaded(animSet) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
RequestAnimSet(animSet)
|
||||
while not HasAnimSetLoaded(animSet) do Wait() end
|
||||
return cb and cb(animSet) or animSet
|
||||
end
|
||||
|
||||
function ESX.Streaming.RequestAnimDict(animDict, cb)
|
||||
if not HasAnimDictLoaded(animDict) then
|
||||
RequestAnimDict(animDict)
|
||||
|
||||
while not HasAnimDictLoaded(animDict) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
RequestAnimDict(animDict)
|
||||
while not HasAnimDictLoaded(animDict) do Wait() end
|
||||
return cb and cb(animDict) or animDict
|
||||
end
|
||||
|
||||
function ESX.Streaming.RequestWeaponAsset(weaponHash, cb)
|
||||
if not HasWeaponAssetLoaded(weaponHash) then
|
||||
RequestWeaponAsset(weaponHash)
|
||||
|
||||
while not HasWeaponAssetLoaded(weaponHash) do
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
if cb ~= nil then
|
||||
cb()
|
||||
end
|
||||
RequestWeaponAsset(weaponHash)
|
||||
while not HasWeaponAssetLoaded(weaponHash) do Wait() end
|
||||
return cb and cb(weaponHash) or weaponHash
|
||||
end
|
||||
|
||||
@@ -305,7 +305,7 @@ Locales["de"] = {
|
||||
["component_camo_slide_finish10"] = "BOOM 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_drum"] = "Trommelmagazin",
|
||||
["component_clip_box"] = "Kastenmagazin",
|
||||
|
||||
@@ -52,12 +52,11 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
_ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.license, self.group))
|
||||
|
||||
local stateBag = Player(self.source).state
|
||||
stateBag:set("identifier", self.identifier, true)
|
||||
stateBag:set("license", self.license, true)
|
||||
stateBag:set("identifier", self.identifier, false)
|
||||
stateBag:set("license", self.license, false)
|
||||
stateBag:set("job", self.job, true)
|
||||
stateBag:set("group", self.group, true)
|
||||
stateBag:set("name", self.name, true)
|
||||
stateBag:set("metadata", self.metadata, true)
|
||||
|
||||
---@param eventName string
|
||||
---@param ... any
|
||||
@@ -77,7 +76,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
end
|
||||
|
||||
---@param vector boolean
|
||||
---@param heading boolean
|
||||
---@param heading boolean
|
||||
---@return vector3 | vector4 | table
|
||||
function self.getCoords(vector, heading)
|
||||
local ped <const> = _GetPlayerPed(self.source)
|
||||
@@ -163,7 +162,6 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
---@return void
|
||||
function self.set(k, v)
|
||||
self.variables[k] = v
|
||||
Player(self.source).state:set(k, v, true)
|
||||
end
|
||||
|
||||
---@param k string
|
||||
@@ -191,8 +189,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
---@param account string
|
||||
---@return table | nil
|
||||
function self.getAccount(account)
|
||||
account = string.lower(account)
|
||||
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]
|
||||
end
|
||||
end
|
||||
@@ -476,7 +476,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
local lastJob = self.job
|
||||
|
||||
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
|
||||
|
||||
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][value] = subValue
|
||||
end
|
||||
|
||||
Player(self.source).state:set("metadata", self.metadata, true)
|
||||
self.triggerEvent('esx:updatePlayerData', 'metadata', self.metadata)
|
||||
end
|
||||
|
||||
function self.clearMeta(index, subValues)
|
||||
@@ -874,8 +873,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
else
|
||||
return print(("[^1ERROR^7] xPlayer.clearMeta ^5subValues^7 should be ^5string^7 or ^5table^7, received ^5%s^7!"):format(type(subValues)))
|
||||
end
|
||||
|
||||
Player(self.source).state:set("metadata", self.metadata, true)
|
||||
self.triggerEvent('esx:updatePlayerData', 'metadata', self.metadata)
|
||||
end
|
||||
|
||||
for fnName, fn in pairs(targetOverrides) do
|
||||
|
||||
@@ -129,6 +129,7 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
inventory = {},
|
||||
loadout = {},
|
||||
weight = 0,
|
||||
name = GetPlayerName(playerId),
|
||||
identifier = identifier,
|
||||
firstName = "John",
|
||||
lastName = "Doe",
|
||||
@@ -257,9 +258,12 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
userData.firstName = result.firstname
|
||||
userData.lastName = result.lastname
|
||||
|
||||
local name = ("%s %s"):format(result.firstname, result.lastname)
|
||||
userData.name = name
|
||||
|
||||
xPlayer.set("firstName", result.firstname)
|
||||
xPlayer.set("lastName", result.lastname)
|
||||
xPlayer.setName(("%s %s"):format(result.firstname, result.lastname))
|
||||
xPlayer.setName(name)
|
||||
|
||||
if result.dateofbirth then
|
||||
userData.dateofbirth = result.dateofbirth
|
||||
@@ -302,7 +306,7 @@ end
|
||||
|
||||
AddEventHandler("chatMessage", function(playerId, _, message)
|
||||
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()
|
||||
local commandName = message:sub(1):gmatch("%w+")()
|
||||
xPlayer.showNotification(TranslateCap("commanderror_invalidcommand", commandName))
|
||||
|
||||
@@ -19,18 +19,6 @@ local function doesJobAndGradesExist(name, grades)
|
||||
return true
|
||||
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 job = { name = name, label = label, grades = {} }
|
||||
for _, v in pairs(grades) do
|
||||
@@ -60,43 +48,47 @@ function ESX.CreateJob(name, label, grades)
|
||||
|
||||
if not name or name == '' then
|
||||
notify("ERROR",currentResourceName, 'Missing argument `name`')
|
||||
return
|
||||
return success
|
||||
end
|
||||
|
||||
if not label or label == '' then
|
||||
notify("ERROR",currentResourceName, 'Missing argument `label`')
|
||||
return
|
||||
return success
|
||||
end
|
||||
|
||||
if not grades or not next(grades) then
|
||||
notify("ERROR",currentResourceName, 'Missing argument `grades`')
|
||||
return
|
||||
return success
|
||||
end
|
||||
|
||||
local currentJobExist = doesJobAndGradesExist(name, grades)
|
||||
|
||||
if currentJobExist then
|
||||
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
|
||||
return
|
||||
return success
|
||||
end
|
||||
|
||||
MySQL.insert('INSERT IGNORE INTO jobs (name, label) VALUES (?, ?)', {name, label}, function(jobId)
|
||||
if jobId == nil or jobId == 0 then
|
||||
notify("ERROR",currentResourceName, 'Failed to insert job: `%s`', name)
|
||||
return
|
||||
end
|
||||
local queries = {
|
||||
{ query = 'INSERT INTO jobs (name, label) VALUES (?, ?)', values = { name, label } }
|
||||
}
|
||||
|
||||
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 = results
|
||||
if not results then
|
||||
notify("ERROR",currentResourceName, 'Failed to insert one or more grades for job: `%s`', name)
|
||||
return
|
||||
end
|
||||
success = exports.oxmysql:transaction_async(queries)
|
||||
|
||||
ESX.Jobs[name] = generateNewJobTable(name,label,grades)
|
||||
notify("SUCCESS",currentResourceName, 'Job created successfully: `%s`', name)
|
||||
end)
|
||||
end)
|
||||
if not success then
|
||||
notify("ERROR", currentResourceName, 'Failed to insert one or more grades for job: `%s`', name)
|
||||
return success
|
||||
end
|
||||
|
||||
ESX.Jobs[name] = generateNewJobTable(name, label, grades)
|
||||
|
||||
notify("SUCCESS", currentResourceName, 'Job created successfully: `%s`', name)
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
@@ -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-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.
|
||||
|
||||
|
||||
@@ -12,20 +12,34 @@ local function deleteIdentityFromDatabase(xPlayer)
|
||||
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)
|
||||
if not alreadyRegistered[xPlayer.identifier] then
|
||||
return
|
||||
end
|
||||
|
||||
xPlayer.setName(("%s %s"):format(nil, nil))
|
||||
xPlayer.set("firstName", nil)
|
||||
xPlayer.set("lastName", nil)
|
||||
xPlayer.set("dateofbirth", nil)
|
||||
xPlayer.set("sex", nil)
|
||||
xPlayer.set("height", nil)
|
||||
SetPlayerData(xPlayer, {firstName = nil, lastName = nil, dateOfBirth = nil, sex = nil, height = nil})
|
||||
deleteIdentityFromDatabase(xPlayer)
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
@@ -222,12 +236,8 @@ if Config.UseDeferrals then
|
||||
end
|
||||
|
||||
local currentIdentity = playerIdentity[xPlayer.identifier]
|
||||
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)
|
||||
SetPlayerData(xPlayer, currentIdentity)
|
||||
|
||||
if currentIdentity.saveToDatabase then
|
||||
saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
|
||||
end
|
||||
@@ -243,13 +253,8 @@ else
|
||||
return
|
||||
end
|
||||
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)
|
||||
if currentIdentity.saveToDatabase then
|
||||
saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
|
||||
@@ -340,12 +345,8 @@ else
|
||||
local currentIdentity = playerIdentity[xPlayer.identifier]
|
||||
|
||||
if currentIdentity and alreadyRegistered[xPlayer.identifier] then
|
||||
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)
|
||||
SetPlayerData(xPlayer, currentIdentity)
|
||||
|
||||
TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity)
|
||||
if currentIdentity.saveToDatabase then
|
||||
saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
|
||||
@@ -400,12 +401,8 @@ else
|
||||
|
||||
local currentIdentity = playerIdentity[xPlayer.identifier]
|
||||
|
||||
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)
|
||||
SetPlayerData(xPlayer, currentIdentity)
|
||||
|
||||
TriggerClientEvent("esx_identity:setPlayerData", xPlayer.source, currentIdentity)
|
||||
saveIdentityToDatabase(xPlayer.identifier, currentIdentity)
|
||||
alreadyRegistered[xPlayer.identifier] = true
|
||||
|
||||
@@ -9,7 +9,7 @@ A default List type menu for ESX.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ start esx_menu_dialog
|
||||
### License
|
||||
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.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Advanced menu inputs for ESX
|
||||
### License
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 message any the message :D
|
||||
local function Notify(notificatonType, length, message)
|
||||
@@ -36,6 +36,7 @@ RegisterNetEvent("ESX:Notify", Notify)
|
||||
|
||||
if Debug then
|
||||
RegisterCommand("oldnotify", function()
|
||||
---@diagnostic disable-next-line
|
||||
ESX.ShowNotification("No Waypoint Set.", true, false, 140)
|
||||
end)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ notification = (data) => {
|
||||
const notification = $(`
|
||||
<div class="notify ${data.type}">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ local function Progressbar(message, length, Options)
|
||||
end
|
||||
end
|
||||
if CurrentProgress.FreezePlayer then
|
||||
FreezeEntityPosition(PlayerPedId(), CurrentProgress.FreezePlayer)
|
||||
FreezeEntityPosition(ESX.PlayerData.ped, CurrentProgress.FreezePlayer)
|
||||
end
|
||||
SendNUIMessage({
|
||||
type = "Progressbar",
|
||||
@@ -30,7 +30,7 @@ local function Progressbar(message, length, Options)
|
||||
else
|
||||
ClearPedTasks(ESX.PlayerData.ped)
|
||||
if CurrentProgress.FreezePlayer then
|
||||
FreezeEntityPosition(PlayerPedId(), false)
|
||||
FreezeEntityPosition(ESX.PlayerData.ped, false)
|
||||
end
|
||||
if CurrentProgress.onFinish then
|
||||
CurrentProgress.onFinish()
|
||||
@@ -50,7 +50,7 @@ local function CancelProgressbar()
|
||||
})
|
||||
ClearPedTasks(ESX.PlayerData.ped)
|
||||
if CurrentProgress.FreezePlayer then
|
||||
FreezeEntityPosition(PlayerPedId(), false)
|
||||
FreezeEntityPosition(ESX.PlayerData.ped, false)
|
||||
end
|
||||
if CurrentProgress.onCancel then
|
||||
CurrentProgress.onCancel()
|
||||
|
||||
@@ -63,4 +63,16 @@
|
||||
--Code here
|
||||
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/>.
|
||||
@@ -32,7 +32,7 @@ start esx_skin
|
||||
### License
|
||||
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.
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ end)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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 |
|
||||
------
|
||||
|
||||
|
||||
<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/)
|
||||
|
||||
Reference in New Issue
Block a user