Need help? Ask on the forum, ask a fellow user or developer (join us on IRC), or contact sk89q.
Contribute to the wiki, edit this page! Register an account, or login with Facebook first.
HEY! Do you integrate (or want to) a plugin into yours? Do you help work on WE/WG/etc.? Please subscribe to our mailing list!
CommandHelper
Advanced Guide
If you are wanting to do more dynamic things other than variables, CommandHelper allows you to do this through the use of Turing complete language. If you aren't familiar with any type of programming, you may wish to find resources on languages like Java and PHP. The language mirrors both languages in certain ways, as well as introducing it's own methodologies.
Contents |
Functions
Functions allow for very dynamic scripts to be run. There are many defined functions, including functions that provide control flow functionality. For the full list of functions, see the API.
Final Variable
Final variables allow you to specify a variable number of arguments be assigned to one variable. There is a special variable defined for this purpose, "$". This is particularly useful for writing some sort of message alias. Say we want to create an alias for /tell.
/msg $player $ = /tell $player $
Multiline Construct
The multiline construct is only available in global configs, due to the sheer nature of how users input aliases.
Since complex alias scripts would be hard to read if they were only on one line, the multiline construct allows you to put as many newlines in the middle of your alias definition as you want. To use the multiline construct, use the following syntax:
/cmd = >>> #As many newlines as you want <<<
The special symbols >>> and <<< are the "multiline start" and "multiline end" symbols. They may not appear in any other context throughout your script. Newlines inside the construct are simply ignored, and do not denote the end of the alias. The multiline end symbol does not have to be on a line of it's own, but the multiline start symbol must come directly after the equals sign.
Alias Signatures
To better understand how to write a script and debug a potential problem, it may be helpful to understand the basics of how the alias engine works. When the config file is initially parsed, it is checked for compile errors, but no commands are being run at that time. It is however compiled into an intermediate stage that can more quickly be processed when a user runs a command. Once a user runs a command, this sequence is followed:
The command is checked against the signatures of all defined aliases (the left side of the alias) to see if it matches. It is not possible to have the same signature for two aliases within the config file, but a user may have defined an alias with an identical signature in their personal aliases. In this event, the global alias will be run. If a match is found, any variables are assigned, then filled in on the right side, and functions are resolved, then each command in the macro (possibly only one command) is run.
For a command to "match" an alias, the following factors are taken into consideration. Any literals must match exactly. Non-optional variables must be present, but can be anything. Optional variables may be present, but extra arguments will make the match fail. (Except in the case of a final variable being present.) Let's look at the following two command signatures:
/cmd $var1 [$var2] /cmd $var1 $var2
Both of these signatures are ambiguous, because the command "/cmd one two" would match both signatures.
/cmd $var1 /cmd $var1 $var2
The above two commands would not be ambiguous however, because the command "/cmd one two" would only match the second one.
Data Types
In the alias script, there are several "types" of data. The language is loosely typed however, so the string '2' is equivalent to the integer 2, is equivalent to the double 2.0, is equivalent to the boolean true. Values are cast just before they are used. Note that sometimes data cannot be cast, for instance the string 'string' cannot be cast to a number, so a runtime exception will be thrown if a function expects a number, and is given that. Also, arrays are not able to be cast into other data types, but can contain values of any data type.
- boolean - Created with the "true" or "false" keywords: true
- int - Any number that doesn't have a decimal point: 2
- double - Any number that has a decimal point: 2.0
- string - Any set of characters surrounded by single quotes: 'string'. \u0000 inside a string will allow for arbitrary unicode characters to be inserted into a string, and \n is a newline. \\ (double slash) inserts a literal slash, and \' will insert a literal quote.
- array - An array of any other datatypes, including other arrays. Created by using the function
array - null - Created with the "null" keyword: null
- void - Some functions return void, which is actually a datatype. When viewed as a string, it is equivalent to an empty string. Cannot be created directly.
- ivariable - An ivarible is a variable that can be defined and used from within the script. Normal variables ($var) are defined by the user at command runtime, and are technically constants as far as the script is concerned. IVariables can be defined by the script writer and assigned various values that can change throughout the script running. To define and use an ivariable, use the assign() function. If an ivariable is used without first being defined, the value of the variable will be 0, 0.0, '', false, {}, depending on how it is used. Most functions use the value in the ivariable without caring that it is an ivariable, but it is possible that a function requires that a certain argument be an ivariable, such as the for() function.
Exception Handling
Sometimes a script may sometimes cause an error that could be anticipated. Instead of just having the script die, it is now possible to catch these errors, and perform alternate functionality. MScript mirrors the functionality of languages like PHP and Java with exception handling. For more information about exception handling in MScript, see this page. For a more general discussion on exception handling, see this page on Wikipedia.
main.ms, auto_include.ms, and config.txt
Each of these files serve a separate purpose. main.ms is run at server startup, only once. Typically, you use this to register bound events, using the bind() function, or anything else you want to run only once. Keep in mind, this is re-run when you /reloadaliases, but bound events and intervals and such are stopped, so you won't have multiples of anything. main.ms uses pure MScript, that is, it has a slightly different syntax than config.txt. In config.txt, each alias is defined, along with a snippet of MScript. For instance, in the alias,
/test [$command=''] = >>>
console($command)
<<<
the /test [$command=''] = >>> <<< part is the alias markup, and is not actual MScript, that is, the symbols and characters follow different meanings than inside the alias definition. In this example, console($command) is pure MScript.
config.txt is where your aliases go, and each alias is a fully separate compilation unit, in fact, even a compile error in one alias will not usually interfere with the other aliases.
The auto_include.ms file is also a pure MScript file, and it is as if in each execution unit, it is automatically include()ed for you. There are a few different ways to make an execution unit. The first way is to create a new alias. Each alias is it's own execution unit, and uniquely to the aliases, each alias is also it's own compilation unit. A bound event handler is another execution unit, and the entirety of main.ms is an execution unit. set_interval and set_timeout are also separate execution unit.
So, what is an execution unit? It is a unit of code that gets run, from top to bottom. You can think of it as an insertion point to your code, or places that will start up your code. An execution unit is often times made up of several parts, so let's create a multifile example, which demonstrates a few execution units.
In auto_include.ms:
proc(_my_proc,
console('In _my_proc')
)
In main.ms:
bind(some_event, null, null, @event,
_my_proc()
console('In some_event')
)
console('Finished binding all events in main.ms')
In config.txt:
/test $alias = >>>
_my_proc()
console('In /test $alias')
<<<
Here, we have created 3 separate execution units. main.ms and aliases are implicitly created, simply by their existence, but here we have also created an event handler, which will get run when some_event is triggered. main.ms is triggered upon server startup (and /reloadaliases) and aliases are triggered when a command is run. When this script is compiled, you can essentially visualize the execution units as ending up like this: (with some code removed to make things more readable)
First execution unit, main.ms:
proc(_my_proc, ...)
bind(some_event, null, null, @event, <CREATION OF EXECUTION UNIT>)
console('Finished binding all events in main.ms')
Second execution unit, the /test $alias alias:
proc(_my_proc, ...)
_my_proc()
console('In /test $alias')
Third execution unit, the event handler created in main.ms:
proc(_my_proc, ...)
_my_proc()
console('In some_event')
In this simple example, you can think of each execution path as a line through your code (in reality, it's a tree, but if that doesn't make sense to you, ignore that for now). That line follows very specific rules based on code structure, so how you lay out your code is important to be able to visualize.
Scripting Examples
Here are a few more complex examples to get you started on how to use the advanced features of CommandHelper. Because of the Turing completeness of the plugin, it may be possible to do things I've never even thought of.
#Better tp. If "/tp name" is used, it implies "/tp thisplayer name"
#but if "/tp name1 name2" is used, it implies "/tp name1 name2"
/tp $p1 [$p2=''] = /tp if(equals($p2, ''), player(), $p1) if(equals($p2, ''), $p1, $p2)
###
#Sets the time on the server, and uses english words to set the time
#If the english word isn't "day" or "night", it uses the number
# Note that the equals function returns a true value if the two
# parameters are the same, and if() runs the second argument if the
# first argument is true, and the third argument if it is false.
# To make an "if else" statement, you can nest if() calls, as shown
# here.
/time $time = /time set if(equals($time, 'day'), 1, if(equals($time, 'night'), 13500, $time))
###
#Give the player 64 each of gold tools
# Note that the data_values function uses the enum values of
# the material
/kit gold = /give player() data_values('gold_pickaxe') 64 \
/give player() data_values('gold_spade') 64 \
/give player() data_values('gold_axe') 64
###
#Shows help information
# This demonstrates how to use die() and msg(). They work basically the same, except
# die() kills the command if evaluated. They both send a message to the player.
/help [$cmd=''] = if(equals($cmd, ''), die('Do you need halp?'), if(equals($cmd, 'commandhelper'), msg('The best plugin ever!'), ''))
###
#Simple hello world scripts
/hello world console = console('Hello Console!')
/hello world player = msg('Hello Player!')
Symbol Table
In your scripts, there are a few special symbols that are not treated as literals. In the event of a compiler error, it may be helpful to know what each symbol is called. These are as follows:
| Symbol | Name | Meaning |
|---|---|---|
| # | comment | Denotes that the rest of this line is a comment |
| /* ... */ | block comment | Everything inside this (newlines included) is a comment. The only thing you can't have inside a block comment is a */ (because that ends the comment) |
| = | opt_var_assign or alias_end | If inside an optional variable [$opt='val'], it's an opt_var_assign. Otherwise, it's a alias_end |
| [ and ] | lsquare_bracket and rsquare_bracket | Denotes that this variable is optional, if included on the left side of an alias, or accesses an element in an array if used on the right. |
| , | comma | Separates arguments in a function |
| ( and ) | func_start and func_end | The start and end of a function. If a literal proceeds the func_start symbol, it is called func_name |
| New Line | newline | An 'enter' in the file |
| >>> and <<< | multiline_start and multiline_end | Starts and stops a multiline construct |
| / | command | This symbol followed by a literal denotes a command |
| \ | separator | Separates each macro command |
| $... | variable | When a single dollar sign, it is final_var, otherwise variable |
| @... | ivariable | This is a variable that can be defined and then used on the right side |
| ...: | label | This is a label for commands |
| All other characters | lit | Anything not defined above is a lit |
Note that a lit and string are treated the same, however special characters must be put in a string to be treated as literal character. Technically all other special characters are treated as literals, but to make your script compatible with future versions, you must put any non-word character inside a string.
Note that as of 3.1.0, string concatenation happens automatically. Let's take the following example:
if(equals(0, @i),
run(/run this command)
die(The command has been run)
,
#else
run(/run another command)
)
Both run and die return void (or nothing), so no concatenation occurs here, however, since no comma separates the two, it is possible to chain together as many functions as needed. In the run function, we see that '/run', 'this', and 'command' are all technically separate arguments, but because they are not separated by commas, they are automatically sconcatenated together, essentially yeilding: '/run this command', as you would expect. This is also the behaviour exhibited by a simple alias: /cmd = /real command. '/real' and 'command' are automatically sconcatenated together, and then run.
| |||||||||||||||||||||||