-
Notifications
You must be signed in to change notification settings - Fork 1
Scripting
Jump To | back |
Overview | Contents | Macros | Index |
---|
The FAST scripting system can read in strings and external files to compile code which can be executed in game. The Scripting engine is entirely sandboxed, thus restricting it from running unintended code. These protections can be disabled if desired. It allows for comparisons, branch logic, loops, and coroutines. It also doesn't rely on external DLLs and is cross-platform as a result. Additionally, while Scripts can be executed without the use of ScriptEngine, doing so will remove some of their features such as a shared scope and automatic coroutine resumption.
** NOTE ** While the scripts rely on a JIT compiler and have had many optimizations applied to them, they are ultimately much slower that pure GML by orders of magnitude. Thus they are not suitable for writing complex code in: this can be worked around by writing expensive operations as GML code and exposing that as a function to your scripts. While optimization is still sought, the current implementation seems to be at its limit for how much I can squeeze out of it. Thus, limiting the scripting engine to cutscene logic, one-time scripts, and coroutines is best.
Dependencies: Core
var _engine = new ScriptEngine();
While GML does not support true parallel execution, you can make multiple engines and run them. As noted above, while each engine does not require significant processing power on it's own, running many scripts per frame can and likely will.
Method | Default | Description |
---|---|---|
execute(script,[arguments...]) |
none | Calls the given script with the provided arguments. |
trace(message) |
none | This is the method that is called by trace commands in-script, not normally needed outside of scripts. |
update() |
none | Updates any scripts that have been yielded by the engine. |
allow_global(yes?) |
true | Setting this to false will disallow use of global in-script. |
set_global(struct) |
undefined | This will set the "global" scope scripts can access, setting this to global will allow the engine access to GMS' global scope. |
set_output(stream) |
none | Used to set the output stream that trace calls will use. By default uses SystemOutput (ie: the output tab in IDE) but any output stream can be used. |
bind(name,method) |
none | By default, functions can not be called via numbers (as they can in GML). Bind will wrap script functions which allows them to be called by the engine. |
push_restriction(array) |
none | Pushes an execution restriction to the engine, this is an array that contains syntax the engine should ignore. |
pop_restriction() |
none | Pops the last restriction pushed to the engine. |
load_async(stream,[function]) |
none | An input stream of files to be loaded. The engine will then load them over time as to not hold up processing of the game and call, if provided, the function upon completion. |
load(filename,[name]) |
none | Loads the given filename as a script. If name is provided, that will be used, otherwise the filename sans extension will be used. |
var _script = new Script().from_string("trace \"Hello World!\"");
Scripts use a human-readable syntax. They are written with an emphasis on simplicity, and thus do not use brackets. Instead, indentation is used to determine code blocks much like Python. There are nine commands, and only one command can be on each line, and comments and block comments are supported using the familiar //
and /**/
syntax. This is an example of a script that takes a single argument and will reduce the amount by 1 each frame until it reaches 0. After which, "Done!" will be written to the output stream.
func a
while a > 0
put a - 1 into a
yield
trace "Done!"
NOTE Blocks are shaped by indentation using spaces or tabs! There are only two rules, a block must share the exact indentation as all other lines in that block (ie: two spaces) and is determined by the first line in the block, while any nested blocks must be more indented than the last. Otherwise, any indentation scheme may be used.
if 10 > 0
if 10 < 0
trace "Impossible!"
trace "Possible!"
trace "Done!"
-
if expression
,else expression
- Used to create conditional branches. Else must follow on from an if block, but can be otherwise chained as much as required.
temp x
if x == 0
trace "0"
else x == 1
trace "1"
else
trace x
-
while expression
- Used to create loops
temp x
while x > 0
put x - 1 into x
- return - Ends the script and, if provided, will return the value given.
return 10 * 2
-
temp name[,name...] [as value...]
- Used to create local variables and/or retrieve arguments passed to the script. Usingas
will allow you to set default values. When used as the first line in the script, x and y would be set to the first two arguments passed to the script. If these arguments were not provided 10 would be used instead. When used elsewhere in the script, simply sets a local variable to the values given.
temp x, y as 10, 10
-
yield [value] [while expression]
- Halts script execution and behaves like a return statement withvalue
being captured by the executing scope. If value is not provided, undefined would be returned. Additionally, the script will continue from this line if called again, typically through update() on the ScriptEngine used to call the script. Yield can also be used withwhile
to provide a condition to wait for.
yield
yield 0
yield 0 while x > 10
NOTE As mentioned, a Script can be called without a ScriptEngine. In this case, capturing a coroutine and executing it yourself is possible but otherwise calling the same script over and over would not result in the expected behavior. A coroutine exists in the context that it was originally called, thus repeatedly calling a script would only cause that script to be repeatedly executed. The coroutine will only continue/complete when called from the original scope, something ScriptEngine handles for you.
-
put expression[,expression...] into variable[,variable...]
- Liketemp
, commas can be used to separate multiple expressions and variables to be set with this command.
put 42 in myVar
put 10, 10 into x, y
-
trace expression
- Writes the given expression, after execution to the output log.
trace "Hello" + " " + "World!"
-
load file as variable
- This command can be used to load additional scripts into the engine.
load "script/wait.txt" as wait
Name | Type | Value | Description |
---|---|---|---|
SCRIPT_EXPRESSION_TYPE_OTHER | int | 0 | Indicates an expression fragment is of type OTHER. |
SCRIPT_EXPRESSION_TYPE_STRING | int | 1 | Indicates an expression fragment is of type STRING. |
SCRIPT_EXPRESSION_TYPE_NUMBER | int | 2 | Indicates an expression fragment is of type NUMBER. |
SCRIPT_EXPRESSION_TYPE_VARIABLE | int | 3 | Indicates an expression fragment is of type VARIABLE. |
SCRIPT_EXPRESSION_TYPE_OPERAND | int | 4 | Indicates an expression fragment is of type OPERAND. |
SCRIPT_EXPRESSION_TYPE_FUNCTION | int | 5 | Indicates an expression fragment is of type FUNCTION. |
Devon Mullane 2020