-
-
Notifications
You must be signed in to change notification settings - Fork 432
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
2 changed files
with
229 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
TriggerClientEvent('chat:addSuggestions', -1, commands) | ||
TriggerClientEvent('chat:addSuggestions', -1, registeredCommands) | ||
end) | ||
|
||
AddEventHandler('playerJoining', function(source) | ||
TriggerClientEvent('chat:addSuggestions', source, commands) | ||
TriggerClientEvent('chat:addSuggestions', source, registeredCommands) | ||
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 | ||
} | ||
---@param commandName string | ||
---@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 | ||
|
||
for i = 1, #params do | ||
local arg, param = args[i], params[i] | ||
local value | ||
|
||
if param.type == 'number' then | ||
value = tonumber(arg) | ||
elseif param.type == 'string' then | ||
value = not tonumber(arg) and arg | ||
elseif param.type == 'playerId' then | ||
value = arg == 'me' and source or tonumber(arg) | ||
|
||
if not value or not GetPlayerGuid(value--[[@as string]]) then | ||
value = false | ||
end | ||
else | ||
value = arg | ||
end | ||
|
||
if not value and (not param.optional or param.optional and arg) then | ||
return Citizen.Trace(("^1command '%s' received an invalid %s for argument %s (%s), received '%s'^0"):format(commandName, param.type, i, param.name, arg)) | ||
end | ||
|
||
arg = value | ||
|
||
args[param.name] = arg | ||
args[i] = nil | ||
end | ||
|
||
return args | ||
end | ||
|
||
---@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 | ||
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 | ||
---@param commandName string | string[] | ||
---@param properties OxCommandProperties | false | ||
---@param cb fun(source: number, args: table, raw: string) | ||
---@param ... any | ||
function lib.addCommand(commandName, properties, cb, ...) | ||
-- Try to handle backwards-compatibility with the old addCommand syntax (prior to v3.0) | ||
local restricted, params | ||
|
||
if properties then | ||
if ... or table.type(properties) ~= 'hash' then | ||
local _commandName = type(properties) == 'table' and properties[1] or properties | ||
local info = debug.getinfo(2, 'Sl') | ||
|
||
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)) | ||
---@diagnostic disable-next-line: deprecated | ||
return lib.__addCommand(commandName, properties, cb, ...) | ||
end | ||
|
||
restricted = properties.restricted | ||
params = properties.params | ||
end | ||
|
||
if params then | ||
for i = 1, #params do | ||
local param = params[i] | ||
|
||
if param.type then | ||
param.help = param.help and ('%s (type: %s)'):format(param.help, param.type) or ('(type: %s)'):format(param.type) | ||
end | ||
end | ||
end | ||
|
||
local commands = type(commandName) ~= 'table' and { commandName } or commandName | ||
local numCommands = #commands | ||
local totalCommands = #registeredCommands | ||
|
||
for i = 1, numCommands do | ||
totalCommands += 1 | ||
commandName = commands[i] | ||
|
||
RegisterCommand(commandName, function(source, args, raw) | ||
args = parseArguments(commandName, source, args, params) | ||
|
||
if not args then return end | ||
|
||
cb(source, args, raw) | ||
end, restricted and true) | ||
|
||
if restricted then | ||
local ace = ('command.%s'):format(commandName) | ||
local restrictedType = type(restricted) | ||
|
||
if restrictedType == 'string' and not IsPrincipalAceAllowed(restricted, ace) then | ||
lib.addAce(restricted, ace) | ||
elseif restrictedType == 'table' then | ||
for j = 1, #restricted do | ||
if not IsPrincipalAceAllowed(restricted[j], ace) then | ||
lib.addAce(restricted[j], ace) | ||
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 | ||
|
||
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 | ||
]] |