Skip to content

Scripts

McHorse edited this page Aug 11, 2021 · 4 revisions

Scripts are programs that allow Mappet map makers to program custom game logic in JS (JavaScript, it has nothing to do with Java) programming language powered by Nashorn (ES5) scripting engine.

If you don't know or have very little experience with JS, try finding some tutorials online that cover basics, like this one. This wiki isn't a JS wiki, you need to learn it elsewhere.

Scripts can be executed either from triggers or from /mp script exec command. Scripts can be edited by going to world's mappet/scripts/ folder, and creating there a .js file, and edit in a text editor of your choice, or directly in the game in the Mappet's dashboard scripting panel:

Mappet's script editor

Scripting

The way scripts work: you define a function in the JS file, and then that function passes one argument, which is a IScriptEvent object which contains all the information you need about the event that was passed from a trigger or from /mp script exec command, like server (IScriptServer), world (IScriptWorld) and event related entities (IScriptEntity, subject and object).

Depending on the trigger, there might be no subject/object, or even world, for example server tick and server on load global triggers have only access to the server (IScriptServer).

Basic example

For example, assuming that we have a script named up.js in world's mappet/scripts/ folder with following code:

function main(c)
{
    c.getSubject().setMotion(0, 1, 0);
}

If we execute it with /mp script exec @r up, it will throw a random player in the air. To fully utilize scripts, you need to learn more about Mappet's scripting API, which can be found here for the latest version of Mappet.

In-game documentation

Alternatively, Mappet comes with an in-game up-to-date documentation which also features code samples. You can open the in-game documentation in scripts panel, by pressing ❔ icon button under REPL icon, and there you'll find all the available scripting functions and APIs along with examples of how to use majority of them:

Mappet's in-game documentation

Script editor

Script editor is a simple text editor that allows editing scripts directly in the game. It offers:

  • Syntax highlighting similar to Sublime Text 3
  • Syntax highlighting themes (it comes with 2 by default)
  • Simple undo (Ctrl + Z) and redo (Ctrl + Y)
  • Basic code editing features:
    • Keeping indentation when inserting a new line
    • Insertion of closing ), ], }, ", '
    • Unwrapping of {}
    • Shifting selection by pressing Tab forward or Shift + Tab backward
  • Typing sounds 🗿 (they can be disabled in Mappet's mod options in Ctrl + 0, Mappet, Script editor)

Script themes

Script themes can be managed in Ctrl + 0, Mappet, Script editor. Click on Edit themes... button and it will open theme editor where you can edit colors, add/remove themes. To pick current theme, simply click on desired theme in the sidebar on the left and close the theme editor.

Syntax highlighting theme editor

REPL

Another useful feature that Mappet's scripts panel is offering is a REPL (Read-Eval-Print Loop). REPL is basically an interactive console. You type into the console some JS code, it evaluates it, and then sends the result of the code's evaluation.

Using REPL, you can experiment with short JS code snippets (for example you can throw yourself into the air by executing c.getSubject().setMotion(0, 1, 0) in the REPL) or using mappet.dump(Object) you can analyze what fields and methods Java objects have.

Mappet's REPL

REPL's code editor is same as script editor, for a couple of exceptions:

  • Enter key evaluates the code, so if you want to insert a new line, you need to press Shift + Enter key combo
  • Ctrl + Arrow up and Ctrl + Arrow down key combos allow to cycle through history of evaluated code snippets

Script libraries

Script libraries allow you to organize code into separate files. Let's say you have some common functions which are used in multiple scripts. Instead of copy pasting that code across multiple files, you can move specific functions to a separate file, and add it as a library to all scripts that use that function.

You can manage libraries by clicking on Manage libraries... button:

Manage libraries... icon

After clicking it, an overlay will popup and you'd be able manage libraries there. To add a library, right click in the middle of the popup, and Add a library.... Then you will be able to pick the script you want to be used as a library, and close the second popup. To remove a library, select a library, right click the list and click Remove this library...

Example

So when we have two scripts, main:

function main(c)
{
    librarySayHi(c);
}

And library:

function librarySayHi(c)
{
    c.send("Hi!");
}

You can add library to main script, and it will be able to use librarySayHi function within main. BEWARE: due to the nature of implementation, global variables and classes implemented through prototype, won't be accessible globally to all scripts. So if you want global variables and classes to be shared all within one context, you'll need to create a single script that will combine all of the scripts.

Unique

Enabling Unique toggle is highly recommended when using libraries, as it won't have to read, join and evaluate multiple scripts every time script gets executed via trigger or /mp script exec command.

In a couple of words, when Unique is enabled, the script won't get unloaded after it gets executed, but rather it gets cached, and it can be run again without reading, joining and executing the code, thus making it also possible to reuse global variables within current script's context.

Examples

Calling Minecraft code directly

Since Nashorn works directly on top of Java, you can technically use Forge and Minecraft code directly from JS using functions like Java.type(full_class_name), however you'll quickly run into problems as most of Minecraft methods are obfuscated.

You can use MCP mappings that come with Forge's MDK (after you setup gradlew setupDecompWorkspace). They will be located in user's .gradle/caches/minecraft/de/oceanlabs/mcp/mcp_snapshot/SNAPSHOT_DATE/1.12.2/srgs/. You will need to open the srg-mcp.srg file with a text editor, and there will be all mappings. I would recommend setting up a development environment with IntelliJ/Eclipse and going through the source, this way it will be easier.

For example, here is a script that outputs the max player the server is allowed to host:

function main(c)
{
    // According to srg-mcp.srg, MinecraftServer.getMaxPlayers() is called "func_71275_y"
    c.send(c.getServer().getMinecraftServer().func_71275_y());
}

Every wrapper in the scripting API (like IScriptServer, IScriptWorld, etc.) can return the original object upon which

Forge code can also be called. Here is an example of getting blocks registry and outputting every block ID in the chat (probably not a good idea):

function main(c)
{
    // Get Forge's registry
    var forgeRegistries = Java.type("net.minecraftforge.fml.common.registry.ForgeRegistries");
    var entities = [];
    
    // Don't try with BLOCKS, it will be too much text and it will crash client or server
    for each (var key in forgeRegistries.ENTITIES.getKeys())
    {
        entities.push(key.toString());
    }
    
    // Send list of all loaded entities to all players
    c.send(entities.join(", "));
}

Teleporting to where player looked

Here is a simple script that demonstrates ray tracing API:

function main(c)
{
    // Get the player
    var subject = c.getSubject();
    // Ray trace up to 64 blocks ignoring entities
    var ray = subject.rayTraceBlock(64);
    
    // Important check as ray tracing could fail
    if (ray.isBlock())
    {
        var pos = ray.getBlock();
        
        // Teleport the player on top of the block
        subject.setPosition(pos.x + 0.5, pos.y + 1, pos.z + 0.5);
    }
}

You can bind this script to a keybind using trigger hotkeys and then every time player presses the key it will teleport them to where they look.