Skip to content
This repository was archived by the owner on Dec 30, 2025. It is now read-only.

Scripting

JulieWoolie edited this page Feb 11, 2023 · 18 revisions

Running Scripts

By Command

/scripts run <script name> [<function name>]
  • <script name> - The script's file path within the scripts directory, you can use tab completions to find the one you're lookingfor.
  • [function name] - An optional argument for the name of the function within the script to call.

You can also execute raw JavaScript code by using the eval command like so:

/scripts eval <java script code>

Examples:

/scripts run path/to/script.js
Simply runs the script file, not calling any methods

/scripts run path/to/script.js methodName
Evaluates the script file like above, and after executes The method named 'methodName'

/scripts eval let x = 0; let y = 1; print(x + y);
Executes the raw JavaScript code that's passed to it

From Java code

First, we'll need to create an instance of the Script class, we can do so using one of the following methods:

Via a file path:

import net.fothecrown.core.script2.*;

Path path = /* Acquire it somehow */
Script script = Script.of(path);

// This string path is relative to the `plugins/ForTheCrown/scripts`
// directory
String stringPath = /* Create a path */
Script anotherScript = Script.of(stringPath);

Or via raw JavaScript:

String jsCode = "print('Hello, world!')";
Script script = Script.ofCode(jsCode);

Next, we'll need to compile and then evaluate the script

Script script = // ...;

// This will load and compile the script
script.compile();

// After compilation, you can place any optional binding
// values into the script, these will be accesible from
// inside the script
script.put("a_binding_value", "Foor");

// Evaluate the script's main function, aka run it
// Returns a result detailing how the execution went.
ScriptResult result = script.eval();

// Test if the execution failed due to an error
// Note: If any error was thrown, it will be logged automatically
if (result.error().isPresent()) {
  return;
}

// result() returns an Optional<Object>, empty if there was an
// error, or if the function returned nothing
Object resultingObject = result.result().orElse(null);

// This will return the resultingObject, shown above, as an
// Optional<Boolean>. If the resultingObject doesn't exist, or
// can't be converted to a boolean, this method returns empty
result.asBoolean();

Loader

scripts/loader.toml is used by FTC's script loader to load scripts on plugin startup. See the file itself for it's format.

Java Interopability

The JavaScript engine used by FTC (Nashorn) is completely compatible with Java.

To this end, you can import java classes like almost just like you would in regular Java, for example, to import the Time utility class, you can use the following:

import "@ftc.utils.Time";

The @ftc is an 'import placeholder', basically used to make import statements shorter and less of a hassel to write. All import placeholders are defined in import_placeholders.toml

If you're encountering issues with the above shown method, you can fallback to Nashorn's actual method of importing classes:

const Time = Java.type("net.forthecrown.utils.Time");

FYI, the method of using the 'import' keyword, is just remapped to the second method by a preprocessor, thus, in effect, the 2 above methods are essentially the same

Please also be aware that the Nashorn engine, is not 100% up to ECMAscript 6 standards, there are many missing features. Be careful when using newer JavaScript features such as classes, as they do not exist within the engine.

Built in

The script engine comes with several custom built in parts, the first of these are the classes that are automatically imported in to every script.
Be aware, this list may not be complete, see the BuiltIn class for the definitive list of all imported classes.

  • org.bukkit.Bukkit
  • org.bukkit.Location
  • org.bukkit.enity.EntityType
  • org.bukkit.Material
  • net.kyori.adventure.text.Component
  • net.kyori.adventure.text.format.NamedTextColor
  • net.kyori.adventure.text.event.ClickEvent
  • net.kyori.adventure.text.event.HoverEvent
  • net.kyori.adventure.text.format.Style
  • net.kyori.adventure.text.format.TextDecoration
  • net.forthecrown.utils.text.Text
  • net.forthecrown.utils.Util
  • net.forthecrown.utils.Cooldowns
  • net.forthecrown.utils.inventory.ItemStacks
  • net.forthecrown.utils.math.Bounds3i
  • net.forthecrown.utils.math.WorldVec3i
  • net.forthecrown.utils.math.WorldBounds3i
  • net.forthecrown.utils.math.Vectors
  • net.forthecrown.user.Users

Ontop of these imports, you are given a Log4J logger to use for logging, accessible like so:

logger.info("I am logged message! :)");
Note: Any logger calls on level ERROR will be automatically forwarded to the error-log channel in Discord.

And a function to compile other scripts:

// This will only perform the Script#compile() call
// Note: the path given to this function is relative to
//  file of the current script
const otherScript = compile("path/to/script.js");

// Here, just like above, you can place objects into the script's
// bindings, but in a simpler way, like so:
otherScript.foo = "bar";

// calls Script#eval()
// Will throw an exception if the script's evaluation fails and
// returns the result of the script's evaluation
let result = otherScript();

Extra

Links for some extra reading:

Clone this wiki locally