Improved comment in commands

This commit is contained in:
Cooldude2606
2019-04-10 15:25:47 +01:00
parent 3b2336a056
commit 247461d21d

View File

@@ -3,115 +3,170 @@
-- @module Commands
--[[
>>>>Example Authenticator:
The command system is most useful when you can control who can use commands; to do this would would need to
define an authenticator which is ran every time a command is run; in this example I will show a simple one
that requires some commands to require the user to be a game admin:
-- adds an admin only authenticator where if a command has the tag admin_only: true
-- then will only allow admins to use this command
Commands.add_authenticator(function(player,command,tags,reject)
if tags.admin_only then -- the command has the tag admin_only set to true
if player.admin then -- the player is an admin
return true -- no return is needed for success but is useful to include
else -- the player is not admin
-- you must return to block a command, they are a few ways to do this:
-- return false -- most basic and has no custom error message
-- return reject -- sill no error message and is here in case people dont know its a function
-- reject() -- rejects the player, return not needed but please return if possible
-- return reject() -- rejects the player and has a failsafe return to block command
-- reject('This command is for admins only!') -- reject but with custom error message, return not needed but please return if possible
return reject('This command is for admins only!') -- reject but with custom error message and has return failsafe
When the authenticator is called be the command handler it will be passed 4 vales:
1) the player who used the command
2) the name of the command that is being used
3) any flags which have been set for this command, this is a table of values set using :set_flag(name,value)
4) the reject function which is the preferred method to prevent execution of the command
For our admin only example we will set a flag to true when we want it do be admin only so when we define the
command will will use :set_flag('admin_only',true) and then inside the authenticator we will test if the flag
is present using: if flags.admin_only then
Although no return is required to allow the command to execute it is best practice to return true; we do this in
two cases in our authenticator:
1) when the "admin_only" flag is not set, which we take to mean any one can use it
2) when the "admin_only" flag is set, and the player is admin
Now when the user is not an admin and the command requires you to be an admin then we must reject the request:
1) return false -- this is the most basic block and should only be used while testing
2) return reject -- returning the reject function is only an option as a fail safe, same as returning false
3) reject() -- this will block execution without returning to allow further code to be ran in the authenticator
4) reject('This command is for admins only!') -- Using reject as a function allows a error message to be returned
5) return reject() -- using return on either case above is best practice as you should execute all code before rejecting
Example Code:
Commands.add_authenticator(function(player,command,flags,reject)
if flags.admin_only then -- our test for the "admin_only" flag
if player.admin then
return true -- true return 2
else
return reject('This command is for admins only!') -- reject return 5 with a custom error message
end
else -- command does not require admin
return true -- no return is needed for success but is useful to include
else
return true -- true return 1
end
end)
>>>>Example Parse:
Before you go making commands it is important to understand the most powerful feature of this command handler,
when you define a command you are able to type the params and have then be parsed by an handler so before your
command is ever executed you can be sure that all the params are valid. This module should be paired with a general
command parse but you may want to create your own:
-- adds a parse that will cover numbers within the given range
-- input, player and reject are common to all parse functions
-- range_min and range_max are passed to the function from add_param
For our example we will create a parse to accept only integer numbers in a given range:
1) we will give it the name "number-range-int" this is the "type" that the input is expected to be
2) when we define the type we will also define the min and max of the range so we can use the function more than once
Example parse usage:
:add_param('repeat_count',false,'number-range-int',5,10) -- range 5 to 10 inclusive
The command parse will be passed 3 params and any other you define, in our case:
1) the input that has been given by the user for this param, the role of this function is to transform this value
nb: the input is a string but can be nil if the param is marked as optional
2) the player who is using the command, this is always present
3) the reject function to throw an error to the user, this is always present
4) the range min, this is user defined and has the value given when the param is defined
5) the range max, this is user defined and has the value given when the param is defined
When returning from the param parse you again have a few options with how to do this:
1) you return the new value for the param (any non nil value) this value is then passed to the command callback
2) not returning will cause a generic invalid error and the command callback is blocked, not recommenced
3) return reject -- this is just a failsafe in case the function is not called, same as no return
4) return reject() -- will give a shorter error message as you pass a nil custom error
5) return reject('Number entered is not in range: '..range_min..', '..range_max) -- returns a custom error the the user
nb: if you do not return reject after you call it then you are still returning nil so there will be a duplicate message
It should be noted that if you want to expand on an existing parse you can use Commands.parse(type,input,player,reject)
and this value will either return a new value for the input or nil, if it is nil you should return nil to prevent dobble
messages to the user:
input = Commands.parse('number-int',input,player,reject)
if not input then return end -- nil check
Example Code:
Commands.add_parse('number-range-int',function(input,player,reject,range_min,range_max)
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil -- converts input to number
if not rtn or rtn < range_min or rtn > range_max then -- check if it is nil or out of the range
-- invalid input for we will reject the input, they are a few ways to do this:
-- dont return anything -- will print generic input error
-- return false -- this WILL NOT reject the input as false can be a valid output
-- return reject -- will print generic input error
-- return reject() -- will print generic input error with no please check type message
-- reject() -- if you do not return the value then they will be a duplicate message
return reject('Number entered is not in range: '..range_min..', '..range_max) -- reject with custom error
else
return rtn -- returns the number value this will be passed to the command callback
end
end)
>>>>Example Command:
-- adds a command that will print the players name a given number of times
-- and can only be used by admin to show how auth works
Commands.new_command('repeat-name','Will repeat you name a number of times in chat.') -- creates the new command with the name "repeat-name" and a help message
:add_param('repeat-count',false,'number-range-int',1,5) -- adds a new param called "repeat-count" that is required and is type "number_range_int" the name can be used here as add_parse was used
:add_param('smiley',true,function(input,player,reject) -- this param is optional and has a custom parse function where add_parse was not used before hand
if not input then return end -- when they is an optional param input may be nil, you can return a default value here, but using nil will allow add_defaults to pick a default
if input:lower() == 'true' or input:lower() == 'yes' then
return true -- the value is truthy so true is returned
else
-- it should be noted that this function will be ran even when the param is not present
-- in this case input is nil and so a default can be returned, see above
return false -- false is returned other wise
end
end)
:set_defaults{smiley=false} -- adds false as the default for smiley
:set_flag('admin_only',true) -- adds the tag admin_only: true which because of the above authenticator means you must be added to use this command
:add_alias('name','rname') -- adds two aliases "name" and "rname" for this command which will work as if the ordinal name was used
--:enable_auto_concat() -- cant be used due to optional param here, but this will make all user input params after the last expected one be added to the last expected one
:register(function(player,repeat_count,smiley,raw) -- this registers the command to the game, notice the params are what were defined above
-- prints the raw input to show that it can be used
game.print(player.name..' used a command with input: '..raw)
-- some smiley logic
local msg
if smiley then
msg = ':) '..player.name
else
msg = ') '..player.name
end
-- prints your name alot
for i = 1,repeat_count do
Commands.print(i..msg) -- this command is an alias for ("expcore.common").player_return it will print any value to the player/server not just strings
end
-- if you wanted to you can return some values here
-- no return -- only success message is printed
-- Commands.error('optional message here') -- prints an error message
-- return Commands.error('optional message here') -- prints an error message, and stops success message being printed
-- Commands.success('optional message here') -- same as below but success message is printed twice DONT DO this
-- return Commands.success('optional message here') -- prints your message and then the success message
end)
>>>>Examples With No Comments (for example formatting):
Commands.add_authenticator(function(player,command,tags,reject)
if tags.admin_only then
if player.admin then
return true
else
return reject('This command is for admins only!')
end
else
return true
end
end)
Commands.add_parse('number_range_int',function(input,player,reject,range_min,range_max)
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil
if not rtn or rtn < range_min or rtn > range_max then
-- the input is either not a number or is outside the range
return reject('Number entered is not in range: '..range_min..', '..range_max)
else
-- returns the input as a number value rather than a string, thus the param is now the correct type
return rtn
end
end)
>>>>Example Command:
How for the fun part making the commands, the commands can be set up with any number of params and flags that you want,
you can add aliases for the commands and set default values for optional params and of course register your command callback
in our example we will just have a command that will repeat the users name in chat X amount of times and only allow admins to use it.
First we create the new command, nb this will not register the command to the game this is done at the end, we will call
the command "repeat-name" and set the help message as follows:
Commands.new_command('repeat-name','Will repeat you name a number of times in chat.')
:add_param('repeat-count',false,'number_range_int',1,5)
Now for our first param we will call "repeat-count" and it will be a required value between 1 and 5 inclusive:
:add_param('repeat-count',false,'number-range-int',1,5)
Our second param we need a custom parse for but we have not defined it, this is an option for when it is unlikely for
any other command to use the same input type; however in our case it will just be a boolean which should be noted as being
included in the general command parse config. As for the param its self it will be called "smiley" and will be optional with
a default value of false:
:add_param('smiley',true,function(input,player,reject)
-- since it is optional the input can be nil, in which case we just return
if not input then return end
-- if it is not nil then we check for a truthy value
if input:lower() == 'true' or input:lower() == 'yes' then
return true
else
-- note that because we did not return nil or reject then false will be passed to command callback, see example parse
return false
end
end)
Once all params are defined you can now define some default values if you have optional params, the default value will be used only
when no value is given as input, if an invalid value is given then the command will still fail and this value will not be used, the
default can also be a function which is passed the player using the command and returns a value. Here we set the default for "smiley" to false:
:set_defaults{smiley=false}
Another example of defaults if we have: item, amount[opt], player[opt]
:set_defaults{
amount = 50, -- more than one value can be set at a time
player = function(player)
return player -- default is the player using the command
end
}
Now the params are set up we can alter how the command works, we can set auth flags, add aliases to this command or enable "auto concat"
which is when you want all extra words to be concatenated onto the end of the last param, useful for reason or messages:
:set_flag('admin_only',true) -- in our case we want "admin_only" to be set to true so only admins can use the command
:add_alias('name','rname') -- we also add two aliases here: "name" and "rname" which point to this command
-- :enable_auto_concat() we do not use this in our case but this can also be used to enable the "auto concat" feature
And finally we want to register a callback to this command, the callback is what defines what the command does, can be as complex as you
want it to be to as simple as our example; the command receives two params plus all that you have defines:
1) the player who used the command
2) in our case repeat_count which will be a number
3) in our case smiley which will be a boolean
4) the raw input; this param is always last as is always present as a catch all
:register(function(player,repeat_count,smiley,raw)
-- this is to show the value for raw as this is an example command, the log file will also show this
game.print(player.name..' used a command with input: '..raw)
local msg = ') '..player.name
if smiley then
-- this is where that smiley param is used
msg = ':'..msg
end
for 1 = 1,repeat_count do
-- this print function will return ANY value to the user in a desync safe manor, this includes if the command was used through rcon
Command.print(1..msg)
end
-- see below for what else can be used here
end)
Some other useful functions that can be used are:
Commands.print(any,colour[opt]) -- this will return any value value to the user including if it is ran through rcon console
Commands.error(message[opt]) -- this returns a warning to the user, aka an error that does not prevent execution of the command
return Commands.error(message[opt]) -- this returns an error to the user, and will halt the command execution, ie no success message is returned
Commands.success(message[opt]) -- used to return a success message however dont use this method see below
return Commands.success(message[opt]) -- will return the success message to the user and your given message, halts execution
Example Code:
Commands.new_command('repeat-name','Will repeat you name a number of times in chat.')
:add_param('repeat-count',false,'number-range-int',1,5) -- required int in range 1 to 5 inclusive
:add_param('smiley',true,function(input,player,reject) -- optional boolean default false
if not input then return end
if input:lower() == 'true' or input:lower() == 'yes' then
return true
@@ -120,16 +175,16 @@
end
end)
:set_defaults{smiley=false}
:set_flag('admin_only',true)
:add_alias('name','rname')
:set_flag('admin_only',true) -- command is admin only
:add_alias('name','rname') -- allow alias: name and rname
:register(function(player,repeat_count,smiley,raw)
game.print(player.name..' used a command with input: '..raw)
local msg = ') '..player.name
if smiley then
msg = ':'..msg
end
for i = 1,repeat_count do
Commands.print(i..msg)
for 1 = 1,repeat_count do
Command.print(1..msg)
end
end)
@@ -148,7 +203,7 @@
Commands.add_command(name,help) --- Creates a new command object to added details to, note this does not register the command to the game
Commands._prototype:add_param(name,optional,parse,...) --- Adds a new param to the command this will be displayed in the help and used to parse the input
Commands._prototype:set_defaults(defaults) --- Adds default values to params only matters if the param is optional
Commands._prototype:set_flag(name,value) --- Adds a tag to the command which is passed via the tags param to the authenticators, can be used to assign command roles or type
Commands._prototype:set_flag(name,value) --- Adds a tag to the command which is passed via the flags param to the authenticators, can be used to assign command roles or type
Commands._prototype:add_alias(...) --- Adds an alias or multiple that will also be registered with the same callback, eg /teleport can be /tp with both working
Commands._prototype:enable_auto_concat() --- Enables auto concatenation of any params on the end so quotes are not needed for last param
Commands._prototype:register(callback) --- Adds the callback to the command and registers all aliases, params and help message with the game
@@ -183,7 +238,7 @@ local Commands = {
-- @tparam callback function the callback you want to register as an authenticator
-- callback param - player: LuaPlayer - the player who is trying to use the command
-- callback param - command: string - the name of the command which is being used
-- callback param - tags: table - any tags which have been set for the command
-- callback param - flags: table - any flags which have been set for the command
-- callback param - reject: function(error_message?: string) - call to fail authorize with optional error message
-- @treturn number the index it was inserted at use to remove the callback, if anon function used
function Commands.add_authenticator(callback)
@@ -241,8 +296,8 @@ function Commands.authorize(player,command_name)
-- loops over each authorization callback if any return false or unauthorized command will fail
for _,callback in pairs(Commands.authorization) do
-- callback(player: LuaPlayer, command: string, tags: table, reject: function(error_message?: string))
local success, rtn = pcall(callback,player,command_name,command_data.tags,auth_fail)
-- callback(player: LuaPlayer, command: string, flags: table, reject: function(error_message?: string))
local success, rtn = pcall(callback,player,command_name,command_data.flags,auth_fail)
-- error handler
if not success then
-- the callback failed to run
@@ -360,7 +415,7 @@ function Commands.new_command(name,help)
auto_concat=false,
min_param_count=0,
max_param_count=0,
tags={}, -- stores tags that can be used by auth
flags={}, -- stores flags that can be used by auth
aliases={}, -- n = name: string
params={}, -- [param_name] = {optional: boolean, default: any, parse: function, parse_args: table}
}, {
@@ -408,18 +463,18 @@ function Commands._prototype:set_defaults(defaults)
return self
end
--- Adds a tag to the command which is passed via the tags param to the authenticators, can be used to assign command roles or type
-- @tparam name string the name of the tag to be added; used to keep tags separate
--- Adds a tag to the command which is passed via the flags param to the authenticators, can be used to assign command roles or type
-- @tparam name string the name of the tag to be added; used to keep flags separate
-- @tparam value any the tag that you want can be anything that the authenticators are expecting
-- nb: if value is nil then name will be assumed as the value and added at a numbered index
-- @treturn Commands._prototype pass through to allow more functions to be called
function Commands._prototype:set_flag(name,value)
if not value then
-- value not given so name is the value
table.insert(self.tags,name)
table.insert(self.flags,name)
else
-- name is given so its key: value
self.tags[name] = value
self.flags[name] = value
end
return self
end