mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-28 17:01:14 +00:00
Merge branch 'v1.14.1' into ox-inv
This commit is contained in:
+18
-31
@@ -16,7 +16,8 @@ INSERT INTO `addon_account` (`name`, `label`, `shared`) VALUES
|
||||
('society_cardealer', 'Cardealer', 1),
|
||||
('society_mechanic', 'Mechanic', 1),
|
||||
('society_police', 'Police', 1),
|
||||
('society_taxi', 'Taxi', 1);
|
||||
('society_taxi', 'Taxi', 1),
|
||||
('bank_savings','Savings account',0);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@@ -89,7 +90,7 @@ CREATE TABLE `billing` (
|
||||
`identifier` varchar(60) NOT NULL,
|
||||
`sender` varchar(60) NOT NULL,
|
||||
`target_type` varchar(50) NOT NULL,
|
||||
`target` varchar(40) NOT NULL,
|
||||
`target` varchar(60) NOT NULL,
|
||||
`label` varchar(255) NOT NULL,
|
||||
`amount` int(11) NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
@@ -364,7 +365,7 @@ CREATE TABLE `rented_vehicles` (
|
||||
`player_name` varchar(255) NOT NULL,
|
||||
`base_price` int(11) NOT NULL,
|
||||
`rent_price` int(11) NOT NULL,
|
||||
`owner` varchar(22) NOT NULL
|
||||
`owner` varchar(60) NOT NULL
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
--
|
||||
@@ -749,7 +750,8 @@ ALTER TABLE `addon_account`
|
||||
ALTER TABLE `addon_account_data`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `index_addon_account_data_account_name_owner` (`account_name`,`owner`),
|
||||
ADD KEY `index_addon_account_data_account_name` (`account_name`);
|
||||
ADD KEY `index_addon_account_data_account_name` (`account_name`),
|
||||
ADD KEY `esx_addon_account_data_owner` (`owner`);
|
||||
|
||||
--
|
||||
-- Indexes for table `addon_inventory`
|
||||
@@ -764,13 +766,15 @@ ALTER TABLE `addon_inventory_items`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `index_addon_inventory_items_inventory_name_name` (`inventory_name`,`name`),
|
||||
ADD KEY `index_addon_inventory_items_inventory_name_name_owner` (`inventory_name`,`name`,`owner`),
|
||||
ADD KEY `index_addon_inventory_inventory_name` (`inventory_name`);
|
||||
ADD KEY `index_addon_inventory_inventory_name` (`inventory_name`),
|
||||
ADD KEY `esx_addon_inventory_items_owner` (`owner`);
|
||||
|
||||
--
|
||||
-- Indexes for table `billing`
|
||||
--
|
||||
ALTER TABLE `billing`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `esx_billing_identifier` (`identifier`);
|
||||
|
||||
--
|
||||
-- Indexes for table `cardealer_vehicles`
|
||||
@@ -790,7 +794,8 @@ ALTER TABLE `datastore`
|
||||
ALTER TABLE `datastore_data`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `index_datastore_data_name_owner` (`name`,`owner`),
|
||||
ADD KEY `index_datastore_data_name` (`name`);
|
||||
ADD KEY `index_datastore_data_name` (`name`),
|
||||
ADD KEY `esx_datastore_data_owner` (`owner`);
|
||||
|
||||
--
|
||||
-- Indexes for table `items`
|
||||
@@ -821,7 +826,8 @@ ALTER TABLE `licenses`
|
||||
-- Indexes for table `owned_vehicles`
|
||||
--
|
||||
ALTER TABLE `owned_vehicles`
|
||||
ADD PRIMARY KEY (`plate`);
|
||||
ADD PRIMARY KEY (`plate`),
|
||||
ADD KEY `esx_owned_vehicles_owner` (`owner`);
|
||||
|
||||
--
|
||||
--
|
||||
@@ -840,7 +846,8 @@ ALTER TABLE `rented_vehicles`
|
||||
-- Indexes for table `society_moneywash`
|
||||
--
|
||||
ALTER TABLE `society_moneywash`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `esx_society_moneywash_identifier` (`identifier`);
|
||||
|
||||
--
|
||||
-- Indexes for table `users`
|
||||
@@ -856,7 +863,8 @@ ALTER TABLE `users`
|
||||
-- Indexes for table `user_licenses`
|
||||
--
|
||||
ALTER TABLE `user_licenses`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `esx_user_licenses_owner` (`owner`);
|
||||
|
||||
--
|
||||
-- Indexes for table `vehicle_categories`
|
||||
@@ -991,27 +999,6 @@ INSERT INTO `fine_types` (label, amount, category) VALUES
|
||||
('Fraud', 2000, 2);
|
||||
|
||||
|
||||
--
|
||||
-- ESX Bankerjob
|
||||
--
|
||||
|
||||
INSERT INTO `addon_account` (name, label, shared) VALUES
|
||||
('society_banker','Bank',1),
|
||||
('bank_savings','Savings account',0)
|
||||
;
|
||||
|
||||
INSERT INTO `jobs` (name, label) VALUES
|
||||
('banker','Banker')
|
||||
;
|
||||
|
||||
INSERT INTO `job_grades` (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
|
||||
('banker',0,'advisor','Consultant',10,'{}','{}'),
|
||||
('banker',1,'banker','Banker',20,'{}','{}'),
|
||||
('banker',2,'business_banker',"Investment banker",30,'{}','{}'),
|
||||
('banker',3,'trader','Broker',40,'{}','{}'),
|
||||
('banker',4,'boss','Boss',0,'{}','{}')
|
||||
;
|
||||
|
||||
--
|
||||
-- ESX Banking
|
||||
--
|
||||
|
||||
@@ -4,6 +4,6 @@ game 'gta5'
|
||||
author 'ESX-Framework'
|
||||
description 'Allows resources to Run tasks at specific intervals.'
|
||||
lua54 'yes'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
server_script 'server/main.lua'
|
||||
|
||||
@@ -26,19 +26,30 @@ end
|
||||
|
||||
---@param timestamp number
|
||||
function OnTime(timestamp)
|
||||
for i = 1, #cronJobs, 1 do
|
||||
local scheduledTimestamp = os.time({
|
||||
hour = cronJobs[i].h,
|
||||
min = cronJobs[i].m,
|
||||
local function scheduledAt(job, dayOffset)
|
||||
return os.time({
|
||||
hour = job.h,
|
||||
min = job.m,
|
||||
sec = 0, -- Assuming tasks run at the start of the minute
|
||||
day = os.date("%d", timestamp),
|
||||
day = os.date("%d", timestamp) + dayOffset,
|
||||
month = os.date("%m", timestamp),
|
||||
year = os.date("%Y", timestamp),
|
||||
})
|
||||
end
|
||||
|
||||
if timestamp >= scheduledTimestamp and (not lastTimestamp or lastTimestamp < scheduledTimestamp) then
|
||||
for i = 1, #cronJobs, 1 do
|
||||
local scheduledTimestamp = scheduledAt(cronJobs[i], 0)
|
||||
|
||||
if scheduledTimestamp > timestamp then
|
||||
scheduledTimestamp = scheduledAt(cronJobs[i], -1)
|
||||
end
|
||||
|
||||
if not lastTimestamp or lastTimestamp < scheduledTimestamp then
|
||||
local d = os.date('*t', scheduledTimestamp).wday
|
||||
cronJobs[i].cb(d, cronJobs[i].h, cronJobs[i].m)
|
||||
|
||||
if not pcall(cronJobs[i].cb, d, cronJobs[i].h, cronJobs[i].m) then
|
||||
print(("[^1ERROR^7] cron job at ^5%02d:%02d^7 errored, skipping it"):format(cronJobs[i].h, cronJobs[i].m))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,9 @@ function Adjustments:DisableNPCDrops()
|
||||
end
|
||||
|
||||
function Adjustments:SeatShuffle()
|
||||
if Config.DisableVehicleSeatShuff then
|
||||
if Config.DisableVehicleSeatShuff and not self.seatShuffleRegistered then
|
||||
self.seatShuffleRegistered = true
|
||||
|
||||
AddEventHandler("esx:enteredVehicle", function(vehicle, _, seat)
|
||||
if seat > -1 then
|
||||
SetPedIntoVehicle(ESX.PlayerData.ped, vehicle, seat)
|
||||
@@ -43,7 +45,7 @@ end
|
||||
|
||||
function Adjustments:AmmoAndVehicleRewards()
|
||||
CreateThread(function()
|
||||
while true do
|
||||
while ESX.PlayerLoaded do
|
||||
if Config.DisableDisplayAmmo then
|
||||
DisplayAmmoThisFrame(false)
|
||||
end
|
||||
@@ -175,11 +177,11 @@ function Adjustments:ReplacePlaceholders(text)
|
||||
local success, result = pcall(cb)
|
||||
|
||||
if not success then
|
||||
error(("Failed to execute placeholder: ^5%s^7\n%s"):format(placeholder, result))
|
||||
print(("[^1ERROR^7] Failed to execute placeholder: ^5%s^7\n%s"):format(placeholder, result))
|
||||
result = "Unknown"
|
||||
end
|
||||
|
||||
text = text:gsub(("{%s}"):format(placeholder), tostring(result))
|
||||
text = text:gsub(("{%s}"):format(placeholder), (tostring(result):gsub("%%", "%%%%")))
|
||||
end
|
||||
return text
|
||||
end
|
||||
@@ -187,7 +189,7 @@ end
|
||||
function Adjustments:DiscordPresence()
|
||||
if Config.DiscordActivity.appId ~= 0 then
|
||||
CreateThread(function()
|
||||
while true do
|
||||
while ESX.PlayerLoaded do
|
||||
SetDiscordAppId(Config.DiscordActivity.appId)
|
||||
SetRichPresence(self:ReplacePlaceholders(Config.DiscordActivity.presence))
|
||||
SetDiscordRichPresenceAsset(Config.DiscordActivity.assetName)
|
||||
@@ -213,7 +215,9 @@ function Adjustments:WantedLevel()
|
||||
end
|
||||
|
||||
function Adjustments:DisableRadio()
|
||||
if Config.RemoveHudComponents[16] then
|
||||
if Config.RemoveHudComponents[16] and not self.disableRadioRegistered then
|
||||
self.disableRadioRegistered = true
|
||||
|
||||
AddEventHandler("esx:enteredVehicle", function(vehicle, plate, seat, displayName, netId)
|
||||
SetVehRadioStation(vehicle,"OFF")
|
||||
SetUserRadioControlEnabled(false)
|
||||
@@ -223,7 +227,7 @@ end
|
||||
|
||||
function Adjustments:Multipliers()
|
||||
CreateThread(function()
|
||||
while true do
|
||||
while ESX.PlayerLoaded do
|
||||
SetPedDensityMultiplierThisFrame(Config.Multipliers.pedDensity)
|
||||
SetScenarioPedDensityMultiplierThisFrame(Config.Multipliers.scenarioPedDensityInterior, Config.Multipliers.scenarioPedDensityExterior)
|
||||
SetAmbientVehicleRangeMultiplierThisFrame(Config.Multipliers.ambientVehicleRange)
|
||||
|
||||
@@ -102,7 +102,7 @@ function ESX.AwaitServerCallback(eventName, ...)
|
||||
|
||||
-- if the server callback takes longer than 15 seconds to respond, reject the promise
|
||||
SetTimeout(15000, function()
|
||||
if p.state == "pending" then
|
||||
if p.state == 0 then
|
||||
p:reject("Server Callback Timed Out")
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -372,7 +372,6 @@ if not Config.CustomInventory then
|
||||
while true do
|
||||
local Sleep = 1500
|
||||
local playerCoords = GetEntityCoords(ESX.PlayerData.ped)
|
||||
local _, closestDistance = ESX.Game.GetClosestPlayer(playerCoords)
|
||||
|
||||
for pickupId, pickup in pairs(pickups) do
|
||||
local distance = #(playerCoords - pickup.coords)
|
||||
@@ -383,6 +382,8 @@ if not Config.CustomInventory then
|
||||
|
||||
if distance < 1 then
|
||||
if IsControlJustReleased(0, 38) then
|
||||
local _, closestDistance = ESX.Game.GetClosestPlayer(playerCoords)
|
||||
|
||||
if IsPedOnFoot(ESX.PlayerData.ped) and (closestDistance == -1 or closestDistance > 3) and not pickup.inRange then
|
||||
pickup.inRange = true
|
||||
|
||||
|
||||
@@ -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.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
shared_scripts {
|
||||
'@esx_lib/imports.lua',
|
||||
|
||||
@@ -33,7 +33,7 @@ function Translate(str, ...) -- Translate string
|
||||
end
|
||||
|
||||
function TranslateCap(str, ...) -- Translate string first char uppercase
|
||||
return _(str, ...):gsub("^%l", string.upper)
|
||||
return (_(str, ...):gsub("^%l", string.upper))
|
||||
end
|
||||
|
||||
_ = Translate
|
||||
|
||||
@@ -15,57 +15,84 @@ local requiredMethods = {
|
||||
}
|
||||
|
||||
---Parses the inventory:accounts convar to build a lookup table.
|
||||
---Uses the same default as ox_inventory: '["money"]'
|
||||
---Uses the same default and JSON format as ox_inventory.
|
||||
---@return table<string, boolean>
|
||||
local function getAccountList()
|
||||
local accounts = {}
|
||||
local convar = GetConvar("inventory:accounts", '["money"]')
|
||||
local ok, list = pcall(json.decode, convar)
|
||||
|
||||
if ok and type(list) == "table" then
|
||||
if not ok or type(list) ~= "table" then
|
||||
error(
|
||||
("[es_extended] Invalid inventory:accounts convar: %s")
|
||||
:format(tostring(convar)),
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
local accounts = {}
|
||||
|
||||
for i = 1, #list do
|
||||
accounts[list[i]] = true
|
||||
end
|
||||
else
|
||||
-- Fallback for non-JSON formats (e.g. "money" or "money black_money")
|
||||
for account in convar:gmatch("[%w_]+") do
|
||||
if account ~= "[]" then
|
||||
local account = list[i]
|
||||
|
||||
if type(account) == "string" and account ~= "" then
|
||||
accounts[account] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return accounts
|
||||
end
|
||||
|
||||
---Creates a proxy table that routes method calls to ox_inventory exports.
|
||||
---Safely resolves an ox_inventory export without executing it.
|
||||
---@param resourceExports table
|
||||
---@param method string
|
||||
---@return function
|
||||
local function resolveExport(resourceExports, method)
|
||||
local ok, exportFn = pcall(function()
|
||||
return resourceExports[method]
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
error(
|
||||
("[es_extended] Missing required ox_inventory export '%s': %s")
|
||||
:format(method, tostring(exportFn)),
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
if type(exportFn) ~= "function" then
|
||||
error(
|
||||
("[es_extended] Invalid ox_inventory export '%s' (expected function, got %s)")
|
||||
:format(method, type(exportFn)),
|
||||
2
|
||||
)
|
||||
end
|
||||
|
||||
return exportFn
|
||||
end
|
||||
|
||||
---Creates a proxy table that routes method calls to public ox_inventory exports.
|
||||
---@return table
|
||||
local function createOxInventoryProxy()
|
||||
local proxy = {}
|
||||
local accounts = getAccountList()
|
||||
local resourceExports = exports.ox_inventory
|
||||
|
||||
print("^3[es_extended] ox_inventory: using direct export proxy (Inventory() module unavailable)^7")
|
||||
print("^3[es_extended] ox_inventory: using direct export proxy (Inventory() interface unavailable or incomplete)^7")
|
||||
|
||||
for i = 1, #requiredMethods do
|
||||
local method = requiredMethods[i]
|
||||
local ok, err = pcall(function()
|
||||
exports.ox_inventory[method](exports.ox_inventory, 0, "test", 0)
|
||||
end)
|
||||
|
||||
if not ok and tostring(err):find("No such export") then
|
||||
print(("^1[es_extended] CRITICAL: exports.ox_inventory.%s is missing^7"):format(method))
|
||||
end
|
||||
local exportFn = resolveExport(resourceExports, method)
|
||||
|
||||
proxy[method] = function(...)
|
||||
return exports.ox_inventory[method](exports.ox_inventory, ...)
|
||||
return exportFn(resourceExports, ...)
|
||||
end
|
||||
end
|
||||
|
||||
proxy.accounts = accounts
|
||||
proxy.accounts = getAccountList()
|
||||
|
||||
return proxy
|
||||
end
|
||||
|
||||
---Checks if a module table contains all required methods.
|
||||
---Checks if an Inventory() module provides everything ESX requires.
|
||||
---@param module table
|
||||
---@return boolean
|
||||
local function isValidModule(module)
|
||||
@@ -79,12 +106,15 @@ local function isValidModule(module)
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.accounts) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---Returns the ox_inventory interface.
|
||||
---Tries the legacy Inventory() export first for backward compatibility,
|
||||
---then falls back to direct export calls.
|
||||
---Uses Inventory() when available, otherwise falls back to public exports.
|
||||
---@return table
|
||||
local function getOxInventory()
|
||||
if OxInventory then
|
||||
|
||||
@@ -505,6 +505,10 @@ function CreateExtendedPlayer(playerId, identifier, ssn, group, accounts, invent
|
||||
if item and count >= 0 then
|
||||
count = ESX.Math.Round(count)
|
||||
|
||||
if count == item.count then
|
||||
return
|
||||
end
|
||||
|
||||
if count > item.count then
|
||||
self.addInventoryItem(item.name, count - item.count)
|
||||
else
|
||||
|
||||
@@ -154,6 +154,7 @@ Core.vehicleClass = {
|
||||
vehicleData.plate = newPlate
|
||||
Core.vehicles[newPlate] = table.clone(vehicleData)
|
||||
Core.vehicles[oldPlate] = nil
|
||||
self.plate = newPlate
|
||||
|
||||
TriggerEvent("esx:changedExtendedVehiclePlate", vehicleData.plate, oldPlate)
|
||||
Wait(0)
|
||||
@@ -167,7 +168,7 @@ Core.vehicleClass = {
|
||||
assert(type(newProps) == "table", "Expected 'props' to be a table")
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -136,7 +136,7 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion)
|
||||
end
|
||||
local merge = table.concat(args, " ")
|
||||
|
||||
newArgs[v.name] = string.sub(merge, length)
|
||||
newArgs[v.name] = string.sub(merge, length + 1)
|
||||
elseif v.type == "coordinate" then
|
||||
local coord = tonumber(args[k]:match("(-?%d+%.?%d*)"))
|
||||
if not coord then
|
||||
@@ -251,6 +251,7 @@ function Core.SavePlayers(cb)
|
||||
local parameters = {}
|
||||
|
||||
for _, xPlayer in pairs(ESX.Players) do
|
||||
if xPlayer.spawned then
|
||||
updateHealthAndArmorInMetadata(xPlayer)
|
||||
parameters[#parameters + 1] = {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
@@ -264,6 +265,11 @@ function Core.SavePlayers(cb)
|
||||
xPlayer.identifier,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if not parameters[1] then
|
||||
return cb and cb()
|
||||
end
|
||||
|
||||
MySQL.prepare(
|
||||
"UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ?, `metadata` = ? WHERE `identifier` = ?",
|
||||
@@ -799,9 +805,15 @@ if not Config.CustomInventory then
|
||||
end
|
||||
|
||||
if #toInsert > 0 then
|
||||
local parameters = {}
|
||||
for i = 1, #toInsert do
|
||||
local row = toInsert[i]
|
||||
parameters[i] = { row.name, row.label, row.weight, row.rare, row.canRemove }
|
||||
end
|
||||
|
||||
MySQL.prepare.await(
|
||||
"INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)",
|
||||
toInsert)
|
||||
parameters)
|
||||
|
||||
for i = 1, #toInsert do
|
||||
local row = toInsert[i]
|
||||
|
||||
@@ -2,6 +2,7 @@ SetMapName("San Andreas")
|
||||
SetGameType("ESX Legacy")
|
||||
|
||||
local oneSyncState = GetConvar("onesync", "off")
|
||||
local isEnhanced = xLib.isEnhanced()
|
||||
local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `ssn` = ?, `group` = ?"
|
||||
local loadPlayer = "SELECT `accounts`, `ssn`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`"
|
||||
|
||||
@@ -151,7 +152,7 @@ if not Config.Multichar then
|
||||
return deferrals.done(("[ESX] ESX Requires a minimum Artifact version of 10188, Please update your server."))
|
||||
end
|
||||
|
||||
if oneSyncState == "off" or oneSyncState == "legacy" then
|
||||
if not isEnhanced and (oneSyncState == "off" or oneSyncState == "legacy") then
|
||||
return deferrals.done(("[ESX] ESX Requires Onesync Infinity to work. This server currently has Onesync set to: %s"):format(oneSyncState))
|
||||
end
|
||||
|
||||
@@ -288,9 +289,9 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
|
||||
local loadout = json.decode(result.loadout)
|
||||
for name, weapon in pairs(loadout) do
|
||||
local label = ESX.GetWeaponLabel(name)
|
||||
local found, label = pcall(ESX.GetWeaponLabel, name)
|
||||
|
||||
if label then
|
||||
if found and label then
|
||||
userData.loadout[#userData.loadout + 1] = {
|
||||
name = name,
|
||||
ammo = weapon.ammo,
|
||||
@@ -307,7 +308,7 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
userData.coords = json.decode(result.position) or Config.DefaultSpawns[ESX.Math.Random(1,#Config.DefaultSpawns)]
|
||||
|
||||
-- Skin
|
||||
userData.skin = (result.skin and result.skin ~= "") and json.decode(result.skin) or { sex = userData.sex == "f" and 1 or 0 }
|
||||
userData.skin = (result.skin and result.skin ~= "") and json.decode(result.skin) or { sex = result.sex == "f" and 1 or 0 }
|
||||
|
||||
-- Metadata
|
||||
userData.metadata = (result.metadata and result.metadata ~= "") and json.decode(result.metadata) or {}
|
||||
@@ -474,6 +475,9 @@ if not Config.CustomInventory then
|
||||
local weaponComponents = ESX.Table.Clone(weapon.components)
|
||||
local weaponTint = weapon.tintIndex
|
||||
|
||||
sourceXPlayer.removeWeapon(itemName)
|
||||
targetXPlayer.addWeapon(itemName, itemCount)
|
||||
|
||||
if weaponTint then
|
||||
targetXPlayer.setWeaponTint(itemName, weaponTint)
|
||||
end
|
||||
@@ -484,9 +488,6 @@ if not Config.CustomInventory then
|
||||
end
|
||||
end
|
||||
|
||||
sourceXPlayer.removeWeapon(itemName)
|
||||
targetXPlayer.addWeapon(itemName, itemCount)
|
||||
|
||||
if weaponObject.ammo and itemCount > 0 then
|
||||
local ammoLabel = weaponObject.ammo.label
|
||||
sourceXPlayer.showNotification(TranslateCap("gave_weapon_withammo", weaponLabel, itemCount, ammoLabel, targetXPlayer.name))
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
local esxVersion = "v1.14.2"
|
||||
|
||||
Core.Migrations = Core.Migrations or {}
|
||||
Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {}
|
||||
|
||||
if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then
|
||||
return
|
||||
end
|
||||
|
||||
---@return boolean restartRequired
|
||||
Core.Migrations[esxVersion].billingTargetLength = function()
|
||||
print("^4[esx_migration:v1.14.2:billingTargetLength]^7 Checking the length of billing.target.")
|
||||
|
||||
local length = MySQL.scalar.await([[
|
||||
SELECT CHARACTER_MAXIMUM_LENGTH
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'billing'
|
||||
AND COLUMN_NAME = 'target'
|
||||
]])
|
||||
|
||||
if not length then
|
||||
print("^4[esx_migration:v1.14.2:billingTargetLength]^7 Column not found, migration not needed.")
|
||||
return false
|
||||
end
|
||||
|
||||
if length >= 60 then
|
||||
print(("^4[esx_migration:v1.14.2:billingTargetLength]^7 Column is already varchar(%s), migration not needed."):format(length))
|
||||
return false
|
||||
end
|
||||
|
||||
print(("^4[esx_migration:v1.14.2:billingTargetLength]^7 Column is varchar(%s), widening it to 60."):format(length))
|
||||
|
||||
MySQL.update.await([[
|
||||
ALTER TABLE `billing`
|
||||
MODIFY `target` VARCHAR(60) NOT NULL
|
||||
]])
|
||||
|
||||
print("^4[esx_migration:v1.14.2:billingTargetLength]^7 Migration complete.")
|
||||
return true
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
local esxVersion = "v1.14.2"
|
||||
|
||||
Core.Migrations = Core.Migrations or {}
|
||||
Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {}
|
||||
|
||||
if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local targets = {
|
||||
{ table = "billing", column = "identifier" },
|
||||
{ table = "owned_vehicles", column = "owner" },
|
||||
{ table = "user_licenses", column = "owner" },
|
||||
{ table = "society_moneywash", column = "identifier" },
|
||||
{ table = "addon_account_data", column = "owner" },
|
||||
{ table = "addon_inventory_items", column = "owner" },
|
||||
{ table = "datastore_data", column = "owner" },
|
||||
}
|
||||
|
||||
---@return boolean restartRequired
|
||||
Core.Migrations[esxVersion].multicharDeleteIndexes = function()
|
||||
print("^4[esx_migration:v1.14.2:multicharDeleteIndexes]^7 Indexing character deletion columns.")
|
||||
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
|
||||
local tableExists = MySQL.scalar.await([[
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
]], { target.table })
|
||||
|
||||
if tableExists ~= 0 then
|
||||
local leadingIndex = MySQL.scalar.await([[
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME = ?
|
||||
AND SEQ_IN_INDEX = 1
|
||||
]], { target.table, target.column })
|
||||
|
||||
if leadingIndex == 0 then
|
||||
MySQL.update.await(("CREATE INDEX `esx_%s_%s` ON `%s` (`%s`)"):format(target.table, target.column, target.table, target.column))
|
||||
print(("^4[esx_migration:v1.14.2:multicharDeleteIndexes]^7 Indexed ^5%s.%s^7."):format(target.table, target.column))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print("^4[esx_migration:v1.14.2:multicharDeleteIndexes]^7 Migration complete.")
|
||||
return false
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
local esxVersion = "v1.14.2"
|
||||
|
||||
Core.Migrations = Core.Migrations or {}
|
||||
Core.Migrations[esxVersion] = Core.Migrations[esxVersion] or {}
|
||||
|
||||
if GetResourceKvpInt(("esx_migration:%s"):format(esxVersion)) == 1 then
|
||||
return
|
||||
end
|
||||
|
||||
---@return boolean restartRequired
|
||||
Core.Migrations[esxVersion].rentedVehiclesOwnerLength = function()
|
||||
print("^4[esx_migration:v1.14.2:rentedVehiclesOwnerLength]^7 Checking the length of rented_vehicles.owner.")
|
||||
|
||||
local length = MySQL.scalar.await([[
|
||||
SELECT CHARACTER_MAXIMUM_LENGTH
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'rented_vehicles'
|
||||
AND COLUMN_NAME = 'owner'
|
||||
]])
|
||||
|
||||
if not length then
|
||||
print("^4[esx_migration:v1.14.2:rentedVehiclesOwnerLength]^7 Column not found, migration not needed.")
|
||||
return false
|
||||
end
|
||||
|
||||
if length >= 60 then
|
||||
print(("^4[esx_migration:v1.14.2:rentedVehiclesOwnerLength]^7 Column is already varchar(%s), migration not needed."):format(length))
|
||||
return false
|
||||
end
|
||||
|
||||
print(("^4[esx_migration:v1.14.2:rentedVehiclesOwnerLength]^7 Column is varchar(%s), widening it to 60."):format(length))
|
||||
|
||||
MySQL.update.await([[
|
||||
ALTER TABLE `rented_vehicles`
|
||||
MODIFY `owner` VARCHAR(60) NOT NULL
|
||||
]])
|
||||
|
||||
print("^4[esx_migration:v1.14.2:rentedVehiclesOwnerLength]^7 Migration complete.")
|
||||
return true
|
||||
end
|
||||
@@ -107,7 +107,7 @@ function ESX.AwaitClientCallback(player, eventName, ...)
|
||||
if not p then return end
|
||||
|
||||
SetTimeout(15000, function()
|
||||
if p.state == "pending" then
|
||||
if p.state == 0 then
|
||||
p:reject("Server Callback Timed Out")
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
local CommandPermissions = Config.CommandPermissions or {}
|
||||
|
||||
local function FilterGroups(groups)
|
||||
return (groups and #groups > 0) and groups or { "user" }
|
||||
end
|
||||
|
||||
ESX.RegisterCommand(
|
||||
{ "setcoords", "tp" },
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.setcoords),
|
||||
function(xPlayer, args)
|
||||
xPlayer.setCoords({ x = args.x, y = args.y, z = args.z })
|
||||
if Config.AdminLogging then
|
||||
@@ -27,7 +33,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"setjob",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.setjob),
|
||||
function(xPlayer, args, showError)
|
||||
if not ESX.DoesJobExist(args.job, args.grade) then
|
||||
return showError(TranslateCap("command_setjob_invalid"))
|
||||
@@ -68,7 +74,7 @@ local upgrades = Config.SpawnVehMaxUpgrades and {
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"car",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.car),
|
||||
function(xPlayer, args, showError)
|
||||
if not xPlayer then
|
||||
return showError("[^1ERROR^7] The xPlayer value is nil")
|
||||
@@ -132,7 +138,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
{ "cardel", "dv" },
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.cardel),
|
||||
function(xPlayer, args)
|
||||
local ped = GetPlayerPed(xPlayer.source)
|
||||
local pedVehicle = GetVehiclePedIsIn(ped, false)
|
||||
@@ -168,7 +174,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
{ "fix", "repair" },
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.fix),
|
||||
function(xPlayer, args, showError)
|
||||
local xTarget = args.playerId
|
||||
local ped = GetPlayerPed(xTarget.source)
|
||||
@@ -178,9 +184,11 @@ ESX.RegisterCommand(
|
||||
return
|
||||
end
|
||||
xTarget.triggerEvent("esx:repairPedVehicle")
|
||||
xPlayer.showNotification(TranslateCap("command_repair_success"), true, false, 140)
|
||||
if xPlayer.source ~= xTarget.source then
|
||||
xTarget.showNotification(TranslateCap("command_repair_success_target"), true, false, 140)
|
||||
if xPlayer then
|
||||
xPlayer.showNotification(TranslateCap("command_repair_success"))
|
||||
end
|
||||
if not xPlayer or xPlayer.source ~= xTarget.source then
|
||||
xTarget.showNotification(TranslateCap("command_repair_success_target"))
|
||||
end
|
||||
if Config.AdminLogging then
|
||||
ESX.DiscordLogFields("UserActions", "Fix Vehicle /fix Triggered!", "pink", {
|
||||
@@ -202,7 +210,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"setaccountmoney",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.setaccountmoney),
|
||||
function(xPlayer, args, showError)
|
||||
if not args.playerId.getAccount(args.account) then
|
||||
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
||||
@@ -232,7 +240,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"giveaccountmoney",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.giveaccountmoney),
|
||||
function(xPlayer, args, showError)
|
||||
if not args.playerId.getAccount(args.account) then
|
||||
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
||||
@@ -262,7 +270,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"removeaccountmoney",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.removeaccountmoney),
|
||||
function(xPlayer, args, showError)
|
||||
if not args.playerId.getAccount(args.account) then
|
||||
return showError(TranslateCap("command_removeaccountmoney_invalid"))
|
||||
@@ -293,7 +301,7 @@ ESX.RegisterCommand(
|
||||
if not Config.CustomInventory then
|
||||
ESX.RegisterCommand(
|
||||
"giveitem",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.giveitem),
|
||||
function(xPlayer, args)
|
||||
args.playerId.addInventoryItem(args.item, args.count)
|
||||
if Config.AdminLogging then
|
||||
@@ -323,7 +331,7 @@ if not Config.CustomInventory then
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"giveweapon",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.giveweapon),
|
||||
function(xPlayer, args, showError)
|
||||
if args.playerId.hasWeapon(args.weapon) then
|
||||
return showError(TranslateCap("command_giveweapon_hasalready"))
|
||||
@@ -353,7 +361,7 @@ if not Config.CustomInventory then
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"giveammo",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.giveammo),
|
||||
function(xPlayer, args, showError)
|
||||
if not args.playerId.hasWeapon(args.weapon) then
|
||||
return showError(TranslateCap("command_giveammo_noweapon_found"))
|
||||
@@ -383,7 +391,7 @@ if not Config.CustomInventory then
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"giveweaponcomponent",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.giveweaponcomponent),
|
||||
function(xPlayer, args, showError)
|
||||
if args.playerId.hasWeapon(args.weaponName) then
|
||||
local component = ESX.GetWeaponComponent(args.weaponName, args.componentName)
|
||||
@@ -423,11 +431,11 @@ if not Config.CustomInventory then
|
||||
)
|
||||
end
|
||||
|
||||
ESX.RegisterCommand({ "clear", "cls" }, "user", function(xPlayer)
|
||||
ESX.RegisterCommand({ "clear", "cls" }, FilterGroups(CommandPermissions.clear), function(xPlayer)
|
||||
xPlayer.triggerEvent("chat:clear")
|
||||
end, false, { help = TranslateCap("command_clear") })
|
||||
|
||||
ESX.RegisterCommand({ "clearall", "clsall" }, "admin", function(xPlayer)
|
||||
ESX.RegisterCommand({ "clearall", "clsall" }, FilterGroups(CommandPermissions.clearall), function(xPlayer)
|
||||
TriggerClientEvent("chat:clear", -1)
|
||||
if Config.AdminLogging then
|
||||
ESX.DiscordLogFields("UserActions", "Clear Chat /clearall Triggered!", "pink", {
|
||||
@@ -437,20 +445,24 @@ ESX.RegisterCommand({ "clearall", "clsall" }, "admin", function(xPlayer)
|
||||
end
|
||||
end, true, { help = TranslateCap("command_clearall") })
|
||||
|
||||
ESX.RegisterCommand("refreshjobs", "admin", function()
|
||||
ESX.RegisterCommand("refreshjobs", FilterGroups(CommandPermissions.refreshjobs), function()
|
||||
ESX.RefreshJobs()
|
||||
end, true, { help = TranslateCap("command_clearall") })
|
||||
|
||||
if not Config.CustomInventory then
|
||||
ESX.RegisterCommand("refreshitems", "admin", function(xPlayer)
|
||||
ESX.RegisterCommand("refreshitems", FilterGroups(CommandPermissions.refreshitems), function(xPlayer)
|
||||
local itemCount = ESX.RefreshItems()
|
||||
|
||||
xPlayer.showNotification(Translate("command_refreshitems_success", itemCount), true, false, 140)
|
||||
if xPlayer then
|
||||
xPlayer.showNotification(Translate("command_refreshitems_success", itemCount))
|
||||
else
|
||||
print(("[^2INFO^7] %s^7"):format(Translate("command_refreshitems_success", itemCount)))
|
||||
end
|
||||
end, true, { help = TranslateCap("command_refreshitems") })
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"clearinventory",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.clearinventory),
|
||||
function(xPlayer, args)
|
||||
for _, v in ipairs(args.playerId.inventory) do
|
||||
if v.count > 0 then
|
||||
@@ -478,7 +490,7 @@ if not Config.CustomInventory then
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"clearloadout",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.clearloadout),
|
||||
function(xPlayer, args)
|
||||
for i = #args.playerId.loadout, 1, -1 do
|
||||
args.playerId.removeWeapon(args.playerId.loadout[i].name)
|
||||
@@ -505,7 +517,7 @@ end
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"setgroup",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.setgroup),
|
||||
function(xPlayer, args)
|
||||
if not args.playerId then
|
||||
args.playerId = xPlayer.source
|
||||
@@ -537,7 +549,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"save",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.save),
|
||||
function(_, args)
|
||||
Core.SavePlayer(args.playerId)
|
||||
print(("[^2Info^0] Saved Player - ^5%s^0"):format(args.playerId.source))
|
||||
@@ -552,26 +564,26 @@ ESX.RegisterCommand(
|
||||
}
|
||||
)
|
||||
|
||||
ESX.RegisterCommand("saveall", "admin", function()
|
||||
ESX.RegisterCommand("saveall", FilterGroups(CommandPermissions.saveall), function()
|
||||
Core.SavePlayers()
|
||||
end, true, { help = TranslateCap("command_saveall") })
|
||||
|
||||
ESX.RegisterCommand("group", { "user", "admin" }, function(xPlayer, _, _)
|
||||
ESX.RegisterCommand("group", FilterGroups(CommandPermissions.group), function(xPlayer, _, _)
|
||||
print(("%s, you are currently: ^5%s^0"):format(xPlayer.getName(), xPlayer.getGroup()))
|
||||
end, true)
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("job", { "user", "admin" }, function(xPlayer, _, _)
|
||||
ESX.RegisterCommand("job", FilterGroups(CommandPermissions.job), function(xPlayer, _, _)
|
||||
local job = xPlayer.getJob()
|
||||
|
||||
print(("%s, your job is: ^5%s^0 - ^5%s^0 - ^5%s^0"):format(xPlayer.getName(), job.name, job.grade_label, job.onDuty and "On Duty" or "Off Duty"))
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("info", { "user", "admin" }, function(xPlayer)
|
||||
ESX.RegisterCommand("info", FilterGroups(CommandPermissions.info), function(xPlayer)
|
||||
local job = xPlayer.getJob().name
|
||||
print(("^2ID: ^5%s^0 | ^2Name: ^5%s^0 | ^2Group: ^5%s^0 | ^2Job: ^5%s^0"):format(xPlayer.source, xPlayer.getName(), xPlayer.getGroup(), job))
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("playtime", { "user", "admin" }, function(xPlayer)
|
||||
ESX.RegisterCommand("playtime", FilterGroups(CommandPermissions.playtime), function(xPlayer)
|
||||
local playtime = xPlayer.getPlayTime()
|
||||
local days = math.floor(playtime / 86400)
|
||||
local hours = math.floor((playtime % 86400) / 3600)
|
||||
@@ -579,7 +591,7 @@ ESX.RegisterCommand("playtime", { "user", "admin" }, function(xPlayer)
|
||||
print(("Playtime: ^5%s^0 Days | ^5%s^0 Hours | ^5%s^0 Minutes"):format(days, hours, minutes))
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("coords", "admin", function(xPlayer)
|
||||
ESX.RegisterCommand("coords", FilterGroups(CommandPermissions.coords), function(xPlayer)
|
||||
local ped = GetPlayerPed(xPlayer.source)
|
||||
local coords = GetEntityCoords(ped, false)
|
||||
local heading = GetEntityHeading(ped)
|
||||
@@ -587,7 +599,7 @@ ESX.RegisterCommand("coords", "admin", function(xPlayer)
|
||||
print(("Coords - Vector4: ^5%s^0"):format(vector4(coords.x, coords.y, coords.z, heading)))
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("tpm", "admin", function(xPlayer)
|
||||
ESX.RegisterCommand("tpm", FilterGroups(CommandPermissions.tpm), function(xPlayer)
|
||||
xPlayer.triggerEvent("esx:tpm")
|
||||
if Config.AdminLogging then
|
||||
ESX.DiscordLogFields("UserActions", "Admin Teleport /tpm Triggered!", "pink", {
|
||||
@@ -599,7 +611,7 @@ end, false)
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"goto",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions["goto"]),
|
||||
function(xPlayer, args)
|
||||
local targetCoords = args.playerId.getCoords()
|
||||
local srcDim = GetPlayerRoutingBucket(xPlayer.source)
|
||||
@@ -630,7 +642,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"bring",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.bring),
|
||||
function(xPlayer, args)
|
||||
local targetCoords = args.playerId.getCoords()
|
||||
local playerCoords = xPlayer.getCoords()
|
||||
@@ -662,7 +674,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"kill",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.kill),
|
||||
function(xPlayer, args)
|
||||
args.playerId.triggerEvent("esx:killPlayer")
|
||||
if Config.AdminLogging then
|
||||
@@ -685,7 +697,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"freeze",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.freeze),
|
||||
function(xPlayer, args)
|
||||
args.playerId.triggerEvent("esx:freezePlayer", "freeze")
|
||||
if Config.AdminLogging then
|
||||
@@ -708,7 +720,7 @@ ESX.RegisterCommand(
|
||||
|
||||
ESX.RegisterCommand(
|
||||
"unfreeze",
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.unfreeze),
|
||||
function(xPlayer, args)
|
||||
args.playerId.triggerEvent("esx:freezePlayer", "unfreeze")
|
||||
if Config.AdminLogging then
|
||||
@@ -729,7 +741,7 @@ ESX.RegisterCommand(
|
||||
}
|
||||
)
|
||||
|
||||
ESX.RegisterCommand("noclip", "admin", function(xPlayer)
|
||||
ESX.RegisterCommand("noclip", FilterGroups(CommandPermissions.noclip), function(xPlayer)
|
||||
xPlayer.triggerEvent("esx:noclip")
|
||||
if Config.AdminLogging then
|
||||
ESX.DiscordLogFields("UserActions", "Admin NoClip /noclip Triggered!", "pink", {
|
||||
@@ -739,7 +751,7 @@ ESX.RegisterCommand("noclip", "admin", function(xPlayer)
|
||||
end
|
||||
end, false)
|
||||
|
||||
ESX.RegisterCommand("players", "admin", function()
|
||||
ESX.RegisterCommand("players", FilterGroups(CommandPermissions.players), function()
|
||||
local xPlayers = ESX.GetExtendedPlayers() -- Returns all xPlayers
|
||||
print(("^5%s^2 online player(s)^0"):format(#xPlayers))
|
||||
for i = 1, #xPlayers do
|
||||
@@ -750,7 +762,7 @@ end, true)
|
||||
|
||||
ESX.RegisterCommand(
|
||||
{"setdim", "setbucket"},
|
||||
"admin",
|
||||
FilterGroups(CommandPermissions.setdim),
|
||||
function(xPlayer, args)
|
||||
SetPlayerRoutingBucket(args.playerId.source, args.dimension)
|
||||
if Config.AdminLogging then
|
||||
|
||||
@@ -5,24 +5,10 @@ local NOTIFY_TYPES = {
|
||||
ERROR = "^5[%s]^7-^1[ERROR]^7 %s"
|
||||
}
|
||||
|
||||
local function doesJobAndGradesExist(name, grades)
|
||||
if not ESX.Jobs[name] then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, grade in ipairs(grades) do
|
||||
if not ESX.DoesJobExist(name, grade.grade) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function generateNewJobTable(name, label, grades, jobType)
|
||||
local job = { name = name, label = label, type = jobType, grades = {} }
|
||||
local job = ESX.Jobs[name] or { name = name, label = label, type = jobType, grades = {} }
|
||||
for _, v in pairs(grades) do
|
||||
job.grades[tostring(v.grade)] = { job_name = name, grade = v.grade, name = v.name, label = v.label, salary = v.salary, skin_male = v.skin_male or '{}', skin_female = v.skin_female or '{}' }
|
||||
job.grades[tostring(v.grade)] = { job_name = name, grade = v.grade, name = v.name, label = v.label, salary = v.salary, skin_male = v.skin_male, skin_female = v.skin_female }
|
||||
end
|
||||
|
||||
return job
|
||||
@@ -66,23 +52,48 @@ function ESX.CreateJob(name, label, grades, jobType)
|
||||
jobType = "civ"
|
||||
end
|
||||
|
||||
local currentJobExist = doesJobAndGradesExist(name, grades)
|
||||
local existingJob = MySQL.single.await('SELECT `label`, `type` FROM `jobs` WHERE `name` = ?', { name })
|
||||
local jobExists = existingJob ~= nil
|
||||
local existingGrades = {}
|
||||
|
||||
if currentJobExist then
|
||||
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
|
||||
return success
|
||||
if jobExists then
|
||||
label, jobType = existingJob.label, existingJob.type
|
||||
|
||||
local rows = MySQL.query.await('SELECT `grade` FROM `job_grades` WHERE `job_name` = ?', { name })
|
||||
|
||||
for i = 1, #(rows or {}) do
|
||||
existingGrades[tostring(rows[i].grade)] = true
|
||||
end
|
||||
end
|
||||
|
||||
local queries = {
|
||||
{ query = 'INSERT INTO `jobs` (`name`, `label`, `type`) VALUES (?, ?, ?)', values = { name, label, jobType } }
|
||||
local queries = {}
|
||||
|
||||
if not jobExists then
|
||||
queries[#queries + 1] = {
|
||||
query = 'INSERT INTO `jobs` (`name`, `label`, `type`) VALUES (?, ?, ?)',
|
||||
values = { name, label, jobType }
|
||||
}
|
||||
end
|
||||
|
||||
local newGrades = {}
|
||||
|
||||
for _, grade in pairs(grades) do
|
||||
if not existingGrades[tostring(grade.grade)] then
|
||||
local skinMale = grade.skin_male and json.encode(grade.skin_male) or '{}'
|
||||
local skinFemale = grade.skin_female and json.encode(grade.skin_female) or '{}'
|
||||
|
||||
newGrades[#newGrades + 1] = { grade = grade.grade, name = grade.name, label = grade.label, salary = grade.salary, skin_male = skinMale, skin_female = skinFemale }
|
||||
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, grade.skin_male and json.encode(grade.skin_male) or '{}', grade.skin_female and json.encode(grade.skin_female) or '{}' }
|
||||
values = { name, grade.grade, grade.name, grade.label, grade.salary, skinMale, skinFemale }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if not queries[1] then
|
||||
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
|
||||
return success
|
||||
end
|
||||
|
||||
success = exports.oxmysql:transaction_async(queries)
|
||||
|
||||
@@ -91,7 +102,7 @@ function ESX.CreateJob(name, label, grades, jobType)
|
||||
return success
|
||||
end
|
||||
|
||||
ESX.Jobs[name] = generateNewJobTable(name, label, grades, jobType)
|
||||
ESX.Jobs[name] = generateNewJobTable(name, label, newGrades, jobType)
|
||||
|
||||
notify("SUCCESS", currentResourceName, 'Job created successfully: `%s`', name)
|
||||
|
||||
|
||||
@@ -34,11 +34,52 @@ Config.DefaultSpawns = { -- If you want to have more spawn positions and select
|
||||
--{x = 233.5459, y = -868.2626, z = 30.2922, heading = 1.0}
|
||||
}
|
||||
|
||||
---@deprecated Use `Config.CommandPermissions` as the single source of truth for command ACLs.
|
||||
--- Kept temporarily for backwards compatibility with external resources that still read `Config.AdminGroups`.
|
||||
--- Will be removed in ESX 1.15.
|
||||
Config.AdminGroups = {
|
||||
["owner"] = true,
|
||||
["admin"] = true,
|
||||
}
|
||||
|
||||
Config.CommandPermissions = {
|
||||
["setcoords"] = { "owner", "admin" }, -- /setcoords, /tp
|
||||
["setjob"] = { "owner", "admin" },
|
||||
["car"] = { "owner", "admin" },
|
||||
["cardel"] = { "owner", "admin" }, -- /cardel, /dv
|
||||
["fix"] = { "owner", "admin" }, -- /fix, /repair
|
||||
["setaccountmoney"] = { "owner", "admin" },
|
||||
["giveaccountmoney"] = { "owner", "admin" },
|
||||
["removeaccountmoney"] = { "owner", "admin" },
|
||||
["giveitem"] = { "owner", "admin" },
|
||||
["giveweapon"] = { "owner", "admin" },
|
||||
["giveammo"] = { "owner", "admin" },
|
||||
["giveweaponcomponent"] = { "owner", "admin" },
|
||||
["clearall"] = { "owner", "admin" }, -- /clearall, /clsall
|
||||
["refreshjobs"] = { "owner", "admin" },
|
||||
["refreshitems"] = { "owner", "admin" },
|
||||
["clearinventory"] = { "owner", "admin" },
|
||||
["clearloadout"] = { "owner", "admin" },
|
||||
["setgroup"] = { "owner", "admin" },
|
||||
["save"] = { "owner", "admin" },
|
||||
["saveall"] = { "owner", "admin" },
|
||||
["goto"] = { "owner", "admin" },
|
||||
["bring"] = { "owner", "admin" },
|
||||
["kill"] = { "owner", "admin" },
|
||||
["freeze"] = { "owner", "admin" },
|
||||
["unfreeze"] = { "owner", "admin" },
|
||||
["setdim"] = { "owner", "admin" }, -- /setdim, /setbucket
|
||||
["players"] = { "owner", "admin" },
|
||||
["noclip"] = { "owner", "admin" },
|
||||
["tpm"] = { "owner", "admin" },
|
||||
["coords"] = { "owner", "admin" },
|
||||
["clear"] = { "user", "admin", "owner" }, -- /clear, /cls
|
||||
["group"] = { "user", "admin", "owner" },
|
||||
["job"] = { "user", "admin", "owner" },
|
||||
["info"] = { "user", "admin", "owner" },
|
||||
["playtime"] = { "user", "admin", "owner" },
|
||||
}
|
||||
|
||||
Config.ValidCharacterSets = { -- Only enable additional charsets if your server is multilingual. By default everything is false.
|
||||
['el'] = false, -- Greek
|
||||
['sr'] = false, -- Cyrillic
|
||||
|
||||
@@ -186,6 +186,10 @@ function ESX.IsValidLocaleString(str, allowDigits)
|
||||
return false
|
||||
end
|
||||
|
||||
if not utf8.len(str) then
|
||||
return false
|
||||
end
|
||||
|
||||
local locale = string.lower(Config.Locale)
|
||||
|
||||
local defaultRanges ={
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
author 'ESX-Framework'
|
||||
description 'A ESX Stylised theme for the chat resource.'
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ game 'gta5'
|
||||
author 'ESX-Framework & Brayden'
|
||||
description 'A simplistic context menu for ESX.'
|
||||
lua54 'yes'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
ui_page 'index.html'
|
||||
|
||||
|
||||
@@ -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.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
shared_scripts {
|
||||
'@es_extended/imports.lua',
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
---@field icon string
|
||||
---@field count number
|
||||
---@field value string
|
||||
---@field usable boolean
|
||||
---@field canUse boolean?
|
||||
---@field usable boolean?
|
||||
---@field rare boolean
|
||||
---@field canRemove boolean
|
||||
---@field unselectable boolean?
|
||||
@@ -34,7 +35,6 @@ local function appendAccounts(elements)
|
||||
icon = "fas fa-money-bill-wave",
|
||||
count = account.money,
|
||||
value = account.name,
|
||||
usable = false,
|
||||
rare = false,
|
||||
canRemove = canDrop
|
||||
}
|
||||
@@ -56,7 +56,7 @@ local function appendItems(elements)
|
||||
icon = "fas fa-box",
|
||||
count = item.count,
|
||||
value = item.name,
|
||||
usable = item.usable,
|
||||
canUse = item.usable,
|
||||
rare = item.rare,
|
||||
canRemove = item.canRemove
|
||||
}
|
||||
@@ -83,7 +83,6 @@ local function appendLoadout(elements)
|
||||
icon = "fas fa-gun",
|
||||
count = 1,
|
||||
value = weapon.name,
|
||||
usable = false,
|
||||
rare = false,
|
||||
ammo = ammo,
|
||||
canGiveAmmo = (weapon.ammo ~= nil),
|
||||
@@ -119,7 +118,7 @@ end
|
||||
---@return table
|
||||
local function buildItemActionMenu(selected, playerNearby)
|
||||
local elements2 = {}
|
||||
if selected.usable then
|
||||
if selected.canUse then
|
||||
elements2[#elements2 + 1] = { action = "use", label = TranslateCap("use"), icon = "fas fa-utensils", type = selected.type, value = selected.value }
|
||||
end
|
||||
if selected.canRemove then
|
||||
@@ -146,7 +145,6 @@ local function openQuantityDialog(title, maxCount)
|
||||
local qty = tonumber(data.value)
|
||||
if not qty or qty <= 0 or qty > maxCount then
|
||||
ESX.ShowNotification(TranslateCap("amount_invalid"))
|
||||
p:resolve(nil)
|
||||
else
|
||||
menu.close()
|
||||
p:resolve(qty)
|
||||
|
||||
@@ -4,7 +4,7 @@ game "gta5"
|
||||
description "Inventory for the ESX framework"
|
||||
lua54 "yes"
|
||||
use_fxv2_oal "yes"
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
shared_scripts {
|
||||
"/config/main.lua",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
Config = {}
|
||||
|
||||
---@class MedalClipOptions
|
||||
---@field duration integer
|
||||
---@field captureDelayMs integer
|
||||
---@field alertType 'Default'|'Disabled'|'SoundOnly'|'OverlayOnly'
|
||||
|
||||
---@class MedalConfig
|
||||
---@field enabled boolean
|
||||
---@field publicKey string
|
||||
---@field eventName string
|
||||
---@field clipOptions MedalClipOptions
|
||||
|
||||
---@type MedalConfig
|
||||
Config.Medal = {
|
||||
enabled = true,
|
||||
publicKey = 'pub_82qkpMKV77AkpqLSgWsxLlDyfzpPI7Vw',
|
||||
eventName = 'Death',
|
||||
clipOptions = {
|
||||
duration = 30,
|
||||
captureDelayMs = 0,
|
||||
alertType = 'Default'
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,18 @@ author 'ESX Team'
|
||||
version '0.01'
|
||||
description 'Official ESX library'
|
||||
|
||||
ui_page 'html/medal.html'
|
||||
|
||||
files {
|
||||
'imports.lua',
|
||||
'imports/**/client.lua',
|
||||
'imports/**/shared.lua',
|
||||
'html/medal.html',
|
||||
'html/medal.js',
|
||||
}
|
||||
|
||||
shared_scripts {
|
||||
'config.lua',
|
||||
'resource/init.lua',
|
||||
'resource/**/shared.lua',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; connect-src http://localhost:12665 http://127.0.0.1:12665">
|
||||
</head>
|
||||
<body>
|
||||
<script src="medal.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
window.addEventListener('message', function (event) {
|
||||
var data = event.data;
|
||||
if (!data || data.action !== 'medalClip') return;
|
||||
|
||||
fetch('http://localhost:12665/api/v1/event/invoke', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'publicKey': data.publicKey,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data.payload)
|
||||
}).catch(function () {});
|
||||
});
|
||||
@@ -462,7 +462,7 @@ function xLib.game.setVehicleProperties(vehicle, props)
|
||||
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)
|
||||
SetVehicleColours(vehicle, type(props.color1) == "number" and props.color1 or colorPrimary, props.color2)
|
||||
end
|
||||
end
|
||||
if props.pearlescentColor ~= nil then
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
xLib.interactions = {}
|
||||
|
||||
local interactions = {}
|
||||
local pressedInteractions = {}
|
||||
|
||||
---@param name string
|
||||
function xLib.interactions.remove(name)
|
||||
@@ -36,7 +35,6 @@ xLib.addKeybind({
|
||||
for _, interaction in pairs(interactions) do
|
||||
local success, result = pcall(interaction.condition)
|
||||
if success and result then
|
||||
pressedInteractions[#pressedInteractions + 1] = interaction
|
||||
interaction.onPress()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
local ENHANCED_GAME_NAMES <const> = {
|
||||
["gta5enhanced"] = true,
|
||||
["gta5_enhanced"] = true
|
||||
}
|
||||
|
||||
local isEnhanced
|
||||
|
||||
if IsDuplicityVersion() then
|
||||
isEnhanced = ENHANCED_GAME_NAMES[GetConvar("gamename", "gta5")] == true
|
||||
else
|
||||
isEnhanced = type(IsGameEnhancedVersion) == "function" and IsGameEnhancedVersion() == true
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
return function()
|
||||
return isEnhanced
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
local medal = {}
|
||||
|
||||
function medal.getConfig()
|
||||
return exports['esx_lib']:getMedalConfig()
|
||||
end
|
||||
|
||||
function medal.triggerClip(publicKey, eventName, clipOptions)
|
||||
local cfg = medal.getConfig()
|
||||
if not cfg or not cfg.enabled then return end
|
||||
|
||||
exports['esx_lib']:triggerMedalClip(
|
||||
publicKey or cfg.publicKey,
|
||||
eventName or cfg.eventName,
|
||||
clipOptions or cfg.clipOptions
|
||||
)
|
||||
end
|
||||
|
||||
return medal
|
||||
@@ -4,6 +4,7 @@ xLib.points = {}
|
||||
local points = {}
|
||||
local insidePoints = {}
|
||||
local handleCount = 0
|
||||
local loopStarted = false
|
||||
|
||||
---@param coords vector3
|
||||
---@param distance number
|
||||
@@ -44,14 +45,23 @@ function xLib.points.hide(handle, hidden)
|
||||
end
|
||||
|
||||
function xLib.points.startLoop()
|
||||
if loopStarted then
|
||||
return
|
||||
end
|
||||
|
||||
loopStarted = true
|
||||
|
||||
CreateThread(function()
|
||||
local lastScan = 0
|
||||
|
||||
while true do
|
||||
local coords = GetEntityCoords(PlayerPedId())
|
||||
|
||||
for _, point in pairs(insidePoints) do
|
||||
point.inside(#(coords - point.coords))
|
||||
for handle, point in pairs(insidePoints) do
|
||||
if not pcall(point.inside, #(coords - point.coords)) then
|
||||
insidePoints[handle] = nil
|
||||
print(("[^1ERROR^7] point ^5%s^7 from ^5%s^7 errored on inside"):format(handle, point.resource))
|
||||
end
|
||||
end
|
||||
|
||||
local now = GetGameTimer()
|
||||
@@ -63,8 +73,8 @@ function xLib.points.startLoop()
|
||||
if not point.nearby then
|
||||
point.nearby = true
|
||||
|
||||
if point.enter then
|
||||
point.enter()
|
||||
if point.enter and not pcall(point.enter) then
|
||||
print(("[^1ERROR^7] point ^5%s^7 from ^5%s^7 errored on enter"):format(handle, point.resource))
|
||||
end
|
||||
|
||||
if point.inside then
|
||||
@@ -74,8 +84,8 @@ function xLib.points.startLoop()
|
||||
elseif point.nearby then
|
||||
point.nearby = false
|
||||
|
||||
if point.leave then
|
||||
point.leave()
|
||||
if point.leave and not pcall(point.leave) then
|
||||
print(("[^1ERROR^7] point ^5%s^7 from ^5%s^7 errored on leave"):format(handle, point.resource))
|
||||
end
|
||||
|
||||
insidePoints[handle] = nil
|
||||
|
||||
@@ -47,7 +47,7 @@ end
|
||||
function xLib.string.toPascal(s)
|
||||
xLib.verify(s, 'string', true)
|
||||
|
||||
local res = s:gsub("(%a)([%w_]*)", function(first, rest)
|
||||
local res = s:gsub("(%a)([%w]*)", function(first, rest)
|
||||
return first:upper() .. rest:lower()
|
||||
end):gsub("_", "")
|
||||
|
||||
@@ -114,6 +114,10 @@ end
|
||||
function xLib.string.replace(s, old, new)
|
||||
xLib.verify(s, 'string', true)
|
||||
|
||||
if type(new) == "string" then
|
||||
new = new:gsub("%%", "%%%%")
|
||||
end
|
||||
|
||||
local result = s:gsub(xLib.string.escapePattern(old), new)
|
||||
|
||||
return result
|
||||
|
||||
@@ -14,13 +14,9 @@ function xLib.table.isArray(tbl)
|
||||
end
|
||||
|
||||
count, maxIndex = count + 1, math.max(maxIndex, k)
|
||||
|
||||
if count > maxIndex then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
return count == maxIndex
|
||||
end
|
||||
|
||||
---@param tbl table
|
||||
@@ -38,13 +34,6 @@ function xLib.table.searchForKey(tbl, item)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param tbl table
|
||||
---@param item any
|
||||
---@return boolean
|
||||
function xLib.table.contains(tbl, item)
|
||||
return xLib.table.searchForKey(tbl, item) ~= nil
|
||||
end
|
||||
|
||||
---@param tbl table
|
||||
---@param filter fun(value:any, key:any):boolean
|
||||
---@return table
|
||||
@@ -122,7 +111,7 @@ function xLib.table.dump(tbl)
|
||||
local s = '{ '
|
||||
for k,v in pairs(tbl) do
|
||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||
s = s .. '['..k..'] = ' .. tbl(v) .. ','
|
||||
s = s .. '['..k..'] = ' .. xLib.table.dump(v) .. ','
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
|
||||
@@ -81,7 +81,7 @@ local function verifyType(value, valid_type)
|
||||
elseif valid_type == 'float' then
|
||||
return math.type(value) == 'float'
|
||||
elseif valid_type == 'uint' then
|
||||
return math.type(value) == 'int' and value >= 0
|
||||
return math.type(value) == 'integer' and value >= 0
|
||||
elseif valid_type == 'char' then
|
||||
return type(value) == 'string' and #value == 1
|
||||
elseif valid_type == 'ped' then
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---@diagnostic disable: lowercase-global
|
||||
xLib = setmetatable({
|
||||
name = 'xLib',
|
||||
name = 'esx_lib',
|
||||
side = IsDuplicityVersion() and 'server' or 'client'
|
||||
}, {
|
||||
__newindex = function(self, key, fn)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
xLib.getMedalConfig = function()
|
||||
return Config.Medal
|
||||
end
|
||||
|
||||
xLib.triggerMedalClip = function(publicKey, eventName, clipOptions)
|
||||
if not publicKey or publicKey == '' then return end
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'medalClip',
|
||||
publicKey = publicKey,
|
||||
payload = {
|
||||
eventId = ('esx-clip-%s-%s'):format(GetPlayerServerId(PlayerId()), GetGameTimer()),
|
||||
eventName = eventName or 'Event',
|
||||
triggerActions = { 'SaveClip' },
|
||||
clipOptions = clipOptions or {
|
||||
duration = 30,
|
||||
captureDelayMs = 0,
|
||||
alertType = 'Default'
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
@@ -3,7 +3,7 @@ game 'common'
|
||||
fx_version 'cerulean'
|
||||
author 'ESX-Framework'
|
||||
description 'Allows resources to Run tasks at specific intervals.'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
loadscreen 'index.html'
|
||||
|
||||
@@ -3,7 +3,7 @@ fx_version 'cerulean'
|
||||
game 'gta5'
|
||||
description 'A basic menu system for ESX Legacy.'
|
||||
lua54 'yes'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
client_scripts { '@es_extended/imports.lua', 'client/main.lua' }
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
local Timeouts, OpenedMenus, MenuType = {}, {}, "dialog"
|
||||
local OpenedMenus, MenuType = {}, "dialog"
|
||||
|
||||
local function openMenu(namespace, name, data)
|
||||
for i = 1, #Timeouts, 1 do
|
||||
ESX.ClearTimeout(Timeouts[i])
|
||||
end
|
||||
|
||||
OpenedMenus[namespace .. "_" .. name] = true
|
||||
|
||||
SendNUIMessage({
|
||||
@@ -14,11 +10,11 @@ local function openMenu(namespace, name, data)
|
||||
data = data,
|
||||
})
|
||||
|
||||
local timeoutId = ESX.SetTimeout(200, function()
|
||||
ESX.SetTimeout(200, function()
|
||||
if next(OpenedMenus) then
|
||||
SetNuiFocus(true, true)
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(Timeouts, timeoutId)
|
||||
end
|
||||
|
||||
local function closeMenu(namespace, name)
|
||||
|
||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
||||
game 'gta5'
|
||||
description 'A basic input dialog for ESX Legacy.'
|
||||
lua54 'yes'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
client_scripts {
|
||||
'@es_extended/imports.lua',
|
||||
|
||||
@@ -12,7 +12,9 @@ CreateThread(function()
|
||||
data = data,
|
||||
})
|
||||
SetTimeout(200, function()
|
||||
if next(OpenedMenus) then
|
||||
SetNuiFocus(true, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
||||
game 'gta5'
|
||||
description 'A basic table-based menu system for ESX Legacy.'
|
||||
lua54 'yes'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
|
||||
|
||||
client_scripts {
|
||||
|
||||
@@ -50,7 +50,7 @@ local function HideComponents(hide)
|
||||
else
|
||||
if HiddenCompents[components[i]] then
|
||||
local size = HiddenCompents[components[i]]
|
||||
SetHudComponentSize(components[i], size.x, size.z)
|
||||
SetHudComponentSize(components[i], size.x, size.y)
|
||||
HiddenCompents[components[i]] = nil
|
||||
end
|
||||
end
|
||||
@@ -59,9 +59,9 @@ local function HideComponents(hide)
|
||||
end
|
||||
|
||||
function Multicharacter:HideHud(hide)
|
||||
self.hidePlayers = true
|
||||
self.hidePlayers = hide
|
||||
|
||||
MumbleSetVolumeOverride(ESX.PlayerId, 0.0)
|
||||
MumbleSetVolumeOverride(ESX.playerId, hide and 0.0 or -1.0)
|
||||
HideComponents(hide)
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ function Multicharacter:SetupCharacters()
|
||||
SetEntityCoords(self.playerPed, self.spawnCoords.x, self.spawnCoords.y, self.spawnCoords.z, true, false, false, false)
|
||||
SetEntityHeading(self.playerPed, self.spawnCoords.w)
|
||||
|
||||
SetPlayerControl(ESX.PlayerId, false, 0)
|
||||
SetPlayerControl(ESX.playerId, false, 0)
|
||||
self:SetupCamera()
|
||||
self:HideHud(true)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ else
|
||||
jaw_2 = 0,
|
||||
chin_1 = 0,
|
||||
chin_2 = 0,
|
||||
chin_13 = 0,
|
||||
chin_3 = 0,
|
||||
chin_4 = 0,
|
||||
neck_thickness = 0,
|
||||
hair_1 = 76,
|
||||
@@ -147,7 +147,7 @@ else
|
||||
jaw_2 = 0,
|
||||
chin_1 = -10,
|
||||
chin_2 = 10,
|
||||
chin_13 = -10,
|
||||
chin_3 = -10,
|
||||
chin_4 = 0,
|
||||
neck_thickness = -5,
|
||||
hair_1 = 43,
|
||||
|
||||
@@ -2,7 +2,7 @@ fx_version 'cerulean'
|
||||
game 'gta5'
|
||||
author 'ESX-Framework - Linden - KASH'
|
||||
description 'Allows players to have multiple characters on the same account.'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
dependencies { 'es_extended', 'esx_context', 'esx_identity', 'esx_skin' }
|
||||
|
||||
@@ -101,9 +101,17 @@ function Database:GetPlayerSlots(identifier)
|
||||
end
|
||||
|
||||
function Database:GetPlayerInfo(identifier, slots)
|
||||
local placeholders = {}
|
||||
local identifiers = {}
|
||||
|
||||
for i = 1, slots do
|
||||
placeholders[i] = "?"
|
||||
identifiers[i] = ("%s%s:%s"):format(Server.prefix, i, identifier)
|
||||
end
|
||||
|
||||
return MySQL.query.await(
|
||||
"SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier LIKE ? LIMIT ?",
|
||||
{ identifier, slots })
|
||||
("SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier IN (%s)"):format(table.concat(placeholders, ", ")),
|
||||
identifiers)
|
||||
end
|
||||
|
||||
function Database:SetSlots(identifier, slots)
|
||||
|
||||
@@ -14,7 +14,6 @@ function Multicharacter:SetupCharacters(source)
|
||||
ESX.Players[identifier] = source
|
||||
|
||||
local slots = Database:GetPlayerSlots(identifier)
|
||||
identifier = Server.prefix .. "%:" .. identifier
|
||||
|
||||
local rawCharacters = Database:GetPlayerInfo(identifier, slots)
|
||||
local characters
|
||||
|
||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
||||
|
||||
lua54 'yes'
|
||||
game 'gta5'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
author 'ESX-Framework'
|
||||
description 'A beautiful and simple NUI notification system for ESX'
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
---@field public onFinish? function
|
||||
|
||||
local CurrentProgress = nil
|
||||
local progressCount = 0
|
||||
|
||||
local function startProcessing()
|
||||
while (CurrentProgress ~= nil) do
|
||||
if CurrentProgress.length > 0 then
|
||||
CurrentProgress.length = CurrentProgress.length - 1000
|
||||
local function startProcessing(id)
|
||||
while CurrentProgress ~= nil and CurrentProgress.id == id do
|
||||
if GetGameTimer() < CurrentProgress.finishAt then
|
||||
Wait(50)
|
||||
else
|
||||
ClearPedTasks(ESX.PlayerData.ped)
|
||||
if CurrentProgress.FreezePlayer then
|
||||
@@ -24,7 +25,6 @@ local function startProcessing()
|
||||
end
|
||||
CurrentProgress = nil
|
||||
end
|
||||
Wait(1000)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,8 +55,15 @@ local function Progressbar(message, length, Options)
|
||||
length = length or 3000,
|
||||
message = message or "ESX-Framework",
|
||||
})
|
||||
progressCount = progressCount + 1
|
||||
CurrentProgress.id = progressCount
|
||||
CurrentProgress.length = length or 3000
|
||||
CreateThread(startProcessing);
|
||||
CurrentProgress.finishAt = GetGameTimer() + CurrentProgress.length
|
||||
|
||||
local id = progressCount
|
||||
CreateThread(function()
|
||||
startProcessing(id)
|
||||
end)
|
||||
return true;
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
||||
game 'gta5'
|
||||
author 'ESX-Framework'
|
||||
description 'A beautiful and simple NUI progress bar for ESX'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
client_scripts { 'Progress.lua' }
|
||||
|
||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
||||
|
||||
game 'gta5'
|
||||
description 'Allows players to customise their character\'s appearance'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
shared_scripts {
|
||||
|
||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
||||
game 'gta5'
|
||||
author 'ESX-Framework'
|
||||
description 'A beautiful and simple Persistent Notification system for ESX.'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
client_scripts { 'TextUI.lua' }
|
||||
|
||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
||||
|
||||
game 'gta5'
|
||||
description 'Saves/loads character appearances for ESX Legacy.'
|
||||
version '1.13.5'
|
||||
version '1.14.0'
|
||||
lua54 'yes'
|
||||
|
||||
client_scripts {
|
||||
|
||||
@@ -53,6 +53,7 @@ add_ace resource.es_extended command.stop allow
|
||||
ensure oxmysql
|
||||
|
||||
## ESX Legacy
|
||||
ensure esx_lib
|
||||
ensure es_extended
|
||||
ensure [core]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user