Scripting

ScriptingAs of v2.1.0, ScrollingMenuSign has support for running external scripts from commands. With a little knowledge of scripting and the Bukkit API, some very powerful effects can be achieved.

Overview

Using the special SCRIPT command, any uploaded script can be run as follows:

/sms add mymenu "Do something" "SCRIPT myscript.js"

All scripts must be placed in plugins/ScrollingMenuSign/scripts. The script file extension is important - see Language Support below.

Scripts have full access to the Bukkit and ScrollingMenuSign Java API's. The following variables are automatically provided to all scripts:

argsA String array with any arguments passed from the ScrollingMenuSign command
commandSenderA reference to the person or entity which triggered this command (usually a Bukkit player, but possibly the CraftBukkit console)
viewA reference to the ScrollingMenuSign view object that triggered this command
resultA boolean variable, true by default. As of v2.1.1, the script may set this variable to false to indicate a "failed" script result (see below)

See the examples below for more information on using those variables.

See https://dl.dropboxusercontent.com/u/12467600/ScrollingMenuSign/apidocs/index.html for current ScrollingMenuSign API reference.

Security

Because scripts basically have unlimited API access, they can do anything on your server, up to and including destroying it. You have been warned.

The barrier of protection is that access to the server's filesystem is needed to upload any scripts to plugins/ScrollingMenuSign/scripts. Anyone who has this level of access can basically do whatever they like to your server anyway.

It should go without saying that only trusted server admins should be allowed to upload scripts for use with ScrollingMenuSign.

You should also bear in mind that scripts all run in the main server thread. Therefore, you should take care not to spend too long in any script, or you will cause server lag.

Language Support

ScrollingMenuSign has so far been tested with Javascript and Jython, but any language available via the JSR223 Java scripting extensions should be usable. The scripting language used to run a script is automatically chosen based on the file extension of the script being run, e.g. "script1.js" will be run with the Javascript interpreter, and "script2.py" will be run with the Jython interpreter, if it's available.

Javascript

Javascript scripts are supported out of the box, with no extra effort needed to get them running.

Example Javascript script (explode.js):

importClass(org.bukkit.entity.Player);

if (commandSender instanceof Player) {
  var power = args.length > 0 ? args[0] : 0;
  commandSender.getWorld().createExplosion(commandSender.getLocation(), power);
}

The above script checks if the command sender is actually a player, and if so gets an integer from the first argument passed to the script (args[0]), or zero if no argument was passed. It then uses the Bukkit API to create an explosion at the player's location. To use:

/sms add mymenu "Fake Explosion" "SCRIPT explode.js"
/sms add mymenu "Small Explosion" "SCRIPT explode.js 2"
/sms add mymenu "Big Explosion" "SCRIPT explode.js 6"

Another example (rndblock.js):

// When clicking a view, turn any blocks that view is attached to
// to a randomly-coloured wool block

importClass(org.bukkit.Material);
importClass(org.bukkit.entity.Player);

if (commandSender instanceof Player) {
        var colour = Math.floor(Math.random() * 16); 
        locs = view.getLocationsArray();
        for (var i in locs) {
                var block = locs[i].getBlock();
                if (block.getType() == Material.SIGN_POST || block.getType() == Material.WALL_SIGN) {
                        var attached = block.getRelative(block.getState().getData().getAttachedFace());
                        attached.setTypeIdAndData(35, colour, false);
                }
        }
}

Again, the command sender is checked for being a Player and if so, the script uses the ScrollingMenuSign API to enumerate all the possible locations for the view that triggered this script; for a sign view that will be one location, for a multi-sign view it may be several, for a redstone view it may be one or more, and for other view types (map/inventory/spout) there will be zero locations. For each location, if it's a sign, use the Bukkit API to turn block the sign is attached to into a randomly coloured wool block.

And to use it:

/sms add mymenu "Random Block" "SCRIPT rndblock.js"

Python (Jython)

To add Python support, you will need to run your CraftBukkit server with Jython included. I have used the following method successfully:

  • Install Jython from http://www.jython.org/downloads.html - I have used 2.5.4rc1. Earlier releases may be problematic.
  • $JYTHONDIR is where you installed Jython
  • $OTHER_OPTS is any other options you normally pass when running CraftBukkit
  • Run CraftBukkit with:

java -classpath $JYTHONDIR/jython.jar:$JYTHONDIR/Lib:craftbukkit.jar $OTHER_OPTS org.bukkit.craftbukkit.Main

You should now be able to run Python scripts with a .py extension. Some examples:

# When clicking a view, turn any blocks that view is attached to
# to a randomly-coloured wool block

import org.bukkit.entity.Player
import org.bukkit.Material
import random

if isinstance(commandSender, org.bukkit.entity.Player):
    colour = random.randint(0, 15);
    for loc in view.getLocationsArray():
        block = loc.getBlock();
        if (block.getType().name() == 'SIGN_POST' or block.getType().name() == 'WALL_SIGN'):
            attached = block.getRelative(block.getState().getData().getAttachedFace());
            attached.setTypeIdAndData(35, colour, False);

The above is pretty much the Python equivalent of rndblock.js shown before. To use:

/sms add mymenu "Random Block" "SCRIPT rndblock.py"

Return Value

The special result variable may be used to return a "failed" status from the script by setting it to false. What does it mean when a script has "failed"? It is considered to be "restricted" by the Command Parser in that case, which is useful for command chaining purposes. This is best illustrated by an example:

// Script: "even.js"
// return true if the argument is even, false otherwise
var result = args[0] % 2 == 0;

Add the following menu items to call the script:

/sms add mymenu "Even 2?" "SCRIPT even.js 2 && \\yes, 2 is even"
/sms add mymenu "Even 3?" "SCRIPT even.js 3 $$ \\no, 3 is odd"

The above two commands will both produce output - the script returns a true value when 2 is passed as an argument, and a false value when 3 is passed. The "&&" chaining operator runs the following command IF the previous command succeeded (returned true), and the "$$" chaining operator runs the following command IF the previous command failed (returned false).


Comments

Posts Quoted:
Reply
Clear All Quotes