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_cardealer', 'Cardealer', 1),
|
||||||
('society_mechanic', 'Mechanic', 1),
|
('society_mechanic', 'Mechanic', 1),
|
||||||
('society_police', 'Police', 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,
|
`identifier` varchar(60) NOT NULL,
|
||||||
`sender` varchar(60) NOT NULL,
|
`sender` varchar(60) NOT NULL,
|
||||||
`target_type` varchar(50) NOT NULL,
|
`target_type` varchar(50) NOT NULL,
|
||||||
`target` varchar(40) NOT NULL,
|
`target` varchar(60) NOT NULL,
|
||||||
`label` varchar(255) NOT NULL,
|
`label` varchar(255) NOT NULL,
|
||||||
`amount` int(11) NOT NULL
|
`amount` int(11) NOT NULL
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
@@ -364,7 +365,7 @@ CREATE TABLE `rented_vehicles` (
|
|||||||
`player_name` varchar(255) NOT NULL,
|
`player_name` varchar(255) NOT NULL,
|
||||||
`base_price` int(11) NOT NULL,
|
`base_price` int(11) NOT NULL,
|
||||||
`rent_price` int(11) NOT NULL,
|
`rent_price` int(11) NOT NULL,
|
||||||
`owner` varchar(22) NOT NULL
|
`owner` varchar(60) NOT NULL
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -749,7 +750,8 @@ ALTER TABLE `addon_account`
|
|||||||
ALTER TABLE `addon_account_data`
|
ALTER TABLE `addon_account_data`
|
||||||
ADD PRIMARY KEY (`id`),
|
ADD PRIMARY KEY (`id`),
|
||||||
ADD UNIQUE KEY `index_addon_account_data_account_name_owner` (`account_name`,`owner`),
|
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`
|
-- Indexes for table `addon_inventory`
|
||||||
@@ -764,13 +766,15 @@ ALTER TABLE `addon_inventory_items`
|
|||||||
ADD PRIMARY KEY (`id`),
|
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` (`inventory_name`,`name`),
|
||||||
ADD KEY `index_addon_inventory_items_inventory_name_name_owner` (`inventory_name`,`name`,`owner`),
|
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`
|
-- Indexes for table `billing`
|
||||||
--
|
--
|
||||||
ALTER TABLE `billing`
|
ALTER TABLE `billing`
|
||||||
ADD PRIMARY KEY (`id`);
|
ADD PRIMARY KEY (`id`),
|
||||||
|
ADD KEY `esx_billing_identifier` (`identifier`);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Indexes for table `cardealer_vehicles`
|
-- Indexes for table `cardealer_vehicles`
|
||||||
@@ -790,7 +794,8 @@ ALTER TABLE `datastore`
|
|||||||
ALTER TABLE `datastore_data`
|
ALTER TABLE `datastore_data`
|
||||||
ADD PRIMARY KEY (`id`),
|
ADD PRIMARY KEY (`id`),
|
||||||
ADD UNIQUE KEY `index_datastore_data_name_owner` (`name`,`owner`),
|
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`
|
-- Indexes for table `items`
|
||||||
@@ -821,7 +826,8 @@ ALTER TABLE `licenses`
|
|||||||
-- Indexes for table `owned_vehicles`
|
-- Indexes for table `owned_vehicles`
|
||||||
--
|
--
|
||||||
ALTER 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`
|
-- Indexes for table `society_moneywash`
|
||||||
--
|
--
|
||||||
ALTER 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`
|
-- Indexes for table `users`
|
||||||
@@ -856,7 +863,8 @@ ALTER TABLE `users`
|
|||||||
-- Indexes for table `user_licenses`
|
-- Indexes for table `user_licenses`
|
||||||
--
|
--
|
||||||
ALTER 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`
|
-- Indexes for table `vehicle_categories`
|
||||||
@@ -991,27 +999,6 @@ INSERT INTO `fine_types` (label, amount, category) VALUES
|
|||||||
('Fraud', 2000, 2);
|
('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
|
-- ESX Banking
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ game 'gta5'
|
|||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'Allows resources to Run tasks at specific intervals.'
|
description 'Allows resources to Run tasks at specific intervals.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
server_script 'server/main.lua'
|
server_script 'server/main.lua'
|
||||||
|
|||||||
@@ -26,19 +26,30 @@ end
|
|||||||
|
|
||||||
---@param timestamp number
|
---@param timestamp number
|
||||||
function OnTime(timestamp)
|
function OnTime(timestamp)
|
||||||
for i = 1, #cronJobs, 1 do
|
local function scheduledAt(job, dayOffset)
|
||||||
local scheduledTimestamp = os.time({
|
return os.time({
|
||||||
hour = cronJobs[i].h,
|
hour = job.h,
|
||||||
min = cronJobs[i].m,
|
min = job.m,
|
||||||
sec = 0, -- Assuming tasks run at the start of the minute
|
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),
|
month = os.date("%m", timestamp),
|
||||||
year = os.date("%Y", 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
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ function Adjustments:DisableNPCDrops()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Adjustments:SeatShuffle()
|
function Adjustments:SeatShuffle()
|
||||||
if Config.DisableVehicleSeatShuff then
|
if Config.DisableVehicleSeatShuff and not self.seatShuffleRegistered then
|
||||||
|
self.seatShuffleRegistered = true
|
||||||
|
|
||||||
AddEventHandler("esx:enteredVehicle", function(vehicle, _, seat)
|
AddEventHandler("esx:enteredVehicle", function(vehicle, _, seat)
|
||||||
if seat > -1 then
|
if seat > -1 then
|
||||||
SetPedIntoVehicle(ESX.PlayerData.ped, vehicle, seat)
|
SetPedIntoVehicle(ESX.PlayerData.ped, vehicle, seat)
|
||||||
@@ -43,7 +45,7 @@ end
|
|||||||
|
|
||||||
function Adjustments:AmmoAndVehicleRewards()
|
function Adjustments:AmmoAndVehicleRewards()
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while ESX.PlayerLoaded do
|
||||||
if Config.DisableDisplayAmmo then
|
if Config.DisableDisplayAmmo then
|
||||||
DisplayAmmoThisFrame(false)
|
DisplayAmmoThisFrame(false)
|
||||||
end
|
end
|
||||||
@@ -175,11 +177,11 @@ function Adjustments:ReplacePlaceholders(text)
|
|||||||
local success, result = pcall(cb)
|
local success, result = pcall(cb)
|
||||||
|
|
||||||
if not success then
|
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"
|
result = "Unknown"
|
||||||
end
|
end
|
||||||
|
|
||||||
text = text:gsub(("{%s}"):format(placeholder), tostring(result))
|
text = text:gsub(("{%s}"):format(placeholder), (tostring(result):gsub("%%", "%%%%")))
|
||||||
end
|
end
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
@@ -187,7 +189,7 @@ end
|
|||||||
function Adjustments:DiscordPresence()
|
function Adjustments:DiscordPresence()
|
||||||
if Config.DiscordActivity.appId ~= 0 then
|
if Config.DiscordActivity.appId ~= 0 then
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while ESX.PlayerLoaded do
|
||||||
SetDiscordAppId(Config.DiscordActivity.appId)
|
SetDiscordAppId(Config.DiscordActivity.appId)
|
||||||
SetRichPresence(self:ReplacePlaceholders(Config.DiscordActivity.presence))
|
SetRichPresence(self:ReplacePlaceholders(Config.DiscordActivity.presence))
|
||||||
SetDiscordRichPresenceAsset(Config.DiscordActivity.assetName)
|
SetDiscordRichPresenceAsset(Config.DiscordActivity.assetName)
|
||||||
@@ -213,7 +215,9 @@ function Adjustments:WantedLevel()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Adjustments:DisableRadio()
|
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)
|
AddEventHandler("esx:enteredVehicle", function(vehicle, plate, seat, displayName, netId)
|
||||||
SetVehRadioStation(vehicle,"OFF")
|
SetVehRadioStation(vehicle,"OFF")
|
||||||
SetUserRadioControlEnabled(false)
|
SetUserRadioControlEnabled(false)
|
||||||
@@ -223,7 +227,7 @@ end
|
|||||||
|
|
||||||
function Adjustments:Multipliers()
|
function Adjustments:Multipliers()
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while ESX.PlayerLoaded do
|
||||||
SetPedDensityMultiplierThisFrame(Config.Multipliers.pedDensity)
|
SetPedDensityMultiplierThisFrame(Config.Multipliers.pedDensity)
|
||||||
SetScenarioPedDensityMultiplierThisFrame(Config.Multipliers.scenarioPedDensityInterior, Config.Multipliers.scenarioPedDensityExterior)
|
SetScenarioPedDensityMultiplierThisFrame(Config.Multipliers.scenarioPedDensityInterior, Config.Multipliers.scenarioPedDensityExterior)
|
||||||
SetAmbientVehicleRangeMultiplierThisFrame(Config.Multipliers.ambientVehicleRange)
|
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
|
-- if the server callback takes longer than 15 seconds to respond, reject the promise
|
||||||
SetTimeout(15000, function()
|
SetTimeout(15000, function()
|
||||||
if p.state == "pending" then
|
if p.state == 0 then
|
||||||
p:reject("Server Callback Timed Out")
|
p:reject("Server Callback Timed Out")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -372,7 +372,6 @@ if not Config.CustomInventory then
|
|||||||
while true do
|
while true do
|
||||||
local Sleep = 1500
|
local Sleep = 1500
|
||||||
local playerCoords = GetEntityCoords(ESX.PlayerData.ped)
|
local playerCoords = GetEntityCoords(ESX.PlayerData.ped)
|
||||||
local _, closestDistance = ESX.Game.GetClosestPlayer(playerCoords)
|
|
||||||
|
|
||||||
for pickupId, pickup in pairs(pickups) do
|
for pickupId, pickup in pairs(pickups) do
|
||||||
local distance = #(playerCoords - pickup.coords)
|
local distance = #(playerCoords - pickup.coords)
|
||||||
@@ -383,6 +382,8 @@ if not Config.CustomInventory then
|
|||||||
|
|
||||||
if distance < 1 then
|
if distance < 1 then
|
||||||
if IsControlJustReleased(0, 38) 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
|
if IsPedOnFoot(ESX.PlayerData.ped) and (closestDistance == -1 or closestDistance > 3) and not pickup.inRange then
|
||||||
pickup.inRange = true
|
pickup.inRange = true
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'cerulean'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'The Core resource that provides the functionalities for all other resources.'
|
description 'The Core resource that provides the functionalities for all other resources.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
shared_scripts {
|
shared_scripts {
|
||||||
'@esx_lib/imports.lua',
|
'@esx_lib/imports.lua',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function Translate(str, ...) -- Translate string
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TranslateCap(str, ...) -- Translate string first char uppercase
|
function TranslateCap(str, ...) -- Translate string first char uppercase
|
||||||
return _(str, ...):gsub("^%l", string.upper)
|
return (_(str, ...):gsub("^%l", string.upper))
|
||||||
end
|
end
|
||||||
|
|
||||||
_ = Translate
|
_ = Translate
|
||||||
|
|||||||
@@ -15,57 +15,84 @@ local requiredMethods = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
---Parses the inventory:accounts convar to build a lookup table.
|
---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>
|
---@return table<string, boolean>
|
||||||
local function getAccountList()
|
local function getAccountList()
|
||||||
local accounts = {}
|
|
||||||
local convar = GetConvar("inventory:accounts", '["money"]')
|
local convar = GetConvar("inventory:accounts", '["money"]')
|
||||||
local ok, list = pcall(json.decode, convar)
|
local ok, list = pcall(json.decode, convar)
|
||||||
|
|
||||||
if ok and type(list) == "table" then
|
if not ok or type(list) ~= "table" then
|
||||||
for i = 1, #list do
|
error(
|
||||||
accounts[list[i]] = true
|
("[es_extended] Invalid inventory:accounts convar: %s")
|
||||||
end
|
:format(tostring(convar)),
|
||||||
else
|
2
|
||||||
-- Fallback for non-JSON formats (e.g. "money" or "money black_money")
|
)
|
||||||
for account in convar:gmatch("[%w_]+") do
|
end
|
||||||
if account ~= "[]" then
|
|
||||||
accounts[account] = true
|
local accounts = {}
|
||||||
end
|
|
||||||
|
for i = 1, #list do
|
||||||
|
local account = list[i]
|
||||||
|
|
||||||
|
if type(account) == "string" and account ~= "" then
|
||||||
|
accounts[account] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
end
|
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
|
---@return table
|
||||||
local function createOxInventoryProxy()
|
local function createOxInventoryProxy()
|
||||||
local proxy = {}
|
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
|
for i = 1, #requiredMethods do
|
||||||
local method = requiredMethods[i]
|
local method = requiredMethods[i]
|
||||||
local ok, err = pcall(function()
|
local exportFn = resolveExport(resourceExports, method)
|
||||||
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
|
|
||||||
|
|
||||||
proxy[method] = function(...)
|
proxy[method] = function(...)
|
||||||
return exports.ox_inventory[method](exports.ox_inventory, ...)
|
return exportFn(resourceExports, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
proxy.accounts = accounts
|
proxy.accounts = getAccountList()
|
||||||
|
|
||||||
return proxy
|
return proxy
|
||||||
end
|
end
|
||||||
|
|
||||||
---Checks if a module table contains all required methods.
|
---Checks if an Inventory() module provides everything ESX requires.
|
||||||
---@param module table
|
---@param module table
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function isValidModule(module)
|
local function isValidModule(module)
|
||||||
@@ -79,12 +106,15 @@ local function isValidModule(module)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if type(module.accounts) ~= "table" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
---Returns the ox_inventory interface.
|
---Returns the ox_inventory interface.
|
||||||
---Tries the legacy Inventory() export first for backward compatibility,
|
---Uses Inventory() when available, otherwise falls back to public exports.
|
||||||
---then falls back to direct export calls.
|
|
||||||
---@return table
|
---@return table
|
||||||
local function getOxInventory()
|
local function getOxInventory()
|
||||||
if OxInventory then
|
if OxInventory then
|
||||||
|
|||||||
@@ -505,6 +505,10 @@ function CreateExtendedPlayer(playerId, identifier, ssn, group, accounts, invent
|
|||||||
if item and count >= 0 then
|
if item and count >= 0 then
|
||||||
count = ESX.Math.Round(count)
|
count = ESX.Math.Round(count)
|
||||||
|
|
||||||
|
if count == item.count then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if count > item.count then
|
if count > item.count then
|
||||||
self.addInventoryItem(item.name, count - item.count)
|
self.addInventoryItem(item.name, count - item.count)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ Core.vehicleClass = {
|
|||||||
vehicleData.plate = newPlate
|
vehicleData.plate = newPlate
|
||||||
Core.vehicles[newPlate] = table.clone(vehicleData)
|
Core.vehicles[newPlate] = table.clone(vehicleData)
|
||||||
Core.vehicles[oldPlate] = nil
|
Core.vehicles[oldPlate] = nil
|
||||||
|
self.plate = newPlate
|
||||||
|
|
||||||
TriggerEvent("esx:changedExtendedVehiclePlate", vehicleData.plate, oldPlate)
|
TriggerEvent("esx:changedExtendedVehiclePlate", vehicleData.plate, oldPlate)
|
||||||
Wait(0)
|
Wait(0)
|
||||||
@@ -167,7 +168,7 @@ Core.vehicleClass = {
|
|||||||
assert(type(newProps) == "table", "Expected 'props' to be a table")
|
assert(type(newProps) == "table", "Expected 'props' to be a table")
|
||||||
|
|
||||||
local vehicleData = Core.vehicles[self.plate]
|
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
|
if affectedRows <= 0 then
|
||||||
self:delete()
|
self:delete()
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion)
|
|||||||
end
|
end
|
||||||
local merge = table.concat(args, " ")
|
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
|
elseif v.type == "coordinate" then
|
||||||
local coord = tonumber(args[k]:match("(-?%d+%.?%d*)"))
|
local coord = tonumber(args[k]:match("(-?%d+%.?%d*)"))
|
||||||
if not coord then
|
if not coord then
|
||||||
@@ -251,18 +251,24 @@ function Core.SavePlayers(cb)
|
|||||||
local parameters = {}
|
local parameters = {}
|
||||||
|
|
||||||
for _, xPlayer in pairs(ESX.Players) do
|
for _, xPlayer in pairs(ESX.Players) do
|
||||||
updateHealthAndArmorInMetadata(xPlayer)
|
if xPlayer.spawned then
|
||||||
parameters[#parameters + 1] = {
|
updateHealthAndArmorInMetadata(xPlayer)
|
||||||
json.encode(xPlayer.getAccounts(true)),
|
parameters[#parameters + 1] = {
|
||||||
xPlayer.job.name,
|
json.encode(xPlayer.getAccounts(true)),
|
||||||
xPlayer.job.grade,
|
xPlayer.job.name,
|
||||||
xPlayer.group,
|
xPlayer.job.grade,
|
||||||
json.encode(xPlayer.getCoords(false, true)),
|
xPlayer.group,
|
||||||
json.encode(xPlayer.getInventory(true)),
|
json.encode(xPlayer.getCoords(false, true)),
|
||||||
json.encode(xPlayer.getLoadout(true)),
|
json.encode(xPlayer.getInventory(true)),
|
||||||
json.encode(xPlayer.getMeta()),
|
json.encode(xPlayer.getLoadout(true)),
|
||||||
xPlayer.identifier,
|
json.encode(xPlayer.getMeta()),
|
||||||
}
|
xPlayer.identifier,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not parameters[1] then
|
||||||
|
return cb and cb()
|
||||||
end
|
end
|
||||||
|
|
||||||
MySQL.prepare(
|
MySQL.prepare(
|
||||||
@@ -799,9 +805,15 @@ if not Config.CustomInventory then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if #toInsert > 0 then
|
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(
|
MySQL.prepare.await(
|
||||||
"INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)",
|
"INSERT IGNORE INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES (?, ?, ?, ?, ?)",
|
||||||
toInsert)
|
parameters)
|
||||||
|
|
||||||
for i = 1, #toInsert do
|
for i = 1, #toInsert do
|
||||||
local row = toInsert[i]
|
local row = toInsert[i]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ SetMapName("San Andreas")
|
|||||||
SetGameType("ESX Legacy")
|
SetGameType("ESX Legacy")
|
||||||
|
|
||||||
local oneSyncState = GetConvar("onesync", "off")
|
local oneSyncState = GetConvar("onesync", "off")
|
||||||
|
local isEnhanced = xLib.isEnhanced()
|
||||||
local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `ssn` = ?, `group` = ?"
|
local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `ssn` = ?, `group` = ?"
|
||||||
local loadPlayer = "SELECT `accounts`, `ssn`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`"
|
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."))
|
return deferrals.done(("[ESX] ESX Requires a minimum Artifact version of 10188, Please update your server."))
|
||||||
end
|
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))
|
return deferrals.done(("[ESX] ESX Requires Onesync Infinity to work. This server currently has Onesync set to: %s"):format(oneSyncState))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -288,9 +289,9 @@ function loadESXPlayer(identifier, playerId, isNew)
|
|||||||
|
|
||||||
local loadout = json.decode(result.loadout)
|
local loadout = json.decode(result.loadout)
|
||||||
for name, weapon in pairs(loadout) do
|
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] = {
|
userData.loadout[#userData.loadout + 1] = {
|
||||||
name = name,
|
name = name,
|
||||||
ammo = weapon.ammo,
|
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)]
|
userData.coords = json.decode(result.position) or Config.DefaultSpawns[ESX.Math.Random(1,#Config.DefaultSpawns)]
|
||||||
|
|
||||||
-- Skin
|
-- 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
|
-- Metadata
|
||||||
userData.metadata = (result.metadata and result.metadata ~= "") and json.decode(result.metadata) or {}
|
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 weaponComponents = ESX.Table.Clone(weapon.components)
|
||||||
local weaponTint = weapon.tintIndex
|
local weaponTint = weapon.tintIndex
|
||||||
|
|
||||||
|
sourceXPlayer.removeWeapon(itemName)
|
||||||
|
targetXPlayer.addWeapon(itemName, itemCount)
|
||||||
|
|
||||||
if weaponTint then
|
if weaponTint then
|
||||||
targetXPlayer.setWeaponTint(itemName, weaponTint)
|
targetXPlayer.setWeaponTint(itemName, weaponTint)
|
||||||
end
|
end
|
||||||
@@ -484,9 +488,6 @@ if not Config.CustomInventory then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sourceXPlayer.removeWeapon(itemName)
|
|
||||||
targetXPlayer.addWeapon(itemName, itemCount)
|
|
||||||
|
|
||||||
if weaponObject.ammo and itemCount > 0 then
|
if weaponObject.ammo and itemCount > 0 then
|
||||||
local ammoLabel = weaponObject.ammo.label
|
local ammoLabel = weaponObject.ammo.label
|
||||||
sourceXPlayer.showNotification(TranslateCap("gave_weapon_withammo", weaponLabel, itemCount, ammoLabel, targetXPlayer.name))
|
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
|
if not p then return end
|
||||||
|
|
||||||
SetTimeout(15000, function()
|
SetTimeout(15000, function()
|
||||||
if p.state == "pending" then
|
if p.state == 0 then
|
||||||
p:reject("Server Callback Timed Out")
|
p:reject("Server Callback Timed Out")
|
||||||
end
|
end
|
||||||
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(
|
ESX.RegisterCommand(
|
||||||
{ "setcoords", "tp" },
|
{ "setcoords", "tp" },
|
||||||
"admin",
|
FilterGroups(CommandPermissions.setcoords),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
xPlayer.setCoords({ x = args.x, y = args.y, z = args.z })
|
xPlayer.setCoords({ x = args.x, y = args.y, z = args.z })
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
@@ -27,7 +33,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"setjob",
|
"setjob",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.setjob),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not ESX.DoesJobExist(args.job, args.grade) then
|
if not ESX.DoesJobExist(args.job, args.grade) then
|
||||||
return showError(TranslateCap("command_setjob_invalid"))
|
return showError(TranslateCap("command_setjob_invalid"))
|
||||||
@@ -68,7 +74,7 @@ local upgrades = Config.SpawnVehMaxUpgrades and {
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"car",
|
"car",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.car),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not xPlayer then
|
if not xPlayer then
|
||||||
return showError("[^1ERROR^7] The xPlayer value is nil")
|
return showError("[^1ERROR^7] The xPlayer value is nil")
|
||||||
@@ -132,7 +138,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
{ "cardel", "dv" },
|
{ "cardel", "dv" },
|
||||||
"admin",
|
FilterGroups(CommandPermissions.cardel),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
local ped = GetPlayerPed(xPlayer.source)
|
local ped = GetPlayerPed(xPlayer.source)
|
||||||
local pedVehicle = GetVehiclePedIsIn(ped, false)
|
local pedVehicle = GetVehiclePedIsIn(ped, false)
|
||||||
@@ -168,7 +174,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
{ "fix", "repair" },
|
{ "fix", "repair" },
|
||||||
"admin",
|
FilterGroups(CommandPermissions.fix),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
local xTarget = args.playerId
|
local xTarget = args.playerId
|
||||||
local ped = GetPlayerPed(xTarget.source)
|
local ped = GetPlayerPed(xTarget.source)
|
||||||
@@ -178,9 +184,11 @@ ESX.RegisterCommand(
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
xTarget.triggerEvent("esx:repairPedVehicle")
|
xTarget.triggerEvent("esx:repairPedVehicle")
|
||||||
xPlayer.showNotification(TranslateCap("command_repair_success"), true, false, 140)
|
if xPlayer then
|
||||||
if xPlayer.source ~= xTarget.source then
|
xPlayer.showNotification(TranslateCap("command_repair_success"))
|
||||||
xTarget.showNotification(TranslateCap("command_repair_success_target"), true, false, 140)
|
end
|
||||||
|
if not xPlayer or xPlayer.source ~= xTarget.source then
|
||||||
|
xTarget.showNotification(TranslateCap("command_repair_success_target"))
|
||||||
end
|
end
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
ESX.DiscordLogFields("UserActions", "Fix Vehicle /fix Triggered!", "pink", {
|
ESX.DiscordLogFields("UserActions", "Fix Vehicle /fix Triggered!", "pink", {
|
||||||
@@ -202,7 +210,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"setaccountmoney",
|
"setaccountmoney",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.setaccountmoney),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not args.playerId.getAccount(args.account) then
|
if not args.playerId.getAccount(args.account) then
|
||||||
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
||||||
@@ -232,7 +240,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"giveaccountmoney",
|
"giveaccountmoney",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.giveaccountmoney),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not args.playerId.getAccount(args.account) then
|
if not args.playerId.getAccount(args.account) then
|
||||||
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
return showError(TranslateCap("command_giveaccountmoney_invalid"))
|
||||||
@@ -262,7 +270,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"removeaccountmoney",
|
"removeaccountmoney",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.removeaccountmoney),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not args.playerId.getAccount(args.account) then
|
if not args.playerId.getAccount(args.account) then
|
||||||
return showError(TranslateCap("command_removeaccountmoney_invalid"))
|
return showError(TranslateCap("command_removeaccountmoney_invalid"))
|
||||||
@@ -293,7 +301,7 @@ ESX.RegisterCommand(
|
|||||||
if not Config.CustomInventory then
|
if not Config.CustomInventory then
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"giveitem",
|
"giveitem",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.giveitem),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
args.playerId.addInventoryItem(args.item, args.count)
|
args.playerId.addInventoryItem(args.item, args.count)
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
@@ -323,7 +331,7 @@ if not Config.CustomInventory then
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"giveweapon",
|
"giveweapon",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.giveweapon),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if args.playerId.hasWeapon(args.weapon) then
|
if args.playerId.hasWeapon(args.weapon) then
|
||||||
return showError(TranslateCap("command_giveweapon_hasalready"))
|
return showError(TranslateCap("command_giveweapon_hasalready"))
|
||||||
@@ -353,7 +361,7 @@ if not Config.CustomInventory then
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"giveammo",
|
"giveammo",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.giveammo),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if not args.playerId.hasWeapon(args.weapon) then
|
if not args.playerId.hasWeapon(args.weapon) then
|
||||||
return showError(TranslateCap("command_giveammo_noweapon_found"))
|
return showError(TranslateCap("command_giveammo_noweapon_found"))
|
||||||
@@ -383,7 +391,7 @@ if not Config.CustomInventory then
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"giveweaponcomponent",
|
"giveweaponcomponent",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.giveweaponcomponent),
|
||||||
function(xPlayer, args, showError)
|
function(xPlayer, args, showError)
|
||||||
if args.playerId.hasWeapon(args.weaponName) then
|
if args.playerId.hasWeapon(args.weaponName) then
|
||||||
local component = ESX.GetWeaponComponent(args.weaponName, args.componentName)
|
local component = ESX.GetWeaponComponent(args.weaponName, args.componentName)
|
||||||
@@ -423,11 +431,11 @@ if not Config.CustomInventory then
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
ESX.RegisterCommand({ "clear", "cls" }, "user", function(xPlayer)
|
ESX.RegisterCommand({ "clear", "cls" }, FilterGroups(CommandPermissions.clear), function(xPlayer)
|
||||||
xPlayer.triggerEvent("chat:clear")
|
xPlayer.triggerEvent("chat:clear")
|
||||||
end, false, { help = TranslateCap("command_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)
|
TriggerClientEvent("chat:clear", -1)
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
ESX.DiscordLogFields("UserActions", "Clear Chat /clearall Triggered!", "pink", {
|
ESX.DiscordLogFields("UserActions", "Clear Chat /clearall Triggered!", "pink", {
|
||||||
@@ -437,20 +445,24 @@ ESX.RegisterCommand({ "clearall", "clsall" }, "admin", function(xPlayer)
|
|||||||
end
|
end
|
||||||
end, true, { help = TranslateCap("command_clearall") })
|
end, true, { help = TranslateCap("command_clearall") })
|
||||||
|
|
||||||
ESX.RegisterCommand("refreshjobs", "admin", function()
|
ESX.RegisterCommand("refreshjobs", FilterGroups(CommandPermissions.refreshjobs), function()
|
||||||
ESX.RefreshJobs()
|
ESX.RefreshJobs()
|
||||||
end, true, { help = TranslateCap("command_clearall") })
|
end, true, { help = TranslateCap("command_clearall") })
|
||||||
|
|
||||||
if not Config.CustomInventory then
|
if not Config.CustomInventory then
|
||||||
ESX.RegisterCommand("refreshitems", "admin", function(xPlayer)
|
ESX.RegisterCommand("refreshitems", FilterGroups(CommandPermissions.refreshitems), function(xPlayer)
|
||||||
local itemCount = ESX.RefreshItems()
|
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") })
|
end, true, { help = TranslateCap("command_refreshitems") })
|
||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"clearinventory",
|
"clearinventory",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.clearinventory),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
for _, v in ipairs(args.playerId.inventory) do
|
for _, v in ipairs(args.playerId.inventory) do
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
@@ -478,7 +490,7 @@ if not Config.CustomInventory then
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"clearloadout",
|
"clearloadout",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.clearloadout),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
for i = #args.playerId.loadout, 1, -1 do
|
for i = #args.playerId.loadout, 1, -1 do
|
||||||
args.playerId.removeWeapon(args.playerId.loadout[i].name)
|
args.playerId.removeWeapon(args.playerId.loadout[i].name)
|
||||||
@@ -505,7 +517,7 @@ end
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"setgroup",
|
"setgroup",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.setgroup),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
if not args.playerId then
|
if not args.playerId then
|
||||||
args.playerId = xPlayer.source
|
args.playerId = xPlayer.source
|
||||||
@@ -537,7 +549,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"save",
|
"save",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.save),
|
||||||
function(_, args)
|
function(_, args)
|
||||||
Core.SavePlayer(args.playerId)
|
Core.SavePlayer(args.playerId)
|
||||||
print(("[^2Info^0] Saved Player - ^5%s^0"):format(args.playerId.source))
|
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()
|
Core.SavePlayers()
|
||||||
end, true, { help = TranslateCap("command_saveall") })
|
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()))
|
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()
|
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"))
|
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)
|
end, false)
|
||||||
|
|
||||||
ESX.RegisterCommand("info", { "user", "admin" }, function(xPlayer)
|
ESX.RegisterCommand("info", FilterGroups(CommandPermissions.info), function(xPlayer)
|
||||||
local job = xPlayer.getJob().name
|
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))
|
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)
|
end, false)
|
||||||
|
|
||||||
ESX.RegisterCommand("playtime", { "user", "admin" }, function(xPlayer)
|
ESX.RegisterCommand("playtime", FilterGroups(CommandPermissions.playtime), function(xPlayer)
|
||||||
local playtime = xPlayer.getPlayTime()
|
local playtime = xPlayer.getPlayTime()
|
||||||
local days = math.floor(playtime / 86400)
|
local days = math.floor(playtime / 86400)
|
||||||
local hours = math.floor((playtime % 86400) / 3600)
|
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))
|
print(("Playtime: ^5%s^0 Days | ^5%s^0 Hours | ^5%s^0 Minutes"):format(days, hours, minutes))
|
||||||
end, false)
|
end, false)
|
||||||
|
|
||||||
ESX.RegisterCommand("coords", "admin", function(xPlayer)
|
ESX.RegisterCommand("coords", FilterGroups(CommandPermissions.coords), function(xPlayer)
|
||||||
local ped = GetPlayerPed(xPlayer.source)
|
local ped = GetPlayerPed(xPlayer.source)
|
||||||
local coords = GetEntityCoords(ped, false)
|
local coords = GetEntityCoords(ped, false)
|
||||||
local heading = GetEntityHeading(ped)
|
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)))
|
print(("Coords - Vector4: ^5%s^0"):format(vector4(coords.x, coords.y, coords.z, heading)))
|
||||||
end, false)
|
end, false)
|
||||||
|
|
||||||
ESX.RegisterCommand("tpm", "admin", function(xPlayer)
|
ESX.RegisterCommand("tpm", FilterGroups(CommandPermissions.tpm), function(xPlayer)
|
||||||
xPlayer.triggerEvent("esx:tpm")
|
xPlayer.triggerEvent("esx:tpm")
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
ESX.DiscordLogFields("UserActions", "Admin Teleport /tpm Triggered!", "pink", {
|
ESX.DiscordLogFields("UserActions", "Admin Teleport /tpm Triggered!", "pink", {
|
||||||
@@ -599,7 +611,7 @@ end, false)
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"goto",
|
"goto",
|
||||||
"admin",
|
FilterGroups(CommandPermissions["goto"]),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
local targetCoords = args.playerId.getCoords()
|
local targetCoords = args.playerId.getCoords()
|
||||||
local srcDim = GetPlayerRoutingBucket(xPlayer.source)
|
local srcDim = GetPlayerRoutingBucket(xPlayer.source)
|
||||||
@@ -630,7 +642,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"bring",
|
"bring",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.bring),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
local targetCoords = args.playerId.getCoords()
|
local targetCoords = args.playerId.getCoords()
|
||||||
local playerCoords = xPlayer.getCoords()
|
local playerCoords = xPlayer.getCoords()
|
||||||
@@ -662,7 +674,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"kill",
|
"kill",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.kill),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
args.playerId.triggerEvent("esx:killPlayer")
|
args.playerId.triggerEvent("esx:killPlayer")
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
@@ -685,7 +697,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"freeze",
|
"freeze",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.freeze),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
args.playerId.triggerEvent("esx:freezePlayer", "freeze")
|
args.playerId.triggerEvent("esx:freezePlayer", "freeze")
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
@@ -708,7 +720,7 @@ ESX.RegisterCommand(
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
"unfreeze",
|
"unfreeze",
|
||||||
"admin",
|
FilterGroups(CommandPermissions.unfreeze),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
args.playerId.triggerEvent("esx:freezePlayer", "unfreeze")
|
args.playerId.triggerEvent("esx:freezePlayer", "unfreeze")
|
||||||
if Config.AdminLogging then
|
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")
|
xPlayer.triggerEvent("esx:noclip")
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
ESX.DiscordLogFields("UserActions", "Admin NoClip /noclip Triggered!", "pink", {
|
ESX.DiscordLogFields("UserActions", "Admin NoClip /noclip Triggered!", "pink", {
|
||||||
@@ -739,7 +751,7 @@ ESX.RegisterCommand("noclip", "admin", function(xPlayer)
|
|||||||
end
|
end
|
||||||
end, false)
|
end, false)
|
||||||
|
|
||||||
ESX.RegisterCommand("players", "admin", function()
|
ESX.RegisterCommand("players", FilterGroups(CommandPermissions.players), function()
|
||||||
local xPlayers = ESX.GetExtendedPlayers() -- Returns all xPlayers
|
local xPlayers = ESX.GetExtendedPlayers() -- Returns all xPlayers
|
||||||
print(("^5%s^2 online player(s)^0"):format(#xPlayers))
|
print(("^5%s^2 online player(s)^0"):format(#xPlayers))
|
||||||
for i = 1, #xPlayers do
|
for i = 1, #xPlayers do
|
||||||
@@ -750,7 +762,7 @@ end, true)
|
|||||||
|
|
||||||
ESX.RegisterCommand(
|
ESX.RegisterCommand(
|
||||||
{"setdim", "setbucket"},
|
{"setdim", "setbucket"},
|
||||||
"admin",
|
FilterGroups(CommandPermissions.setdim),
|
||||||
function(xPlayer, args)
|
function(xPlayer, args)
|
||||||
SetPlayerRoutingBucket(args.playerId.source, args.dimension)
|
SetPlayerRoutingBucket(args.playerId.source, args.dimension)
|
||||||
if Config.AdminLogging then
|
if Config.AdminLogging then
|
||||||
|
|||||||
@@ -5,24 +5,10 @@ local NOTIFY_TYPES = {
|
|||||||
ERROR = "^5[%s]^7-^1[ERROR]^7 %s"
|
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 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
|
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
|
end
|
||||||
|
|
||||||
return job
|
return job
|
||||||
@@ -66,22 +52,47 @@ function ESX.CreateJob(name, label, grades, jobType)
|
|||||||
jobType = "civ"
|
jobType = "civ"
|
||||||
end
|
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
|
if jobExists then
|
||||||
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
|
label, jobType = existingJob.label, existingJob.type
|
||||||
return success
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
local queries = {
|
local queries = {}
|
||||||
{ query = 'INSERT INTO `jobs` (`name`, `label`, `type`) VALUES (?, ?, ?)', values = { name, label, jobType } }
|
|
||||||
}
|
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
|
for _, grade in pairs(grades) do
|
||||||
queries[#queries + 1] = {
|
if not existingGrades[tostring(grade.grade)] then
|
||||||
query = 'INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
local skinMale = grade.skin_male and json.encode(grade.skin_male) or '{}'
|
||||||
values = { name, grade.grade, grade.name, grade.label, grade.salary, grade.skin_male and json.encode(grade.skin_male) or '{}', grade.skin_female and json.encode(grade.skin_female) or '{}' }
|
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, skinMale, skinFemale }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not queries[1] then
|
||||||
|
notify("ERROR",currentResourceName, 'Job or grades already exists: `%s`', name)
|
||||||
|
return success
|
||||||
end
|
end
|
||||||
|
|
||||||
success = exports.oxmysql:transaction_async(queries)
|
success = exports.oxmysql:transaction_async(queries)
|
||||||
@@ -91,7 +102,7 @@ function ESX.CreateJob(name, label, grades, jobType)
|
|||||||
return success
|
return success
|
||||||
end
|
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)
|
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}
|
--{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 = {
|
Config.AdminGroups = {
|
||||||
["owner"] = true,
|
["owner"] = true,
|
||||||
["admin"] = 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.
|
Config.ValidCharacterSets = { -- Only enable additional charsets if your server is multilingual. By default everything is false.
|
||||||
['el'] = false, -- Greek
|
['el'] = false, -- Greek
|
||||||
['sr'] = false, -- Cyrillic
|
['sr'] = false, -- Cyrillic
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ function ESX.IsValidLocaleString(str, allowDigits)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not utf8.len(str) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local locale = string.lower(Config.Locale)
|
local locale = string.lower(Config.Locale)
|
||||||
|
|
||||||
local defaultRanges ={
|
local defaultRanges ={
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'A ESX Stylised theme for the chat resource.'
|
description 'A ESX Stylised theme for the chat resource.'
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ game 'gta5'
|
|||||||
author 'ESX-Framework & Brayden'
|
author 'ESX-Framework & Brayden'
|
||||||
description 'A simplistic context menu for ESX.'
|
description 'A simplistic context menu for ESX.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
ui_page 'index.html'
|
ui_page 'index.html'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'Allows the player to Pick their characters: Name, Gender, Height and Date-of-birth.'
|
description 'Allows the player to Pick their characters: Name, Gender, Height and Date-of-birth.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
shared_scripts {
|
shared_scripts {
|
||||||
'@es_extended/imports.lua',
|
'@es_extended/imports.lua',
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
---@field icon string
|
---@field icon string
|
||||||
---@field count number
|
---@field count number
|
||||||
---@field value string
|
---@field value string
|
||||||
---@field usable boolean
|
---@field canUse boolean?
|
||||||
|
---@field usable boolean?
|
||||||
---@field rare boolean
|
---@field rare boolean
|
||||||
---@field canRemove boolean
|
---@field canRemove boolean
|
||||||
---@field unselectable boolean?
|
---@field unselectable boolean?
|
||||||
@@ -34,7 +35,6 @@ local function appendAccounts(elements)
|
|||||||
icon = "fas fa-money-bill-wave",
|
icon = "fas fa-money-bill-wave",
|
||||||
count = account.money,
|
count = account.money,
|
||||||
value = account.name,
|
value = account.name,
|
||||||
usable = false,
|
|
||||||
rare = false,
|
rare = false,
|
||||||
canRemove = canDrop
|
canRemove = canDrop
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ local function appendItems(elements)
|
|||||||
icon = "fas fa-box",
|
icon = "fas fa-box",
|
||||||
count = item.count,
|
count = item.count,
|
||||||
value = item.name,
|
value = item.name,
|
||||||
usable = item.usable,
|
canUse = item.usable,
|
||||||
rare = item.rare,
|
rare = item.rare,
|
||||||
canRemove = item.canRemove
|
canRemove = item.canRemove
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,6 @@ local function appendLoadout(elements)
|
|||||||
icon = "fas fa-gun",
|
icon = "fas fa-gun",
|
||||||
count = 1,
|
count = 1,
|
||||||
value = weapon.name,
|
value = weapon.name,
|
||||||
usable = false,
|
|
||||||
rare = false,
|
rare = false,
|
||||||
ammo = ammo,
|
ammo = ammo,
|
||||||
canGiveAmmo = (weapon.ammo ~= nil),
|
canGiveAmmo = (weapon.ammo ~= nil),
|
||||||
@@ -119,7 +118,7 @@ end
|
|||||||
---@return table
|
---@return table
|
||||||
local function buildItemActionMenu(selected, playerNearby)
|
local function buildItemActionMenu(selected, playerNearby)
|
||||||
local elements2 = {}
|
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 }
|
elements2[#elements2 + 1] = { action = "use", label = TranslateCap("use"), icon = "fas fa-utensils", type = selected.type, value = selected.value }
|
||||||
end
|
end
|
||||||
if selected.canRemove then
|
if selected.canRemove then
|
||||||
@@ -146,7 +145,6 @@ local function openQuantityDialog(title, maxCount)
|
|||||||
local qty = tonumber(data.value)
|
local qty = tonumber(data.value)
|
||||||
if not qty or qty <= 0 or qty > maxCount then
|
if not qty or qty <= 0 or qty > maxCount then
|
||||||
ESX.ShowNotification(TranslateCap("amount_invalid"))
|
ESX.ShowNotification(TranslateCap("amount_invalid"))
|
||||||
p:resolve(nil)
|
|
||||||
else
|
else
|
||||||
menu.close()
|
menu.close()
|
||||||
p:resolve(qty)
|
p:resolve(qty)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ game "gta5"
|
|||||||
description "Inventory for the ESX framework"
|
description "Inventory for the ESX framework"
|
||||||
lua54 "yes"
|
lua54 "yes"
|
||||||
use_fxv2_oal "yes"
|
use_fxv2_oal "yes"
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
shared_scripts {
|
shared_scripts {
|
||||||
"/config/main.lua",
|
"/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'
|
version '0.01'
|
||||||
description 'Official ESX library'
|
description 'Official ESX library'
|
||||||
|
|
||||||
|
ui_page 'html/medal.html'
|
||||||
|
|
||||||
files {
|
files {
|
||||||
'imports.lua',
|
'imports.lua',
|
||||||
'imports/**/client.lua',
|
'imports/**/client.lua',
|
||||||
'imports/**/shared.lua',
|
'imports/**/shared.lua',
|
||||||
|
'html/medal.html',
|
||||||
|
'html/medal.js',
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_scripts {
|
shared_scripts {
|
||||||
|
'config.lua',
|
||||||
'resource/init.lua',
|
'resource/init.lua',
|
||||||
'resource/**/shared.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
|
if type(props.color2) == "table" then
|
||||||
SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3])
|
SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3])
|
||||||
else
|
else
|
||||||
SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2)
|
SetVehicleColours(vehicle, type(props.color1) == "number" and props.color1 or colorPrimary, props.color2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if props.pearlescentColor ~= nil then
|
if props.pearlescentColor ~= nil then
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
xLib.interactions = {}
|
xLib.interactions = {}
|
||||||
|
|
||||||
local interactions = {}
|
local interactions = {}
|
||||||
local pressedInteractions = {}
|
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
function xLib.interactions.remove(name)
|
function xLib.interactions.remove(name)
|
||||||
@@ -36,7 +35,6 @@ xLib.addKeybind({
|
|||||||
for _, interaction in pairs(interactions) do
|
for _, interaction in pairs(interactions) do
|
||||||
local success, result = pcall(interaction.condition)
|
local success, result = pcall(interaction.condition)
|
||||||
if success and result then
|
if success and result then
|
||||||
pressedInteractions[#pressedInteractions + 1] = interaction
|
|
||||||
interaction.onPress()
|
interaction.onPress()
|
||||||
end
|
end
|
||||||
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 points = {}
|
||||||
local insidePoints = {}
|
local insidePoints = {}
|
||||||
local handleCount = 0
|
local handleCount = 0
|
||||||
|
local loopStarted = false
|
||||||
|
|
||||||
---@param coords vector3
|
---@param coords vector3
|
||||||
---@param distance number
|
---@param distance number
|
||||||
@@ -44,14 +45,23 @@ function xLib.points.hide(handle, hidden)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function xLib.points.startLoop()
|
function xLib.points.startLoop()
|
||||||
|
if loopStarted then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
loopStarted = true
|
||||||
|
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
local lastScan = 0
|
local lastScan = 0
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local coords = GetEntityCoords(PlayerPedId())
|
local coords = GetEntityCoords(PlayerPedId())
|
||||||
|
|
||||||
for _, point in pairs(insidePoints) do
|
for handle, point in pairs(insidePoints) do
|
||||||
point.inside(#(coords - point.coords))
|
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
|
end
|
||||||
|
|
||||||
local now = GetGameTimer()
|
local now = GetGameTimer()
|
||||||
@@ -63,8 +73,8 @@ function xLib.points.startLoop()
|
|||||||
if not point.nearby then
|
if not point.nearby then
|
||||||
point.nearby = true
|
point.nearby = true
|
||||||
|
|
||||||
if point.enter then
|
if point.enter and not pcall(point.enter) then
|
||||||
point.enter()
|
print(("[^1ERROR^7] point ^5%s^7 from ^5%s^7 errored on enter"):format(handle, point.resource))
|
||||||
end
|
end
|
||||||
|
|
||||||
if point.inside then
|
if point.inside then
|
||||||
@@ -74,8 +84,8 @@ function xLib.points.startLoop()
|
|||||||
elseif point.nearby then
|
elseif point.nearby then
|
||||||
point.nearby = false
|
point.nearby = false
|
||||||
|
|
||||||
if point.leave then
|
if point.leave and not pcall(point.leave) then
|
||||||
point.leave()
|
print(("[^1ERROR^7] point ^5%s^7 from ^5%s^7 errored on leave"):format(handle, point.resource))
|
||||||
end
|
end
|
||||||
|
|
||||||
insidePoints[handle] = nil
|
insidePoints[handle] = nil
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ end
|
|||||||
function xLib.string.toPascal(s)
|
function xLib.string.toPascal(s)
|
||||||
xLib.verify(s, 'string', true)
|
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()
|
return first:upper() .. rest:lower()
|
||||||
end):gsub("_", "")
|
end):gsub("_", "")
|
||||||
|
|
||||||
@@ -114,6 +114,10 @@ end
|
|||||||
function xLib.string.replace(s, old, new)
|
function xLib.string.replace(s, old, new)
|
||||||
xLib.verify(s, 'string', true)
|
xLib.verify(s, 'string', true)
|
||||||
|
|
||||||
|
if type(new) == "string" then
|
||||||
|
new = new:gsub("%%", "%%%%")
|
||||||
|
end
|
||||||
|
|
||||||
local result = s:gsub(xLib.string.escapePattern(old), new)
|
local result = s:gsub(xLib.string.escapePattern(old), new)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -14,13 +14,9 @@ function xLib.table.isArray(tbl)
|
|||||||
end
|
end
|
||||||
|
|
||||||
count, maxIndex = count + 1, math.max(maxIndex, k)
|
count, maxIndex = count + 1, math.max(maxIndex, k)
|
||||||
|
|
||||||
if count > maxIndex then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return count == maxIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param tbl table
|
---@param tbl table
|
||||||
@@ -38,13 +34,6 @@ function xLib.table.searchForKey(tbl, item)
|
|||||||
return nil
|
return nil
|
||||||
end
|
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 tbl table
|
||||||
---@param filter fun(value:any, key:any):boolean
|
---@param filter fun(value:any, key:any):boolean
|
||||||
---@return table
|
---@return table
|
||||||
@@ -122,7 +111,7 @@ function xLib.table.dump(tbl)
|
|||||||
local s = '{ '
|
local s = '{ '
|
||||||
for k,v in pairs(tbl) do
|
for k,v in pairs(tbl) do
|
||||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||||
s = s .. '['..k..'] = ' .. tbl(v) .. ','
|
s = s .. '['..k..'] = ' .. xLib.table.dump(v) .. ','
|
||||||
end
|
end
|
||||||
return s .. '} '
|
return s .. '} '
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ local function verifyType(value, valid_type)
|
|||||||
elseif valid_type == 'float' then
|
elseif valid_type == 'float' then
|
||||||
return math.type(value) == 'float'
|
return math.type(value) == 'float'
|
||||||
elseif valid_type == 'uint' then
|
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
|
elseif valid_type == 'char' then
|
||||||
return type(value) == 'string' and #value == 1
|
return type(value) == 'string' and #value == 1
|
||||||
elseif valid_type == 'ped' then
|
elseif valid_type == 'ped' then
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---@diagnostic disable: lowercase-global
|
---@diagnostic disable: lowercase-global
|
||||||
xLib = setmetatable({
|
xLib = setmetatable({
|
||||||
name = 'xLib',
|
name = 'esx_lib',
|
||||||
side = IsDuplicityVersion() and 'server' or 'client'
|
side = IsDuplicityVersion() and 'server' or 'client'
|
||||||
}, {
|
}, {
|
||||||
__newindex = function(self, key, fn)
|
__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'
|
fx_version 'cerulean'
|
||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'Allows resources to Run tasks at specific intervals.'
|
description 'Allows resources to Run tasks at specific intervals.'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
loadscreen 'index.html'
|
loadscreen 'index.html'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'cerulean'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'A basic menu system for ESX Legacy.'
|
description 'A basic menu system for ESX Legacy.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
client_scripts { '@es_extended/imports.lua', 'client/main.lua' }
|
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)
|
local function openMenu(namespace, name, data)
|
||||||
for i = 1, #Timeouts, 1 do
|
|
||||||
ESX.ClearTimeout(Timeouts[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
OpenedMenus[namespace .. "_" .. name] = true
|
OpenedMenus[namespace .. "_" .. name] = true
|
||||||
|
|
||||||
SendNUIMessage({
|
SendNUIMessage({
|
||||||
@@ -14,11 +10,11 @@ local function openMenu(namespace, name, data)
|
|||||||
data = data,
|
data = data,
|
||||||
})
|
})
|
||||||
|
|
||||||
local timeoutId = ESX.SetTimeout(200, function()
|
ESX.SetTimeout(200, function()
|
||||||
SetNuiFocus(true, true)
|
if next(OpenedMenus) then
|
||||||
|
SetNuiFocus(true, true)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
table.insert(Timeouts, timeoutId)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function closeMenu(namespace, name)
|
local function closeMenu(namespace, name)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'A basic input dialog for ESX Legacy.'
|
description 'A basic input dialog for ESX Legacy.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
client_scripts {
|
client_scripts {
|
||||||
'@es_extended/imports.lua',
|
'@es_extended/imports.lua',
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ CreateThread(function()
|
|||||||
data = data,
|
data = data,
|
||||||
})
|
})
|
||||||
SetTimeout(200, function()
|
SetTimeout(200, function()
|
||||||
SetNuiFocus(true, true)
|
if next(OpenedMenus) then
|
||||||
|
SetNuiFocus(true, true)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'A basic table-based menu system for ESX Legacy.'
|
description 'A basic table-based menu system for ESX Legacy.'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
|
|
||||||
|
|
||||||
client_scripts {
|
client_scripts {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ local function HideComponents(hide)
|
|||||||
else
|
else
|
||||||
if HiddenCompents[components[i]] then
|
if HiddenCompents[components[i]] then
|
||||||
local size = HiddenCompents[components[i]]
|
local size = HiddenCompents[components[i]]
|
||||||
SetHudComponentSize(components[i], size.x, size.z)
|
SetHudComponentSize(components[i], size.x, size.y)
|
||||||
HiddenCompents[components[i]] = nil
|
HiddenCompents[components[i]] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -59,9 +59,9 @@ local function HideComponents(hide)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Multicharacter:HideHud(hide)
|
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)
|
HideComponents(hide)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ function Multicharacter:SetupCharacters()
|
|||||||
SetEntityCoords(self.playerPed, self.spawnCoords.x, self.spawnCoords.y, self.spawnCoords.z, true, false, false, false)
|
SetEntityCoords(self.playerPed, self.spawnCoords.x, self.spawnCoords.y, self.spawnCoords.z, true, false, false, false)
|
||||||
SetEntityHeading(self.playerPed, self.spawnCoords.w)
|
SetEntityHeading(self.playerPed, self.spawnCoords.w)
|
||||||
|
|
||||||
SetPlayerControl(ESX.PlayerId, false, 0)
|
SetPlayerControl(ESX.playerId, false, 0)
|
||||||
self:SetupCamera()
|
self:SetupCamera()
|
||||||
self:HideHud(true)
|
self:HideHud(true)
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ else
|
|||||||
jaw_2 = 0,
|
jaw_2 = 0,
|
||||||
chin_1 = 0,
|
chin_1 = 0,
|
||||||
chin_2 = 0,
|
chin_2 = 0,
|
||||||
chin_13 = 0,
|
chin_3 = 0,
|
||||||
chin_4 = 0,
|
chin_4 = 0,
|
||||||
neck_thickness = 0,
|
neck_thickness = 0,
|
||||||
hair_1 = 76,
|
hair_1 = 76,
|
||||||
@@ -147,7 +147,7 @@ else
|
|||||||
jaw_2 = 0,
|
jaw_2 = 0,
|
||||||
chin_1 = -10,
|
chin_1 = -10,
|
||||||
chin_2 = 10,
|
chin_2 = 10,
|
||||||
chin_13 = -10,
|
chin_3 = -10,
|
||||||
chin_4 = 0,
|
chin_4 = 0,
|
||||||
neck_thickness = -5,
|
neck_thickness = -5,
|
||||||
hair_1 = 43,
|
hair_1 = 43,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ fx_version 'cerulean'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
author 'ESX-Framework - Linden - KASH'
|
author 'ESX-Framework - Linden - KASH'
|
||||||
description 'Allows players to have multiple characters on the same account.'
|
description 'Allows players to have multiple characters on the same account.'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
dependencies { 'es_extended', 'esx_context', 'esx_identity', 'esx_skin' }
|
dependencies { 'es_extended', 'esx_context', 'esx_identity', 'esx_skin' }
|
||||||
|
|||||||
@@ -101,9 +101,17 @@ function Database:GetPlayerSlots(identifier)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Database:GetPlayerInfo(identifier, slots)
|
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(
|
return MySQL.query.await(
|
||||||
"SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier LIKE ? LIMIT ?",
|
("SELECT identifier, accounts, job, job_grade, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier IN (%s)"):format(table.concat(placeholders, ", ")),
|
||||||
{ identifier, slots })
|
identifiers)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Database:SetSlots(identifier, slots)
|
function Database:SetSlots(identifier, slots)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function Multicharacter:SetupCharacters(source)
|
|||||||
ESX.Players[identifier] = source
|
ESX.Players[identifier] = source
|
||||||
|
|
||||||
local slots = Database:GetPlayerSlots(identifier)
|
local slots = Database:GetPlayerSlots(identifier)
|
||||||
identifier = Server.prefix .. "%:" .. identifier
|
|
||||||
|
|
||||||
local rawCharacters = Database:GetPlayerInfo(identifier, slots)
|
local rawCharacters = Database:GetPlayerInfo(identifier, slots)
|
||||||
local characters
|
local characters
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
|||||||
|
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
game 'gta5'
|
game 'gta5'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'A beautiful and simple NUI notification system for ESX'
|
description 'A beautiful and simple NUI notification system for ESX'
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
---@field public onFinish? function
|
---@field public onFinish? function
|
||||||
|
|
||||||
local CurrentProgress = nil
|
local CurrentProgress = nil
|
||||||
|
local progressCount = 0
|
||||||
|
|
||||||
local function startProcessing()
|
local function startProcessing(id)
|
||||||
while (CurrentProgress ~= nil) do
|
while CurrentProgress ~= nil and CurrentProgress.id == id do
|
||||||
if CurrentProgress.length > 0 then
|
if GetGameTimer() < CurrentProgress.finishAt then
|
||||||
CurrentProgress.length = CurrentProgress.length - 1000
|
Wait(50)
|
||||||
else
|
else
|
||||||
ClearPedTasks(ESX.PlayerData.ped)
|
ClearPedTasks(ESX.PlayerData.ped)
|
||||||
if CurrentProgress.FreezePlayer then
|
if CurrentProgress.FreezePlayer then
|
||||||
@@ -24,7 +25,6 @@ local function startProcessing()
|
|||||||
end
|
end
|
||||||
CurrentProgress = nil
|
CurrentProgress = nil
|
||||||
end
|
end
|
||||||
Wait(1000)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -55,8 +55,15 @@ local function Progressbar(message, length, Options)
|
|||||||
length = length or 3000,
|
length = length or 3000,
|
||||||
message = message or "ESX-Framework",
|
message = message or "ESX-Framework",
|
||||||
})
|
})
|
||||||
|
progressCount = progressCount + 1
|
||||||
|
CurrentProgress.id = progressCount
|
||||||
CurrentProgress.length = length or 3000
|
CurrentProgress.length = length or 3000
|
||||||
CreateThread(startProcessing);
|
CurrentProgress.finishAt = GetGameTimer() + CurrentProgress.length
|
||||||
|
|
||||||
|
local id = progressCount
|
||||||
|
CreateThread(function()
|
||||||
|
startProcessing(id)
|
||||||
|
end)
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'A beautiful and simple NUI progress bar for ESX'
|
description 'A beautiful and simple NUI progress bar for ESX'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
client_scripts { 'Progress.lua' }
|
client_scripts { 'Progress.lua' }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
|||||||
|
|
||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'Allows players to customise their character\'s appearance'
|
description 'Allows players to customise their character\'s appearance'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
shared_scripts {
|
shared_scripts {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fx_version 'adamant'
|
|||||||
game 'gta5'
|
game 'gta5'
|
||||||
author 'ESX-Framework'
|
author 'ESX-Framework'
|
||||||
description 'A beautiful and simple Persistent Notification system for ESX.'
|
description 'A beautiful and simple Persistent Notification system for ESX.'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
client_scripts { 'TextUI.lua' }
|
client_scripts { 'TextUI.lua' }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ fx_version 'adamant'
|
|||||||
|
|
||||||
game 'gta5'
|
game 'gta5'
|
||||||
description 'Saves/loads character appearances for ESX Legacy.'
|
description 'Saves/loads character appearances for ESX Legacy.'
|
||||||
version '1.13.5'
|
version '1.14.0'
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
client_scripts {
|
client_scripts {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ add_ace resource.es_extended command.stop allow
|
|||||||
ensure oxmysql
|
ensure oxmysql
|
||||||
|
|
||||||
## ESX Legacy
|
## ESX Legacy
|
||||||
|
ensure esx_lib
|
||||||
ensure es_extended
|
ensure es_extended
|
||||||
ensure [core]
|
ensure [core]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user