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, MethodScript. 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 its own methodologies.
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. A function is identified
by a literal string, followed by parenthesis. So in
func(), "func" is the name of the function, and "("
and ")" begin and end the function argument list (in this case, there are no arguments being passed in.) Functions
can have zero, one, or two or more arguments passed to them. In the case of two or more arguments, each argument
is separated by a comma. For instance:
Comments are a useful way to mark up your code for humans to read later. (With one exception) comments are ignored by the compiler, and you are free to put whatever information you wish in a comment. There are 4 ways to comment code.
// symbols are identical. They are line comments. When either is encountered,
the remainder of the line is ignored by the compiler.
// is preferred over
# is not deprecated, nor will it ever be. The only exception to this preference is when using a
hashbang for cmdline code.
Block comments start with
/* and end with
*/ This causes the compiler to ignore everything
within the comment block, including newlines.
Smart comments are like block comments, but start with
/** (two asterisks) instead. They are currently
unused, but are reserved for future use. They are used for documentation generation functionality.
In a script, there are several types of data. The language is currently 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 or double quotes: 'string', "also a 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
- 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 (or simply a variable) is a variable that can be defined and used from within the script.
Constant variables ($var), are assigned by the user at command runtime, and are technically constants as far as the rest of 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, or the = operator. If an ivariable is used without first being defined, the value of the variable will be 0, 0.0, '', false, or null, 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.
Sometimes a script may sometimes cause an error that could be anticipated. Instead of just having the script die, it is possible to catch these errors, and perform alternate functionality. MethodScript 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 aliases.msa
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 MethodScript, that is, it has a slightly different syntax than aliases.msa. In aliases.msa, each alias is defined, along with a snippet of MethodScript. For instance, in the alias,
/test [$command=''] = >>> <<< part is the alias markup, and is not actual MethodScript,
that is, the symbols and characters follow different meanings than inside the alias definition. In this example,
console($command); is pure MethodScript.
aliases.msa 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 MethodScript file, and it is as if in each execution unit, it is
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, as well as execution queue elements, and others.
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.
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:
Second execution unit, the /test $alias alias:
Third execution unit, the event handler created in main.ms:
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.
The built in
/reloadaliases command is used to reload just CommandHelper, though
also reload CommandHelper. However, using the reloadaliases command can allow for finer grained control of what all gets
reloaded. By default, the command reloads the following things about CommandHelper:
- Scripts - Any changes that you have made to any scripts will be reloaded and applied. Main files are re-run,
and any new commands are re-registered.
- Globals - Global values, set with
export()are cleared out.
- Tasks - Any tasks set with
- Execution-Queue - Any tasks queued up with the queue_* family of functions is cleared.
- Persistance-Config - Any changes to the persistance.config file are reloaded
- Profiler - The profiler.config file is reloaded
- Extensions - Extensions are reloaded
You can actually reload these sub-modules individually if you want, by passing parameters to the command. There are two
modes, whitelist, or by default, blacklist. In blacklist mode, all modules get reloaded except the modules that are
blacklisted, which aren't reloaded. In whitelist mode, only the specified modules are reloaded. You use the underlined
letter to refer to that specific module. For instance, if you want to reload everything but leave the exported variables
and execution queue alone, you can run
/reloadaliases -ge. If you ONLY want to reload tasks, you can
/reloadaliases --whitelist -t. Note that reloading individual modules isn't normally encouraged,
because it can put your server in an inconsistent and unreproducable state if you aren't careful. Running
/reloadaliases by itself (which reloads everything) is recommended. You can also run
/reloadaliases -h for the usage instructions and long options list.
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 is possible to do far more advanced things.
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:
|# or //||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 string concatenation happens automatically (known as autoconcatenation). Let's take the following example:
In the first run function, we see that '/run', 'this', and 'command' are all technically separate arguments, but because they are not separated by commas, operators, or other symbols, they are automatically concatenated together, using the
sconcat() function, essentially yeilding: '/run this command', as you would expect. This is also the
behavior exhibited by a simple alias that uses non-strict formatting:
/cmd = /real command.
'/real' and 'command' are automatically sconcatenated together, and then run.
Many of the scripting concepts are addressed in greater depth in the Learning Trail, shown below. The MScript topics are of great value to go through, and they build off what you have already learned in this tutorial. What is provided in this lesson should be enough to get you started with basic script writing, so start trying to apply these concepts to your own scripts, and continue going down the learning trail!