feat(imports/addCommand)!: update syntax and support param help text

BREAKING CHANGE: First parameter now holds command name(s).
Second parameter is properties, containing (optional) values for help,
restricted, and params.

See #224 for more info.
This commit is contained in:
Linden
2023-02-21 19:52:12 +11:00
parent 76eef708a8
commit 4d15b38d02
2 changed files with 213 additions and 85 deletions

View File

@@ -0,0 +1,110 @@
-- DO NOT USE! Old syntax for addCommand (prior to v3.0)
---@todo convert input and call standard function?
local commands = {}
SetTimeout(1000, function()
TriggerClientEvent('chat:addSuggestions', -1, commands)
end)
AddEventHandler('playerJoining', function(source)
TriggerClientEvent('chat:addSuggestions', source, commands)
end)
local function chatSuggestion(name, parameters, help)
local params = {}
if parameters then
for i = 1, #parameters do
local arg, argType = string.strsplit(':', parameters[i])
if argType and argType:sub(0, 1) == '?' then
argType = argType:sub(2, #argType)
end
params[i] = {
name = arg,
help = argType
}
end
end
commands[#commands + 1] = {
name = '/' .. name,
help = help,
params = params
}
end
---@deprecated
---@param group string | string[] | false
---@param name string | string[]
---@param callback function
---@param parameters table
function lib.__addCommand(group, name, callback, parameters, help)
if not group then group = 'builtin.everyone' end
if type(name) == 'table' then
for i = 1, #name do
---@diagnostic disable-next-line: deprecated
lib.__addCommand(group, name[i], callback, parameters, help)
end
else
chatSuggestion(name, parameters, help)
RegisterCommand(name, function(source, args, raw)
source = tonumber(source) --[[@as number]]
if parameters then
for i = 1, #parameters do
local arg, argType = string.strsplit(':', parameters[i])
local value = args[i]
if arg == 'target' and value == 'me' then value = source end
if argType then
local optional
if argType:sub(0, 1) == '?' then
argType = argType:sub(2, #argType)
optional = true
end
if argType == 'number' then
value = tonumber(value) or value
end
local type = type(value)
if type ~= argType and (not optional or type ~= 'nil') then
local invalid = ('^1%s expected <%s> for argument %s (%s), received %s^0'):format(name,
argType, i, arg, type)
if source < 1 then
return print(invalid)
else
return TriggerClientEvent('chat:addMessage', source, invalid)
end
end
end
args[arg] = value
args[i] = nil
end
end
callback(source, args, raw)
end, group and true)
name = ('command.%s'):format(name)
if type(group) == 'table' then
for _, v in ipairs(group) do
if not IsPrincipalAceAllowed(v, name) then lib.addAce(v, name) end
end
else
if not IsPrincipalAceAllowed(group, name) then lib.addAce(group, name) end
end
end
end
---@diagnostic disable-next-line: deprecated
return lib.__addCommand

View File

@@ -1,113 +1,131 @@
local commands = {} ---@class OxCommandProperties
---@field help string?
---@field params { name: string, type?: 'number' | 'playerId' | 'string', help?: string }[]
---@field restricted boolean | string | string[]?
---@type OxCommandProperties[]
local registeredCommands = {}
SetTimeout(1000, function() SetTimeout(1000, function()
TriggerClientEvent('chat:addSuggestions', -1, commands) TriggerClientEvent('chat:addSuggestions', -1, registeredCommands)
end) end)
AddEventHandler('playerJoining', function(source) AddEventHandler('playerJoining', function(source)
TriggerClientEvent('chat:addSuggestions', source, commands) TriggerClientEvent('chat:addSuggestions', source, registeredCommands)
end) end)
local function chatSuggestion(name, parameters, help) ---@param commandName string
local params = {} ---@param source number
---@param args table
---@param params table
---@return table?
local function parseArguments(commandName, source, args, params)
if not params then return args end
if parameters then for i = 1, #params do
for i = 1, #parameters do local arg, param = args[i], params[i]
local arg, argType = string.strsplit(':', parameters[i]) local value
if argType and argType:sub(0, 1) == '?' then if param.type == 'number' then
argType = argType:sub(2, #argType) value = tonumber(arg)
end elseif param.type == 'string' then
value = not tonumber(arg) and arg
elseif param.type == 'playerId' then
value = arg == 'me' and source or tonumber(arg)
params[i] = { if not value or not GetPlayerGuid(value--[[@as string]]) then
name = arg, value = false
help = argType end
} else
end value = arg
end end
commands[#commands + 1] = { if not value and (not param.optional or param.optional and arg) then
name = '/'..name, return Citizen.Trace(("^1command '%s' received an invalid %s for argument %s (%s), received '%s'^0"):format(commandName, param.type, i, param.name, arg))
help = help, end
params = params
} arg = value
args[param.name] = arg
args[i] = nil
end
return args
end end
---@param group string | string[] | false ---@param commandName string | string[]
---@param name string | string[] ---@param properties OxCommandProperties | false
---@param callback function ---@param cb fun(source: number, args: table, raw: string)
---@param parameters table ---@param ... any
function lib.addCommand(group, name, callback, parameters, help) function lib.addCommand(commandName, properties, cb, ...)
if not group then group = 'builtin.everyone' end -- Try to handle backwards-compatibility with the old addCommand syntax (prior to v3.0)
local restricted, params
if type(name) == 'table' then if properties then
for i = 1, #name do if ... or table.type(properties) ~= 'hash' then
lib.addCommand(group, name[i], callback, parameters, help) local _commandName = type(properties) == 'table' and properties[1] or properties
end local info = debug.getinfo(2, 'Sl')
else
chatSuggestion(name, parameters, help)
RegisterCommand(name, function(source, args, raw) warn(("command '%s' is using deprecated syntax for lib.addCommand\nupdate the command or use lib.__addCommand to ignore this warning\n> source ^0(^5%s^0:%d)"):format(_commandName, info.short_src, info.currentline))
source = tonumber(source) --[[@as number]] ---@diagnostic disable-next-line: deprecated
return lib.__addCommand(commandName, properties, cb, ...)
end
if parameters then restricted = properties.restricted
for i = 1, #parameters do params = properties.params
local arg, argType = string.strsplit(':', parameters[i]) end
local value = args[i]
if arg == 'target' and value == 'me' then value = source end if params then
for i = 1, #params do
local param = params[i]
if argType then if param.type then
local optional param.help = param.help and ('%s (type: %s)'):format(param.help, param.type) or ('(type: %s)'):format(param.type)
end
end
end
if argType:sub(0, 1) == '?' then local commands = type(commandName) ~= 'table' and { commandName } or commandName
argType = argType:sub(2, #argType) local numCommands = #commands
optional = true local totalCommands = #registeredCommands
end
if argType == 'number' then for i = 1, numCommands do
value = tonumber(value) or value totalCommands += 1
end commandName = commands[i]
local type = type(value) RegisterCommand(commandName, function(source, args, raw)
args = parseArguments(commandName, source, args, params)
if type ~= argType and (not optional or type ~= 'nil') then if not args then return end
local invalid = ('^1%s expected <%s> for argument %s (%s), received %s^0'):format(name, argType, i, arg, type)
if source < 1 then
return print(invalid)
else
return TriggerClientEvent('chat:addMessage', source, invalid)
end
end
end
args[arg] = value cb(source, args, raw)
args[i] = nil end, restricted and true)
end
end
callback(source, args, raw) if restricted then
end, group and true) local ace = ('command.%s'):format(commandName)
local restrictedType = type(restricted)
name = ('command.%s'):format(name) if restrictedType == 'string' and not IsPrincipalAceAllowed(restricted, ace) then
if type(group) == 'table' then lib.addAce(restricted, ace)
for _, v in ipairs(group) do elseif restrictedType == 'table' then
if not IsPrincipalAceAllowed(v, name) then lib.addAce(v, name) end for j = 1, #restricted do
end if not IsPrincipalAceAllowed(restricted[j], ace) then
else lib.addAce(restricted[j], ace)
if not IsPrincipalAceAllowed(group, name) then lib.addAce(group, name) end end
end end
end end
end
if properties then
properties.name = ('/%s'):format(commandName)
properties.restricted = nil
registeredCommands[totalCommands] = properties
if i ~= numCommands and numCommands ~= 1 then
properties = table.clone(properties)
end
end
end
end end
return lib.addCommand return lib.addCommand
--[[ Example
AddCommand('group.admin', {'additem', 'giveitem'}, function(source, args)
args.item = Items(args.item)
if args.item and args.count > 0 then
Inventory.AddItem(args.target, args.item.name, args.count, args.metatype)
end
end, {'target:number', 'item:string', 'count:number', 'metatype:?string'})
-- /additem 1 burger 1
]]