diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index 65be9a235..708c3abe1 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -20,6 +20,7 @@ shared_scripts { server_scripts { '@oxmysql/lib/MySQL.lua', 'shared/config/logs.lua', + 'server/config/commandperms.lua', 'server/common.lua', 'server/modules/callback.lua', diff --git a/[core]/es_extended/server/config/commandperms.lua b/[core]/es_extended/server/config/commandperms.lua new file mode 100644 index 000000000..e6597d909 --- /dev/null +++ b/[core]/es_extended/server/config/commandperms.lua @@ -0,0 +1,43 @@ +Config.CommandPermissions = { + ["setcoords"] = { "admin" }, + ["tp"] = { "admin" }, + ["setjob"] = { "admin" }, + ["car"] = { "admin" }, + ["cardel"] = { "admin" }, + ["dv"] = { "admin" }, + ["fix"] = { "admin" }, + ["repair"] = { "admin" }, + ["setaccountmoney"] = { "admin" }, + ["giveaccountmoney"] = { "admin" }, + ["removeaccountmoney"] = { "admin" }, + ["giveitem"] = { "admin" }, + ["giveweapon"] = { "admin" }, + ["giveammo"] = { "admin" }, + ["giveweaponcomponent"] = { "admin" }, + ["clearall"] = { "admin" }, + ["clsall"] = { "admin" }, + ["refreshjobs"] = { "admin" }, + ["refreshitems"] = { "admin" }, + ["clearinventory"] = { "admin" }, + ["clearloadout"] = { "admin" }, + ["setgroup"] = { "admin" }, + ["save"] = { "admin" }, + ["saveall"] = { "admin" }, + ["group"] = { "user" }, + ["job"] = { "user" }, + ["info"] = { "user" }, + ["playtime"] = { "user" }, + ["coords"] = { "admin" }, + ["tpm"] = { "admin" }, + ["goto"] = { "admin" }, + ["bring"] = { "admin" }, + ["kill"] = { "admin" }, + ["freeze"] = { "admin" }, + ["unfreeze"] = { "admin" }, + ["noclip"] = { "admin" }, + ["players"] = { "admin" }, + ["setdim"] = { "admin" }, + ["setbucket"] = { "admin" }, + ["clear"] = { "user" }, + ["cls"] = { "user" }, +} diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 46696f93a..26b803521 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -24,11 +24,14 @@ function ESX.TriggerClientEvent(eventName, playerIds, ...) end end ----@param name string | table ----@param group string | table ----@param cb fun(xPlayer : xPlayer|false, args : table, showError :function ) +---Supports multiple overloads: +---1. ESX.RegisterCommand(name, group, cb, allowConsole?, suggestion?) +---2. ESX.RegisterCommand(name, false, cb, allowConsole?, suggestion?) -- Looks up group in Config.CommandPermissions +---@param name string | string[] +---@param group false | string | string[] -- Use false to lookup in Config.CommandPermissions +---@param cb fun(xPlayer: xPlayer|false, args: table, showError: function) ---@param allowConsole? boolean ----@param suggestion? {help:string, validate:boolean, arguments:{name:string, validate:boolean, help:string, type:'number'|'string'|'player'|'coordinate'|'playerId'|'item'|'weapon'|'any'|'merge'}[]} +---@param suggestion? {help?: string, validate?: boolean, arguments?: {name: string, validate?: boolean, help?: string, type: 'number'|'string'|'player'|'coordinate'|'playerId'|'item'|'weapon'|'any'|'merge'}[]} function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) if type(name) == "table" then for _, v in ipairs(name) do @@ -37,6 +40,21 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) return end + local commandGroup = group + + if group == false then + local permGroups = Config.CommandPermissions[name] + + if permGroups and type(permGroups) == "table" then + commandGroup = #permGroups == 1 and permGroups[1] or permGroups + else + commandGroup = "user" + end + elseif type(group) ~= "string" and type(group) ~= "table" then + error(("Invalid group provided to ESX.RegisterCommand for command '%s'"):format(name)) + return + end + if Core.RegisteredCommands[name] then print(('[^3WARNING^7] Command ^5"%s" ^7already registered, overriding command'):format(name)) @@ -56,7 +74,7 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) TriggerClientEvent("chat:addSuggestion", -1, ("/%s"):format(name), suggestion.help, suggestion.arguments) end - Core.RegisteredCommands[name] = { group = group, cb = cb, allowConsole = allowConsole, suggestion = suggestion } + Core.RegisteredCommands[name] = { group = commandGroup, cb = cb, allowConsole = allowConsole, suggestion = suggestion } RegisterCommand(name, function(playerId, args) local command = Core.RegisteredCommands[name] @@ -146,7 +164,7 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) end end end - + if ESX.IsFunctionReference(v.Validator?.validate) and not err then local candidate = newArgs[v.name] local ok, res = pcall(v.Validator.validate, candidate) @@ -187,12 +205,12 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) end end, true) - if type(group) == "table" then - for _, v in ipairs(group) do + if type(commandGroup) == "table" then + for _, v in ipairs(commandGroup) do ExecuteCommand(("add_ace group.%s command.%s allow"):format(v, name)) end else - ExecuteCommand(("add_ace group.%s command.%s allow"):format(group, name)) + ExecuteCommand(("add_ace group.%s command.%s allow"):format(commandGroup, name)) end end diff --git a/[core]/es_extended/server/modules/commands.lua b/[core]/es_extended/server/modules/commands.lua index 2320e82f1..28046dff1 100644 --- a/[core]/es_extended/server/modules/commands.lua +++ b/[core]/es_extended/server/modules/commands.lua @@ -1,6 +1,6 @@ ESX.RegisterCommand( { "setcoords", "tp" }, - "admin", + false, function(xPlayer, args) xPlayer.setCoords({ x = args.x, y = args.y, z = args.z }) if Config.AdminLogging then @@ -27,7 +27,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "setjob", - "admin", + false, function(xPlayer, args, showError) if not ESX.DoesJobExist(args.job, args.grade) then return showError(TranslateCap("command_setjob_invalid")) @@ -68,7 +68,7 @@ local upgrades = Config.SpawnVehMaxUpgrades and { ESX.RegisterCommand( "car", - "admin", + false, function(xPlayer, args, showError) if not xPlayer then return showError("[^1ERROR^7] The xPlayer value is nil") @@ -132,7 +132,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( { "cardel", "dv" }, - "admin", + false, function(xPlayer, args) local ped = GetPlayerPed(xPlayer.source) local pedVehicle = GetVehiclePedIsIn(ped, false) @@ -168,7 +168,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( { "fix", "repair" }, - "admin", + false, function(xPlayer, args, showError) local xTarget = args.playerId local ped = GetPlayerPed(xTarget.source) @@ -202,7 +202,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "setaccountmoney", - "admin", + false, function(xPlayer, args, showError) if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) @@ -232,7 +232,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "giveaccountmoney", - "admin", + false, function(xPlayer, args, showError) if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_giveaccountmoney_invalid")) @@ -262,7 +262,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "removeaccountmoney", - "admin", + false, function(xPlayer, args, showError) if not args.playerId.getAccount(args.account) then return showError(TranslateCap("command_removeaccountmoney_invalid")) @@ -293,7 +293,7 @@ ESX.RegisterCommand( if not Config.CustomInventory then ESX.RegisterCommand( "giveitem", - "admin", + false, function(xPlayer, args) args.playerId.addInventoryItem(args.item, args.count) if Config.AdminLogging then @@ -323,7 +323,7 @@ if not Config.CustomInventory then ESX.RegisterCommand( "giveweapon", - "admin", + false, function(xPlayer, args, showError) if args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveweapon_hasalready")) @@ -353,7 +353,7 @@ if not Config.CustomInventory then ESX.RegisterCommand( "giveammo", - "admin", + false, function(xPlayer, args, showError) if not args.playerId.hasWeapon(args.weapon) then return showError(TranslateCap("command_giveammo_noweapon_found")) @@ -383,7 +383,7 @@ if not Config.CustomInventory then ESX.RegisterCommand( "giveweaponcomponent", - "admin", + false, function(xPlayer, args, showError) if args.playerId.hasWeapon(args.weaponName) then local component = ESX.GetWeaponComponent(args.weaponName, args.componentName) @@ -423,11 +423,11 @@ if not Config.CustomInventory then ) end -ESX.RegisterCommand({ "clear", "cls" }, "user", function(xPlayer) +ESX.RegisterCommand({ "clear", "cls" }, false, function(xPlayer) xPlayer.triggerEvent("chat:clear") end, false, { help = TranslateCap("command_clear") }) -ESX.RegisterCommand({ "clearall", "clsall" }, "admin", function(xPlayer) +ESX.RegisterCommand({ "clearall", "clsall" }, false, function(xPlayer) TriggerClientEvent("chat:clear", -1) if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Clear Chat /clearall Triggered!", "pink", { @@ -437,12 +437,12 @@ ESX.RegisterCommand({ "clearall", "clsall" }, "admin", function(xPlayer) end end, true, { help = TranslateCap("command_clearall") }) -ESX.RegisterCommand("refreshjobs", "admin", function() +ESX.RegisterCommand("refreshjobs", false, function() ESX.RefreshJobs() end, true, { help = TranslateCap("command_clearall") }) if not Config.CustomInventory then - ESX.RegisterCommand("refreshitems", "admin", function(xPlayer) + ESX.RegisterCommand("refreshitems", false, function(xPlayer) local itemCount = ESX.RefreshItems() xPlayer.showNotification(Translate("command_refreshitems_success", itemCount), true, false, 140) @@ -450,7 +450,7 @@ if not Config.CustomInventory then ESX.RegisterCommand( "clearinventory", - "admin", + false, function(xPlayer, args) for _, v in ipairs(args.playerId.inventory) do if v.count > 0 then @@ -478,7 +478,7 @@ if not Config.CustomInventory then ESX.RegisterCommand( "clearloadout", - "admin", + false, function(xPlayer, args) for i = #args.playerId.loadout, 1, -1 do args.playerId.removeWeapon(args.playerId.loadout[i].name) @@ -505,7 +505,7 @@ end ESX.RegisterCommand( "setgroup", - "admin", + false, function(xPlayer, args) if not args.playerId then args.playerId = xPlayer.source @@ -537,7 +537,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "save", - "admin", + false, function(_, args) Core.SavePlayer(args.playerId) print(("[^2Info^0] Saved Player - ^5%s^0"):format(args.playerId.source)) @@ -552,26 +552,26 @@ ESX.RegisterCommand( } ) -ESX.RegisterCommand("saveall", "admin", function() +ESX.RegisterCommand("saveall", false, function() Core.SavePlayers() end, true, { help = TranslateCap("command_saveall") }) -ESX.RegisterCommand("group", { "user", "admin" }, function(xPlayer, _, _) +ESX.RegisterCommand("group", false, function(xPlayer, _, _) print(("%s, you are currently: ^5%s^0"):format(xPlayer.getName(), xPlayer.getGroup())) end, true) -ESX.RegisterCommand("job", { "user", "admin" }, function(xPlayer, _, _) +ESX.RegisterCommand("job", false, 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", false, 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", false, function(xPlayer) local playtime = xPlayer.getPlayTime() local days = math.floor(playtime / 86400) local hours = math.floor((playtime % 86400) / 3600) @@ -579,7 +579,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", false, function(xPlayer) local ped = GetPlayerPed(xPlayer.source) local coords = GetEntityCoords(ped, false) local heading = GetEntityHeading(ped) @@ -587,7 +587,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", false, function(xPlayer) xPlayer.triggerEvent("esx:tpm") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Admin Teleport /tpm Triggered!", "pink", { @@ -599,7 +599,7 @@ end, false) ESX.RegisterCommand( "goto", - "admin", + false, function(xPlayer, args) local targetCoords = args.playerId.getCoords() local srcDim = GetPlayerRoutingBucket(xPlayer.source) @@ -630,7 +630,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "bring", - "admin", + false, function(xPlayer, args) local targetCoords = args.playerId.getCoords() local playerCoords = xPlayer.getCoords() @@ -662,7 +662,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "kill", - "admin", + false, function(xPlayer, args) args.playerId.triggerEvent("esx:killPlayer") if Config.AdminLogging then @@ -685,7 +685,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "freeze", - "admin", + false, function(xPlayer, args) args.playerId.triggerEvent("esx:freezePlayer", "freeze") if Config.AdminLogging then @@ -708,7 +708,7 @@ ESX.RegisterCommand( ESX.RegisterCommand( "unfreeze", - "admin", + false, function(xPlayer, args) args.playerId.triggerEvent("esx:freezePlayer", "unfreeze") if Config.AdminLogging then @@ -729,7 +729,7 @@ ESX.RegisterCommand( } ) -ESX.RegisterCommand("noclip", "admin", function(xPlayer) +ESX.RegisterCommand("noclip", false, function(xPlayer) xPlayer.triggerEvent("esx:noclip") if Config.AdminLogging then ESX.DiscordLogFields("UserActions", "Admin NoClip /noclip Triggered!", "pink", { @@ -739,7 +739,7 @@ ESX.RegisterCommand("noclip", "admin", function(xPlayer) end end, false) -ESX.RegisterCommand("players", "admin", function() +ESX.RegisterCommand("players", false, 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 +750,7 @@ end, true) ESX.RegisterCommand( {"setdim", "setbucket"}, - "admin", + false, function(xPlayer, args) SetPlayerRoutingBucket(args.playerId.source, args.dimension) if Config.AdminLogging then