Skip to content

Scripting

Hyomoto edited this page Oct 3, 2021 · 11 revisions
Jump To back Overview Contents Macros Index

Module Overview

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

Contents

ScriptEngine

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.

Scripts

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!"

Keywords

  • 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. Using as 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 with value 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 with while 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...] - Like temp, 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

Macros

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.
Clone this wiki locally