diff --git a/.classpath b/.classpath
new file mode 100644
index 000000000..aa55a3852
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index 749abfc1a..b6ba71782 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,8 @@ build.local.properties
/src/docs/build/classes/hello.js
/src/docs/build/classes/.netbeans_automatic_build
/src/docs/build/classes/.netbeans_update_resources
+build
+nbproject
+/manifest.mf
+/platform.properties
+CopyLibs
\ No newline at end of file
diff --git a/.project b/.project
new file mode 100644
index 000000000..27091781d
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ ScriptCraft
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.travis.yml b/.travis.yml
index d99f43b3f..b2168ad38 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: java
jdk:
+ - oraclejdk8
- oraclejdk7
- openjdk7
- - openjdk6
diff --git a/README.md b/README.md
index 029084826..809d43fd9 100644
--- a/README.md
+++ b/README.md
@@ -1,132 +1,188 @@
-# Let's begin...
+# ScriptCraft - Modding Minecraft with Javascript
+
+[](https://gitter.im/walterhiggins/ScriptCraft?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+ScriptCraft lets you write Minecraft Mods using Javascript - a
+programming language that's relatively easy to learn and use.
+ScriptCraft is a Minecraft Server plugin which means it must be used
+with a Minecraft server. Once you've downloaded and installed the
+Minecraft Server, then installed the ScriptCraft Plugin you can write
+your own Minecraft mods using Javascript.
I created ScriptCraft to make it easier for younger programmers to
create their own Minecraft Mods. Mods are written using the
-Javascript programming language. Once the ScriptCraft mod is
-installed, you can add your own new Mods by adding Javascript (.js)
+JavaScript programming language. Once the ScriptCraft mod is
+installed, you can add your own new Mods by adding JavaScript (.js)
files in a directory.
- * If you're new to programming and want to start modding Minecraft, then [Start Here][ypgpm].
+ * If you're new to programming and want to start modding Minecraft, then [Start Here][yp].
* If you've already used [Scratch][scr], have attended a few
- [CoderDojo][cd] sessions, or have already dabbled with Javascript,
+ [CoderDojo][cd] sessions, or have already dabbled with JavaScript,
then [Start Here][cda].
* Watch some [demos][ytpl] of what you can do with ScriptCraft.
-This is a simple mod in a file called greet.js in the scriptcraft/plugins directory...
+This is a simple mod in a file called greet.js in the scriptcraft/plugins directory:
```javascript
-exports.greet = function( player ) {
- player.sendMessage('Hello ' + player.name );
-};
+function greet( player ) {
+ echo( player, 'Hello ' + player.name );
+}
+exports.greet = greet;
```
-At the in-game prompt, type...
+At the in-game prompt, type:
- /js greet(self)
+```javascript
+/js greet(self)
+```
-... to see the greeting. Anything you can do using CraftBukkit's API in Java, you can do using ScriptCraft in Javascript.
+Anything you can do using the Spigot or CanaryMod APIs in Java,
+you can do using ScriptCraft in JavaScript.
# Description
ScriptCraft is a plugin for Minecraft Servers which lets operators,
administrators and plug-in authors customize the game using
-Javascript. ScriptCraft makes it easier to create your own mods. Mods
-can be written in Javscript and can use the full [Bukkit API][bukkit]. The
-ScriptCraft mod also lets you enter javascript commands at the in-game
-prompt. To bring up the in-game prompt press the `/` key then type
-`js ` followed by any javascript statement. E.g. `/js 1+1` will print
-2.
+JavaScript. ScriptCraft makes it easier to create your own mods. Mods
+can be written in Javscript and can use the full [SpigotMC
+API][spigot] or [CanaryMod API][cm]. ScriptCraft works with all of the
+following Minecraft Server software:
+
+* [SpigotMC][spigot] (Recommended)
+* [GlowStone][gs]
+* [CanaryMod][cm]
+
+[spigot]: http://www.spigotmc.org/
+[gs]: http://www.glowstone.net/
+[cm]: http://canarymod.net/
+
+I recommend using SpigotMC because both CanaryMod and CraftBukkit are
+no longer being actively developed. The ScriptCraft mod also lets you
+enter javascript commands at the in-game prompt. To bring up the
+in-game prompt press the `/` key then type `js ` followed by any
+javascript statement. For example: `/js 1 + 1` will print 2.
ScriptCraft also includes many objects and functions to make building
-and modding easier using Javascript. The Javascript `Drone` object
+and modding easier using JavaScript. The JavaScript `Drone` object
bundled with ScriptCraft provides an easy way to build at-scale in
-Minecraft. See the attached [cottage.js][cottage] file for an example
+Minecraft. See the attached [temple.js][temple] file for an example
of how you can use the sample Drone plugin to create new buildings in
Minecraft.
[drone]: https://github.com/walterhiggins/ScriptCraft/tree/master/src/main/javascript/drone/drone.js
-[cottage]: https://github.com/walterhiggins/ScriptCraft/tree/master/src/main/javascript//drone/cottage.js
+[cottage]: https://github.com/walterhiggins/ScriptCraft/tree/master/src/main/js/plugins/drone/contrib/cottage.js
+[temple]: https://github.com/walterhiggins/ScriptCraft/blob/master/src/main/js/plugins/drone/contrib/temple.js
[bukkit]: http://dl.bukkit.org/
# Prerequisites
-You will need to have Java version 6 or 7 installed on your
-machine. Check the version by typing `java -version` at a command
-prompt. You will need to [install Bukkit][ib] on your machine. Bukkit
-is a version of Minecraft (server) that makes it easy to install
-plugins and customize Minecraft. You can [download the CraftBukkit
-server here.][cbdl]
+ScriptCraft is a Minecraft Server Mod which only works with Minecraft
+for Personal computers (Windows, Mac and Linux). It does not work with
+X-BOX, Playstation or WiiU versions of the game. You will need to have
+Java version 7 or later installed. Check the version by typing `java
+-version` at a command prompt.
# Installation
-If you don't want to compile from source, you can [download the
-compiled plugin here][dl] and copy it the craftbukkit's plugins
-directory.
+Before installing ScriptCraft you must first install SpigotMC which is
+a special version of Minecraft Server that makes it easy to customize
+the game.
+
+## Installing and Running SpigotMC
+
+Follow these steps to download and install SpigotMC.
+
+1. Download Spigot's [BuildTools.jar][spigotdl]
+2. Save the BuildTools.jar file to a new directory called spigotmc.
+3. Open a terminal (Mac and Linux) or command prompt (windows) window and type `java -jar BuildTools.jar`. This will kick off a long series of commands to "build" SpigotMC.
+4. When the build is done, there will be a new file beginning with `spigot` and ending in `.jar` in the spigotmc directory. Run this file by typing `java -jar spigot-1.10.2.jar` (it might not be that exact name - you can list files in the directory by typing `dir` (Windows) or `ls` (Mac and Linux).
+5. The server will start up then shut down very shortly afterwards. You'll need to edit a file called `eula.txt` - change `eula=false` to `eula=true` and save the file.
+6. Run the `java -jar spigot-1.10.2.jar` command again - this time the server will start up. Shut it down by typing `stop` at the server prompt.
+
+## Installing ScriptCraft
+
+Follow these steps to download and install ScriptCraft.
+
+1. Download the [scriptcraft.jar][dl] plugin and save it to the `plugins` directory and restart the server by typing `java -jar spigot-1.10.2.jar`.
+2. At the server prompt type `js 1 + 1` and hit enter. The result `2` should be displayed.
+
+Congratulations - you've just installed your Custom Minecraft Server and are ready to begin writing your first mod!
# Post Install
-Once installed, a new js-plugins directory is automatically created in
-the same directory as the plugins folder. All files in the js-plugins
-directory will be automatically loaded when CraftBukkit starts. *Only
-players who are ops can use this plugin.* You can grant a player `op`
-privileges by typing 'op ' at the server console prompt or
-by adding the player's username to the ops.txt file in your
-craftbukkit directory.
+Once installed, a new scriptcraft/plugins directory is automatically
+created. All files in the scriptcraft/plugins directory will be
+automatically loaded when the server starts. *Only players who are
+ops can use this plugin.* You can grant a player `op` privileges by
+typing 'op ' (replacing with your own Minecraft
+user name) at the server console prompt or by adding the player's
+username to the ops.txt file in your server directory.
-Launch CraftBukkit, then launch the Minecraft client and create a new
+Launch the server, then launch the Minecraft client and create a new
server connection. The IP address will be `localhost` . Once you've
-connected to your bukkit server and have entered the game, look at a
-ground-level block and type ...
+connected to your server and have entered the game, look at a
+ground-level block and type:
- /js up().box('35:15', 4, 9, 1)
+ /js up().box( blocks.wool.black, 4, 9, 1 )
-... This will create a black monolith structure 4 blocks wide by 9
+… This will create a black monolith structure 4 blocks wide by 9
blocks high by 1 block long. Take a look at the
src/main/javascript/drone/drone.js file to see what ScriptCraft's
-drone can do. If you're interested in customizing minecraft beyond
-just creating new buildings, take a look at [./homes/homes.js][homes]
-and [./chat/color.js][chatcolor] for examples of how to create a
-javascript plugin for Minecraft.
-
-[ho]: blob/master/src/main/javascript/plugins/homes/homes.js
-[ch]: blob/master/src/main/javascript/plugins/chat/color.js
-[ar]: blob/master/src/main/javascript/plugins/arrows/arrows.js
-[si]: blob/master/src/main/javascript/modules/signs/menu.js
-
-A Javascript mod for minecraft is just a javascript source file (.js)
-located in the craftbukkit/js-plugins directory. All .js files in this
-directory will be automatically loaded when the craftbukkit server
-starts. To get started writing your own mod, first take a look at some
-of the existing mods in the [homes][ho], [chat][ch], [arrows][ar] and
-[signs][si] directories. The chat/color.js mod is probably the
-simplest mod to get started with.
+drone can do.
+
+If you're interested in customizing minecraft beyond just creating new buildings, take a look at [the homes mod][homes] for an example of how to create a more fully-featured JavaScript plugin for Minecraft.
+
+## Your first mod - Howling blocks
+Listed below is a simple mod that will make blocks 'Howl' when they're broken.
+
+``` javascript
+// copy and paste this code to a new file named 'scriptcraft/plugins/howling-blocks.js'
+var sounds = require('sounds');
+function howl(event){
+ sounds.entityWolfHowl( event.block );
+}
+events.blockBreak( howl );
+```
+
+If you're using CanaryMod instead of SpigotMC you can [download the equivalent code](https://gist.github.com/walterhiggins/69cddd15160d803fb096).
+
+A JavaScript mod for minecraft is just a JavaScript source file (.js)
+located in the scriptcraft/plugins directory. All .js files in this
+directory will be automatically loaded when the server starts.
+
+To get started writing your own mod, take a look at some of the
+[examples][examples].
+
+[homes]: src/main/js/plugins/homes/homes.js
+[examples]: src/main/js/plugins/examples/
# Additional information
-Because the Bukkit API is open, all of the Bukkit API is accessible
+Because the SpigotMC API is open, all of the SpigotMC API is accessible
via javascript once the ScriptCraft plugin is loaded. There are a
-couple of useful Java objects exposed via javascript in the Bukkit
-ScriptCraft plugin...
+couple of useful Java objects exposed via javascript in the
+ScriptCraft plugin:
- * `__plugin` - the ScriptCraft Plugin itself. This is a useful
- starting point for accessing other Bukkit objects. The `__plugin`
- object is of type [org.bukkit.plugin.java.JavaPlugin][api] and all
- of its properties and methods are accessible. For example... `js
- __plugin.server.motd` returns the server's message of the day
- (javascript is more concise than the equivalent java code:
- __plugin.getServer().getMotd() ).
+ * `__plugin` – the ScriptCraft Plugin itself. This is a useful
+ starting point for accessing other SpigotMC objects. The `__plugin`
+ object is of type [org.bukkit.plugin.Plugin][api] and all
+ of its properties and methods are accessible. For example: `js
+ __plugin.name` returns the plugin's name
+ (JavaScript is more concise than the equivalent Java code:
+ `__plugin.getName()` ).
- * `server` - The top-level org.bukkit.Server object. See the [Bukkit API docs][bukapi] for reference.
+ * `server` – The top-level org.bukkit.Server object. See the [SpigotMC API docs][spigotapi] for reference.
- * `self` - The player/command-block or server console operator who
+ * `self` – The player/command-block or server console operator who
invoked the `/js` command. Again, this is a good jumping off point for
- diving into the Bukkit API.
+ diving into the SpigotMC API.
-[dl]: http://scriptcraftjs.org/download
-[api]: http://jd.bukkit.org/apidocs/org/bukkit/plugin/java/JavaPlugin.html
-[ib]: http://wiki.bukkit.org/Setting_up_a_server
-[cbdl]: http://dl.bukkit.org/downloads/craftbukkit/
-[bukapi]: http://jd.bukkit.org/apidocs/
+[dl]: http://scriptcraftjs.org/download/latest
+[api]: https://hub.spigotmc.org/javadocs/spigot/
+[ic]: http://canarymod.net/releases
+[spigotdl]: https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar
+[cmapi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/
+[spigotapi]: https://hub.spigotmc.org/javadocs/spigot/
# Contributing
@@ -136,11 +192,12 @@ If you would like to contribute source code and/or documentation changes please
[](http://travis-ci.org/walterhiggins/ScriptCraft)
-# Configuration
+# Bukkit Configuration
+## (You can ignore this if using CanaryMod)
-ScriptCraft is a Bukkit Plugin and uses the Bukkit Configuration
+ScriptCraft works with Bukkit Plugin and uses the Bukkit Configuration
API. On first loading, ScriptCraft will create a config.yml file in
-the plugins/scriptcraft/ directory. This file looks like this...
+the plugins/scriptcraft/ directory. This file looks like this:
extract-js:
plugins: true
@@ -149,31 +206,61 @@ the plugins/scriptcraft/ directory. This file looks like this...
This file allows scriptcraft admins to turn on or off re-unzipping of the `modules`,
`plugins` and `lib` folders when deploying a new version of
-scriptcraft. It's strongly recommended that the `lib` directory always
-be set to true to get the latest core scriptcraft code . The modules
-and plugins directories are optional and not part of scriptcraft core.
+ScriptCraft. It's strongly recommended that the `lib` directory always
+be set to true to get the latest core ScriptCraft code . The modules
+and plugins directories are optional and not part of ScriptCraft core.
# Further Reading
ScriptCraft has [its own website][website] with further information.
- * To get started using ScriptCraft to Learn Javascript, read [The Young Person's Guide to Programming in Minecraft][yp].
+ * To get started using ScriptCraft to Learn JavaScript, read [The Young Person's Guide to Programming in Minecraft][yp].
* The ScriptCraft [API documentation][api].
- * To delve deeper into creating your own minecraft mod for use by others, read [Creating a complete Minecraft Mod in Javascript][mm].
- * Take a look at some [examples][ex]
+ * To delve deeper into creating your own minecraft mod for use by others, read [Creating a complete Minecraft Mod in JavaScript][mm].
+ * Take a look at some [examples][ex].
+ * Buy the Official ScriptCraft Book [A Beginner's Guide to Writing Minecraft Plugins in Javascript][book].
+
+
You can find more information about [ScriptCraft on my blog][blog].
+# Additional Resources
+
+CoderDojo Athenry have some [excellent tutorials][cda] for younger
+programmers who have used [Scratch][scr] and are interested in Modding
+Minecraft using JavaScript. In particular, they have an excellent
+[Scratch - to - JavaScript][sj] tutorial which explains Scratch
+programs and how to do the same thing in JavaScript.
+
+I highly recommend the series of [tutorials provided by CoderDojo Athenry][cda].
+
+Developer Chris Cacciatore has created some interesting tools using ScriptCraft:
+
+ * [A wolf-bot][wb]
+ * [L-Systems (Large-scale fractal structures in Minecraft)][ls]
+
+# Docker
+
+To launch a container with SpigotMC and ScriptCraft you can just do
+
+ docker run -p 25565:25565 -it tclavier/scriptcraft
+
+You can find all files used to build this container in github project: [docker-scriptcraft](https://github.com/tclavier/docker-scriptcraft)
+
+
+[wb]: https://github.com/cacciatc/wolfbot
+[ls]: https://github.com/cacciatc/scriptcraft-lsystems
+
[blog]: http://walterhiggins.net/blog/cat-index-scriptcraft.html
-[buk]: https://github.com/walterhiggins/ScriptCraft/blob/master/bukkit.md
[yp]: docs/YoungPersonsGuideToProgrammingMinecraft.md
[mm]: docs/Anatomy-of-a-Plugin.md
-[api]: https://github.com/walterhiggins/ScriptCraft/blob/master/docs/API-Reference.md
+[api]: docs/API-Reference.md
[website]: http://scriptcraftjs.org/
-[ypgpm]: docs/YoungPersonsGuideToProgrammingMinecraft.md
[cd]: http://coderdojo.com/
[scr]: http://scratch.mit.edu/
[cda]: http://cdathenry.wordpress.com/category/modderdojo/
[ytpl]: http://www.youtube.com/watch?v=DDp20SKm43Y&list=PL4Tw0AgXQZH5BiFHqD2hXyXQi0-qFbGp_
-[ex]: ../../tree/master/src/main/javascript/plugins/examples
+[ex]: src/main/js/plugins/examples
[contrib]: contributing.md
+[sj]: http://cdathenry.wordpress.com/2013/10/12/modderdojo-week-2-moving-from-scratch-to-javascript/
+[book]: http://www.peachpit.com/store/beginners-guide-to-writing-minecraft-plugins-in-javascript-9780133930146
diff --git a/build.properties b/build.properties
index e3d8feed5..f6a95ea6f 100644
--- a/build.properties
+++ b/build.properties
@@ -1,2 +1 @@
-bukkit-version=1.7.9
-scriptcraft-version=2.1.0
+scriptcraft-version=3.2.1
diff --git a/build.xml b/build.xml
index 0c270ab10..48d0e1b5c 100644
--- a/build.xml
+++ b/build.xml
@@ -1,12 +1,43 @@
Builds the scriptcraft.jar file - a plugin for bukkit
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -16,7 +47,7 @@
-
+
@@ -25,83 +56,91 @@
-
-
-
- Retrieving CraftBukkit artifact info
-
-
-
-
-
- Retrieving CraftBukkit jar
-
-
- Creating default ops.txt for your user
-
-
-
-
-
-
- Starting Bukkit with ScriptCraft
-
-
-
-
+
+ debug="true">
+
+
+
+
+
+
+
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- # ScriptCraft API Reference
+ <!--
+IMPORTANT NOTE FOR CONTRIBUTORS
+-------------------------------
+Contributors: This file is generated from comments in javascript source files src/main/js/*
+If you would like to make changes, change the comments in the src/main/js/* files instead.
+-->
+# ScriptCraft API Reference
Walter Higgins
@@ -111,34 +150,36 @@ Walter Higgins
-
+
-
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
-
+
- # The Young Person's Guide to Programming in Minecraft
+ <!--
+ IMPORTANT NOTE FOR CONTRIBUTORS
+ -------------------------------
+ Contributors: This file is generated from source file src/docs/templates/ypgpm.md
+ If you would like to make changes, change file src/docs/templates/ypgpm.md instead
+-->
+# The Young Person's Guide to Programming in Minecraft
@@ -148,7 +189,7 @@ Walter Higgins
-
+
@@ -185,15 +226,19 @@ Walter Higgins
-
+
+
[[version]]
+
+ [[version]]
+
@@ -206,7 +251,7 @@ Walter Higgins
-
+
diff --git a/build/bukkit-to-url.xsl b/build/bukkit-to-url.xsl
deleted file mode 100644
index bb48b3c8b..000000000
--- a/build/bukkit-to-url.xsl
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- http://dl.bukkit.org
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/API-Reference.md b/docs/API-Reference.md
index 5c850102d..fbfd45d54 100644
--- a/docs/API-Reference.md
+++ b/docs/API-Reference.md
@@ -1,3 +1,9 @@
+
# ScriptCraft API Reference
Walter Higgins
@@ -35,6 +41,7 @@ Walter Higgins
* [clearInterval() function](#clearinterval-function)
* [refresh() function](#refresh-function)
* [addUnloadHandler() function](#addunloadhandler-function)
+ * [isOp() function](#isop-function)
* [require - Node.js-style module loading in ScriptCraft](#require---nodejs-style-module-loading-in-scriptcraft)
* [math.js](#mathjs)
* [inc.js](#incjs)
@@ -43,239 +50,440 @@ Walter Higgins
* [module name resolution](#module-name-resolution)
* [events Module](#events-module)
* [events.on() static method](#eventson-static-method)
- * [console global variable](#console-global-variable)
- * [Example](#example)
- * [Using string substitutions](#using-string-substitutions)
- * [Events Helper Module](#events-helper-module)
+ * [Events Helper Module (CanaryMod version)](#events-helper-module-canarymod-version)
* [Usage](#usage)
- * [events.worldUnload()](#eventsworldunload)
- * [events.worldLoad()](#eventsworldload)
- * [events.chunkLoad()](#eventschunkload)
- * [events.chunkPopulate()](#eventschunkpopulate)
- * [events.portalCreate()](#eventsportalcreate)
- * [events.spawnChange()](#eventsspawnchange)
- * [events.chunkUnload()](#eventschunkunload)
- * [events.worldInit()](#eventsworldinit)
- * [events.horseJump()](#eventshorsejump)
- * [events.entityCombust()](#eventsentitycombust)
- * [events.entityRegainHealth()](#eventsentityregainhealth)
- * [events.playerLeashEntity()](#eventsplayerleashentity)
- * [events.pigZap()](#eventspigzap)
- * [events.itemDespawn()](#eventsitemdespawn)
- * [events.entityTarget()](#eventsentitytarget)
+ * [events.minecartActivate()](#eventsminecartactivate)
+ * [events.villagerTradeUnlock()](#eventsvillagertradeunlock)
+ * [events.mobTarget()](#eventsmobtarget)
+ * [events.chickenLayEgg()](#eventschickenlayegg)
+ * [events.potionEffectFinish()](#eventspotioneffectfinish)
+ * [events.entityMove()](#eventsentitymove)
+ * [events.hangingEntityDestroy()](#eventshangingentitydestroy)
+ * [events.vehicleCollision()](#eventsvehiclecollision)
+ * [events.potionEffectApplied()](#eventspotioneffectapplied)
+ * [events.vehicleDestroy()](#eventsvehicledestroy)
+ * [events.vehicleEnter()](#eventsvehicleenter)
+ * [events.damage()](#eventsdamage)
+ * [events.entityMount()](#eventsentitymount)
* [events.slimeSplit()](#eventsslimesplit)
- * [events.entityChangeBlock()](#eventsentitychangeblock)
- * [events.entityPortalEnter()](#eventsentityportalenter)
- * [events.creeperPower()](#eventscreeperpower)
- * [events.entityDeath()](#eventsentitydeath)
+ * [events.endermanDropBlock()](#eventsendermandropblock)
+ * [events.itemTouchGround()](#eventsitemtouchground)
+ * [events.entitySpawn()](#eventsentityspawn)
+ * [events.endermanPickupBlock()](#eventsendermanpickupblock)
+ * [events.vehicleDamage()](#eventsvehicledamage)
+ * [events.entityLightningStruck()](#eventsentitylightningstruck)
+ * [events.entityDespawn()](#eventsentitydespawn)
+ * [events.vehicleMove()](#eventsvehiclemove)
* [events.projectileHit()](#eventsprojectilehit)
+ * [events.entityDeath()](#eventsentitydeath)
* [events.entityTame()](#eventsentitytame)
- * [events.potionSplash()](#eventspotionsplash)
- * [events.expBottle()](#eventsexpbottle)
- * [events.entityExplode()](#eventsentityexplode)
- * [events.creatureSpawn()](#eventscreaturespawn)
- * [events.foodLevelChange()](#eventsfoodlevelchange)
- * [events.entityInteract()](#eventsentityinteract)
- * [events.entityCreatePortal()](#eventsentitycreateportal)
- * [events.sheepRegrowWool()](#eventssheepregrowwool)
- * [events.explosionPrime()](#eventsexplosionprime)
- * [events.entityUnleash()](#eventsentityunleash)
- * [events.entityShootBow()](#eventsentityshootbow)
- * [events.projectileLaunch()](#eventsprojectilelaunch)
- * [events.itemSpawn()](#eventsitemspawn)
- * [events.sheepDyeWool()](#eventssheepdyewool)
- * [events.entityTeleport()](#eventsentityteleport)
+ * [events.vehicleExit()](#eventsvehicleexit)
+ * [events.dimensionSwitch()](#eventsdimensionswitch)
+ * [events.foodLevel()](#eventsfoodlevel)
+ * [events.bookEdit()](#eventsbookedit)
+ * [events.eat()](#eventseat)
+ * [events.playerList()](#eventsplayerlist)
+ * [events.playerIdle()](#eventsplayeridle)
+ * [events.enchant()](#eventsenchant)
+ * [events.playerArmSwing()](#eventsplayerarmswing)
+ * [events.teleport()](#eventsteleport)
+ * [events.anvilUse()](#eventsanviluse)
+ * [events.portalUse()](#eventsportaluse)
+ * [events.foodSaturation()](#eventsfoodsaturation)
+ * [events.connection()](#eventsconnection)
+ * [events.playerRespawned()](#eventsplayerrespawned)
+ * [events.armorBroken()](#eventsarmorbroken)
+ * [events.levelUp()](#eventslevelup)
+ * [events.blockRightClick()](#eventsblockrightclick)
+ * [events.itemDrop()](#eventsitemdrop)
+ * [events.itemFrameRotate()](#eventsitemframerotate)
+ * [events.playerRespawning()](#eventsplayerrespawning)
+ * [events.craft()](#eventscraft)
+ * [events.experience()](#eventsexperience)
+ * [events.signChange()](#eventssignchange)
+ * [events.healthChange()](#eventshealthchange)
+ * [events.disconnection()](#eventsdisconnection)
+ * [events.gameModeChange()](#eventsgamemodechange)
+ * [events.preConnection()](#eventspreconnection)
+ * [events.villagerTrade()](#eventsvillagertrade)
+ * [events.returnFromIdle()](#eventsreturnfromidle)
+ * [events.armorStandModify()](#eventsarmorstandmodify)
+ * [events.slotClick()](#eventsslotclick)
+ * [events.itemFrameSetItem()](#eventsitemframesetitem)
+ * [events.entityRightClick()](#eventsentityrightclick)
+ * [events.foodExhaustion()](#eventsfoodexhaustion)
+ * [events.chat()](#eventschat)
+ * [events.itemPickup()](#eventsitempickup)
+ * [events.bedExit()](#eventsbedexit)
+ * [events.blockPlace()](#eventsblockplace)
+ * [events.heldItemChange()](#eventshelditemchange)
+ * [events.toolBroken()](#eventstoolbroken)
+ * [events.kick()](#eventskick)
+ * [events.playerDeath()](#eventsplayerdeath)
+ * [events.blockLeftClick()](#eventsblockleftclick)
+ * [events.blockDestroy()](#eventsblockdestroy)
+ * [events.bedEnter()](#eventsbedenter)
+ * [events.signShow()](#eventssignshow)
+ * [events.inventory()](#eventsinventory)
+ * [events.playerMove()](#eventsplayermove)
+ * [events.itemUse()](#eventsitemuse)
+ * [events.ban()](#eventsban)
+ * [events.statGained()](#eventsstatgained)
+ * [events.smeltBegin()](#eventssmeltbegin)
+ * [events.treeGrow()](#eventstreegrow)
+ * [events.chunkCreated()](#eventschunkcreated)
+ * [events.liquidDestroy()](#eventsliquiddestroy)
+ * [events.chunkLoaded()](#eventschunkloaded)
+ * [events.pistonRetract()](#eventspistonretract)
+ * [events.smelt()](#eventssmelt)
+ * [events.blockUpdate()](#eventsblockupdate)
+ * [events.portalDestroy()](#eventsportaldestroy)
+ * [events.ignition()](#eventsignition)
+ * [events.redstoneChange()](#eventsredstonechange)
+ * [events.weatherChange()](#eventsweatherchange)
+ * [events.chunkCreation()](#eventschunkcreation)
+ * [events.hopperTransfer()](#eventshoppertransfer)
+ * [events.chunkUnload()](#eventschunkunload)
+ * [events.blockGrow()](#eventsblockgrow)
+ * [events.dispense()](#eventsdispense)
+ * [events.blockDropXp()](#eventsblockdropxp)
+ * [events.fireworkExplode()](#eventsfireworkexplode)
+ * [events.leafDecay()](#eventsleafdecay)
+ * [events.pistonExtend()](#eventspistonextend)
+ * [events.noteBlockPlay()](#eventsnoteblockplay)
+ * [events.lightningStrike()](#eventslightningstrike)
+ * [events.decorate()](#eventsdecorate)
+ * [events.explosion()](#eventsexplosion)
+ * [events.tNTActivate()](#eventstntactivate)
+ * [events.timeChange()](#eventstimechange)
+ * [events.flow()](#eventsflow)
+ * [events.portalCreate()](#eventsportalcreate)
+ * [events.blockPhysics()](#eventsblockphysics)
+ * [events.playerCommand()](#eventsplayercommand)
+ * [events.consoleCommand()](#eventsconsolecommand)
+ * [events.commandBlockCommand()](#eventscommandblockcommand)
+ * [events.loadWorld()](#eventsloadworld)
+ * [events.permissionCheck()](#eventspermissioncheck)
+ * [events.serverGuiStart()](#eventsserverguistart)
+ * [events.unloadWorld()](#eventsunloadworld)
+ * [events.pluginDisable()](#eventsplugindisable)
+ * [events.pluginEnable()](#eventspluginenable)
+ * [events.serverTick()](#eventsservertick)
+ * [events.serverListPing()](#eventsserverlistping)
+ * [events.serverShutdown()](#eventsservershutdown)
+ * [Events Helper Module (SpigotMC version)](#events-helper-module-spigotmc-version)
+ * [Usage](#usage-1)
* [events.blockFade()](#eventsblockfade)
- * [events.blockDamage()](#eventsblockdamage)
+ * [events.blockFromTo()](#eventsblockfromto)
+ * [events.notePlay()](#eventsnoteplay)
+ * [events.blockPlace()](#eventsblockplace-1)
+ * [events.blockGrow()](#eventsblockgrow-1)
+ * [events.entityBlockForm()](#eventsentityblockform)
* [events.blockPistonExtend()](#eventsblockpistonextend)
- * [events.blockExp()](#eventsblockexp)
- * [events.blockGrow()](#eventsblockgrow)
* [events.blockPistonRetract()](#eventsblockpistonretract)
+ * [events.blockSpread()](#eventsblockspread)
+ * [events.blockBurn()](#eventsblockburn)
+ * [events.blockDamage()](#eventsblockdamage)
+ * [events.leavesDecay()](#eventsleavesdecay)
* [events.blockDispense()](#eventsblockdispense)
- * [events.paintingPlace()](#eventspaintingplace)
- * [events.lightningStrike()](#eventslightningstrike)
- * [events.vehicleEnter()](#eventsvehicleenter)
- * [events.vehicleMove()](#eventsvehiclemove)
- * [events.vehicleCreate()](#eventsvehiclecreate)
- * [events.asyncPlayerPreLogin()](#eventsasyncplayerprelogin)
- * [events.playerPreLogin()](#eventsplayerprelogin)
- * [events.inventoryPickupItem()](#eventsinventorypickupitem)
- * [events.inventoryMoveItem()](#eventsinventorymoveitem)
- * [events.furnaceBurn()](#eventsfurnaceburn)
- * [events.inventory()](#eventsinventory)
+ * [events.blockForm()](#eventsblockform)
+ * [events.blockMultiPlace()](#eventsblockmultiplace)
+ * [events.blockIgnite()](#eventsblockignite)
+ * [events.blockPhysics()](#eventsblockphysics-1)
+ * [events.blockRedstone()](#eventsblockredstone)
+ * [events.cauldronLevelChange()](#eventscauldronlevelchange)
+ * [events.blockCanBuild()](#eventsblockcanbuild)
+ * [events.signChange()](#eventssignchange-1)
+ * [events.blockExp()](#eventsblockexp)
+ * [events.blockExplode()](#eventsblockexplode)
+ * [events.blockBreak()](#eventsblockbreak)
+ * [events.prepareAnvil()](#eventsprepareanvil)
* [events.brew()](#eventsbrew)
- * [events.furnaceSmelt()](#eventsfurnacesmelt)
* [events.inventoryClose()](#eventsinventoryclose)
* [events.inventoryDrag()](#eventsinventorydrag)
+ * [events.inventoryCreative()](#eventsinventorycreative)
+ * [events.prepareItemCraft()](#eventsprepareitemcraft)
+ * [events.craftItem()](#eventscraftitem)
* [events.inventoryClick()](#eventsinventoryclick)
+ * [events.brewingStandFuel()](#eventsbrewingstandfuel)
+ * [events.inventoryPickupItem()](#eventsinventorypickupitem)
+ * [events.furnaceExtract()](#eventsfurnaceextract)
+ * [events.inventory()](#eventsinventory-1)
+ * [events.furnaceBurn()](#eventsfurnaceburn)
+ * [events.furnaceSmelt()](#eventsfurnacesmelt)
+ * [events.inventoryOpen()](#eventsinventoryopen)
+ * [events.inventoryMoveItem()](#eventsinventorymoveitem)
* [events.hangingPlace()](#eventshangingplace)
* [events.hangingBreak()](#eventshangingbreak)
- * [events.worldSave()](#eventsworldsave)
- * [events.structureGrow()](#eventsstructuregrow)
- * [events.entityDamage()](#eventsentitydamage)
+ * [events.hangingBreakByEntity()](#eventshangingbreakbyentity)
+ * [events.entityToggleGlide()](#eventsentitytoggleglide)
+ * [events.itemSpawn()](#eventsitemspawn)
+ * [events.spawnerSpawn()](#eventsspawnerspawn)
+ * [events.foodLevelChange()](#eventsfoodlevelchange)
+ * [events.lingeringPotionSplash()](#eventslingeringpotionsplash)
+ * [events.entityShootBow()](#eventsentityshootbow)
+ * [events.entityTargetLivingEntity()](#eventsentitytargetlivingentity)
+ * [events.entityCombust()](#eventsentitycombust)
+ * [events.entityBreed()](#eventsentitybreed)
+ * [events.expBottle()](#eventsexpbottle)
+ * [events.slimeSplit()](#eventsslimesplit-1)
+ * [events.entityDeath()](#eventsentitydeath-1)
+ * [events.entityAirChange()](#eventsentityairchange)
+ * [events.projectileLaunch()](#eventsprojectilelaunch)
* [events.entityPortal()](#eventsentityportal)
+ * [events.horseJump()](#eventshorsejump)
+ * [events.fireworkExplode()](#eventsfireworkexplode-1)
+ * [events.entityExplode()](#eventsentityexplode)
+ * [events.itemMerge()](#eventsitemmerge)
+ * [events.entitySpawn()](#eventsentityspawn-1)
+ * [events.projectileHit()](#eventsprojectilehit-1)
+ * [events.creatureSpawn()](#eventscreaturespawn)
* [events.entityPortalExit()](#eventsentityportalexit)
- * [events.signChange()](#eventssignchange)
- * [events.leavesDecay()](#eventsleavesdecay)
- * [events.blockRedstone()](#eventsblockredstone)
- * [events.blockCanBuild()](#eventsblockcanbuild)
- * [events.blockBurn()](#eventsblockburn)
- * [events.blockPhysics()](#eventsblockphysics)
- * [events.blockIgnite()](#eventsblockignite)
- * [events.notePlay()](#eventsnoteplay)
- * [events.blockPlace()](#eventsblockplace)
- * [events.blockFromTo()](#eventsblockfromto)
- * [events.blockForm()](#eventsblockform)
- * [events.blockSpread()](#eventsblockspread)
- * [events.enchantItem()](#eventsenchantitem)
- * [events.prepareItemEnchant()](#eventsprepareitemenchant)
- * [events.paintingBreak()](#eventspaintingbreak)
- * [events.weatherChange()](#eventsweatherchange)
- * [events.thunderChange()](#eventsthunderchange)
- * [events.vehicleEntityCollision()](#eventsvehicleentitycollision)
- * [events.vehicleBlockCollision()](#eventsvehicleblockcollision)
- * [events.vehicleExit()](#eventsvehicleexit)
- * [events.vehicleUpdate()](#eventsvehicleupdate)
- * [events.vehicleDamage()](#eventsvehicledamage)
- * [events.vehicleDestroy()](#eventsvehicledestroy)
- * [events.playerExpChange()](#eventsplayerexpchange)
- * [events.playerRespawn()](#eventsplayerrespawn)
- * [events.playerCommandPreprocess()](#eventsplayercommandpreprocess)
- * [events.playerPickupItem()](#eventsplayerpickupitem)
- * [events.playerInventory()](#eventsplayerinventory)
- * [events.playerFish()](#eventsplayerfish)
- * [events.playerBedEnter()](#eventsplayerbedenter)
- * [events.playerLogin()](#eventsplayerlogin)
- * [events.playerDropItem()](#eventsplayerdropitem)
- * [events.playerLevelChange()](#eventsplayerlevelchange)
+ * [events.entityTame()](#eventsentitytame-1)
+ * [events.entityPortalEnter()](#eventsentityportalenter)
+ * [events.playerLeashEntity()](#eventsplayerleashentity)
+ * [events.entityDamage()](#eventsentitydamage)
+ * [events.pigZap()](#eventspigzap)
+ * [events.entityCombustByEntity()](#eventsentitycombustbyentity)
+ * [events.entityChangeBlock()](#eventsentitychangeblock)
+ * [events.areaEffectCloudApply()](#eventsareaeffectcloudapply)
+ * [events.creeperPower()](#eventscreeperpower)
+ * [events.sheepDyeWool()](#eventssheepdyewool)
+ * [events.playerDeath()](#eventsplayerdeath-1)
+ * [events.villagerReplenishTrade()](#eventsvillagerreplenishtrade)
+ * [events.entityCombustByBlock()](#eventsentitycombustbyblock)
+ * [events.entityResurrect()](#eventsentityresurrect)
+ * [events.villagerAcquireTrade()](#eventsvillageracquiretrade)
+ * [events.enderDragonChangePhase()](#eventsenderdragonchangephase)
+ * [events.entityCreatePortal()](#eventsentitycreateportal)
+ * [events.sheepRegrowWool()](#eventssheepregrowwool)
+ * [events.entityRegainHealth()](#eventsentityregainhealth)
+ * [events.entityInteract()](#eventsentityinteract)
+ * [events.potionSplash()](#eventspotionsplash)
+ * [events.entityTarget()](#eventsentitytarget)
+ * [events.entityBreakDoor()](#eventsentitybreakdoor)
+ * [events.entityUnleash()](#eventsentityunleash)
+ * [events.entityDamageByBlock()](#eventsentitydamagebyblock)
+ * [events.entityTeleport()](#eventsentityteleport)
+ * [events.itemDespawn()](#eventsitemdespawn)
+ * [events.explosionPrime()](#eventsexplosionprime)
+ * [events.entityDamageByEntity()](#eventsentitydamagebyentity)
+ * [events.portalCreate()](#eventsportalcreate-1)
+ * [events.worldSave()](#eventsworldsave)
+ * [events.worldLoad()](#eventsworldload)
+ * [events.chunkLoad()](#eventschunkload)
+ * [events.chunkPopulate()](#eventschunkpopulate)
+ * [events.chunkUnload()](#eventschunkunload-1)
+ * [events.structureGrow()](#eventsstructuregrow)
+ * [events.worldInit()](#eventsworldinit)
+ * [events.spawnChange()](#eventsspawnchange)
+ * [events.worldUnload()](#eventsworldunload)
+ * [events.playerShearEntity()](#eventsplayershearentity)
* [events.playerVelocity()](#eventsplayervelocity)
+ * [events.playerBucketFill()](#eventsplayerbucketfill)
+ * [events.playerFish()](#eventsplayerfish)
+ * [events.playerGameModeChange()](#eventsplayergamemodechange)
+ * [events.playerCommandPreprocess()](#eventsplayercommandpreprocess)
* [events.playerInteract()](#eventsplayerinteract)
+ * [events.playerInteractAtEntity()](#eventsplayerinteractatentity)
+ * [events.playerPortal()](#eventsplayerportal)
+ * [events.playerPreLogin()](#eventsplayerprelogin)
+ * [events.playerMove()](#eventsplayermove-1)
* [events.playerQuit()](#eventsplayerquit)
- * [events.playerChatTabComplete()](#eventsplayerchattabcomplete)
- * [events.playerEggThrow()](#eventsplayereggthrow)
- * [events.playerChat()](#eventsplayerchat)
- * [events.playerAchievementAwarded()](#eventsplayerachievementawarded)
+ * [events.playerToggleSneak()](#eventsplayertogglesneak)
+ * [events.playerPickupArrow()](#eventsplayerpickuparrow)
* [events.playerBedLeave()](#eventsplayerbedleave)
- * [events.playerChannel()](#eventsplayerchannel)
- * [events.playerStatisticIncrement()](#eventsplayerstatisticincrement)
- * [events.playerToggleSprint()](#eventsplayertogglesprint)
- * [events.playerInteractEntity()](#eventsplayerinteractentity)
+ * [events.playerChangedMainHand()](#eventsplayerchangedmainhand)
* [events.playerEditBook()](#eventsplayereditbook)
- * [events.playerKick()](#eventsplayerkick)
- * [events.playerItemHeld()](#eventsplayeritemheld)
- * [events.playerItemConsume()](#eventsplayeritemconsume)
- * [events.playerGameModeChange()](#eventsplayergamemodechange)
- * [events.playerItemBreak()](#eventsplayeritembreak)
- * [events.playerToggleFlight()](#eventsplayertoggleflight)
* [events.playerAnimation()](#eventsplayeranimation)
+ * [events.playerResourcePackStatus()](#eventsplayerresourcepackstatus)
+ * [events.playerItemDamage()](#eventsplayeritemdamage)
+ * [events.playerBucketEmpty()](#eventsplayerbucketempty)
+ * [events.playerExpChange()](#eventsplayerexpchange)
* [events.asyncPlayerChat()](#eventsasyncplayerchat)
- * [events.playerMove()](#eventsplayermove)
+ * [events.playerItemBreak()](#eventsplayeritembreak)
+ * [events.playerUnleashEntity()](#eventsplayerunleashentity)
+ * [events.playerRespawn()](#eventsplayerrespawn)
+ * [events.playerDropItem()](#eventsplayerdropitem)
+ * [events.playerItemHeld()](#eventsplayeritemheld)
* [events.playerTeleport()](#eventsplayerteleport)
- * [events.playerBucketFill()](#eventsplayerbucketfill)
- * [events.playerJoin()](#eventsplayerjoin)
- * [events.playerShearEntity()](#eventsplayershearentity)
- * [events.playerToggleSneak()](#eventsplayertogglesneak)
+ * [events.playerInteractEntity()](#eventsplayerinteractentity)
+ * [events.playerEggThrow()](#eventsplayereggthrow)
+ * [events.playerChat()](#eventsplayerchat)
+ * [events.playerRegisterChannel()](#eventsplayerregisterchannel)
+ * [events.playerSwapHandItems()](#eventsplayerswaphanditems)
* [events.playerChangedWorld()](#eventsplayerchangedworld)
+ * [events.playerLevelChange()](#eventsplayerlevelchange)
+ * [events.playerPickupItem()](#eventsplayerpickupitem)
+ * [events.asyncPlayerPreLogin()](#eventsasyncplayerprelogin)
+ * [events.playerUnregisterChannel()](#eventsplayerunregisterchannel)
+ * [events.playerStatisticIncrement()](#eventsplayerstatisticincrement)
+ * [events.playerBedEnter()](#eventsplayerbedenter)
+ * [events.playerKick()](#eventsplayerkick)
+ * [events.playerToggleSprint()](#eventsplayertogglesprint)
+ * [events.playerAchievementAwarded()](#eventsplayerachievementawarded)
+ * [events.playerJoin()](#eventsplayerjoin)
+ * [events.playerToggleFlight()](#eventsplayertoggleflight)
+ * [events.playerArmorStandManipulate()](#eventsplayerarmorstandmanipulate)
+ * [events.playerLogin()](#eventsplayerlogin)
+ * [events.playerItemConsume()](#eventsplayeritemconsume)
+ * [events.playerChatTabComplete()](#eventsplayerchattabcomplete)
+ * [events.weatherChange()](#eventsweatherchange-1)
+ * [events.thunderChange()](#eventsthunderchange)
+ * [events.lightningStrike()](#eventslightningstrike-1)
+ * [events.vehicleMove()](#eventsvehiclemove-1)
+ * [events.vehicleDamage()](#eventsvehicledamage-1)
+ * [events.vehicleDestroy()](#eventsvehicledestroy-1)
+ * [events.vehicleUpdate()](#eventsvehicleupdate)
+ * [events.vehicleCreate()](#eventsvehiclecreate)
+ * [events.vehicleEntityCollision()](#eventsvehicleentitycollision)
+ * [events.vehicleBlockCollision()](#eventsvehicleblockcollision)
+ * [events.vehicleEnter()](#eventsvehicleenter-1)
+ * [events.vehicleExit()](#eventsvehicleexit-1)
+ * [events.prepareItemEnchant()](#eventsprepareitemenchant)
+ * [events.enchantItem()](#eventsenchantitem)
+ * [events.pluginDisable()](#eventsplugindisable-1)
+ * [events.serverListPing()](#eventsserverlistping-1)
+ * [events.serviceRegister()](#eventsserviceregister)
* [events.serverCommand()](#eventsservercommand)
* [events.remoteServerCommand()](#eventsremoteservercommand)
- * [events.mapInitialize()](#eventsmapinitialize)
- * [events.serviceRegister()](#eventsserviceregister)
- * [events.serverListPing()](#eventsserverlistping)
+ * [events.pluginEnable()](#eventspluginenable-1)
* [events.serviceUnregister()](#eventsserviceunregister)
- * [events.prepareItemCraft()](#eventsprepareitemcraft)
- * [events.inventoryOpen()](#eventsinventoryopen)
- * [events.playerBucketEmpty()](#eventsplayerbucketempty)
- * [events.playerPortal()](#eventsplayerportal)
- * [events.pluginDisable()](#eventsplugindisable)
- * [events.pluginEnable()](#eventspluginenable)
- * [Blocks Module](#blocks-module)
- * [Examples](#examples)
- * [Fireworks Module](#fireworks-module)
- * [Examples](#examples-1)
- * [Asynchronous Input Module](#asynchronous-input-module)
- * [Http Module](#http-module)
- * [http.request() function](#httprequest-function)
- * [sc-mqtt module](#sc-mqtt-module)
- * [Usage](#usage-1)
- * [Signs Module](#signs-module)
- * [signs.menu() function](#signsmenu-function)
- * [signs.getTargetedBy() function](#signsgettargetedby-function)
- * [Sounds Module](#sounds-module)
- * [Usage:](#usage-2)
- * [Utilities Module](#utilities-module)
- * [utils.player() function](#utilsplayer-function)
- * [utils.locationToJSON() function](#utilslocationtojson-function)
- * [utils.locationToString() function](#utilslocationtostring-function)
- * [utils.locationFromJSON() function](#utilslocationfromjson-function)
- * [utils.getPlayerPos() function](#utilsgetplayerpos-function)
- * [utils.getMousePos() function](#utilsgetmousepos-function)
- * [utils.foreach() function](#utilsforeach-function)
- * [utils.nicely() function](#utilsnicely-function)
- * [utils.at() function](#utilsat-function)
- * [utils.find() function](#utilsfind-function)
- * [utils.serverAddress() function](#utilsserveraddress-function)
- * [utils.watchFile() function](#utilswatchfile-function)
- * [utils.unwatchFile() function](#utilsunwatchfile-function)
+ * [events.tabComplete()](#eventstabcomplete)
+ * [events.mapInitialize()](#eventsmapinitialize)
+ * [console global variable](#console-global-variable)
+ * [Example](#example)
+ * [Using string substitutions](#using-string-substitutions)
* [Drone Plugin](#drone-plugin)
- * [TLDNR; (Just read this if you're impatient)](#tldnr-just-read-this-if-youre-impatient)
* [Constructing a Drone Object](#constructing-a-drone-object)
* [Drone.box() method](#dronebox-method)
* [Drone.box0() method](#dronebox0-method)
* [Drone.boxa() method](#droneboxa-method)
- * [Drone Movement](#drone-movement)
- * [Drone Positional Info](#drone-positional-info)
- * [Drone Markers](#drone-markers)
- * [Drone.prism() method](#droneprism-method)
- * [Drone.prism0() method](#droneprism0-method)
- * [Drone.cylinder() method](#dronecylinder-method)
- * [Drone.cylinder0() method](#dronecylinder0-method)
- * [Drone.arc() method](#dronearc-method)
- * [Drone.door() method](#dronedoor-method)
- * [Drone.door_iron() method](#dronedoor_iron-method)
- * [Drone.door2() method](#dronedoor2-method)
- * [Drone.door2_iron() method](#dronedoor2_iron-method)
- * [Drone.sign() method](#dronesign-method)
- * [Drone Trees methods](#drone-trees-methods)
- * [Drone.garden() method](#dronegarden-method)
- * [Drone.rand() method](#dronerand-method)
- * [Copy & Paste using Drone](#copy--paste-using-drone)
- * [Drone.copy() method](#dronecopy-method)
- * [Drone.paste() method](#dronepaste-method)
* [Chaining](#chaining)
* [Drone Properties](#drone-properties)
* [Extending Drone](#extending-drone)
* [Drone.extend() static method](#droneextend-static-method)
* [Drone Constants](#drone-constants)
* [Drone.times() Method](#dronetimes-method)
+ * [Drone.arc() method](#dronearc-method)
+ * [Drone.bed() method](#dronebed-method)
* [Drone.blocktype() method](#droneblocktype-method)
- * [Drone.rainbow() method](#dronerainbow-method)
+ * [Copy & Paste using Drone](#copy--paste-using-drone)
+ * [Drone.copy() method](#dronecopy-method)
+ * [Drone.paste() method](#dronepaste-method)
+ * [Drone.cylinder() method](#dronecylinder-method)
+ * [Drone.cylinder0() method](#dronecylinder0-method)
+ * [Drone.door() method](#dronedoor-method)
+ * [Drone.door_iron() method](#dronedoor_iron-method)
+ * [Drone.door2() method](#dronedoor2-method)
+ * [Drone.door2_iron() method](#dronedoor2_iron-method)
+ * [Drone.firework() method](#dronefirework-method)
+ * [Drone.garden() method](#dronegarden-method)
+ * [Drone.ladder() method](#droneladder-method)
+ * [Drone Movement](#drone-movement)
+ * [Drone Positional Info](#drone-positional-info)
+ * [Drone Markers](#drone-markers)
+ * [Drone.prism() method](#droneprism-method)
+ * [Drone.prism0() method](#droneprism0-method)
+ * [Drone.rand() method](#dronerand-method)
+ * [Drone.wallsign() method](#dronewallsign-method)
+ * [Drone.signpost() method](#dronesignpost-method)
+ * [Drone.sign() method](#dronesign-method)
* [Drone.sphere() method](#dronesphere-method)
* [Drone.sphere0() method](#dronesphere0-method)
* [Drone.hemisphere() method](#dronehemisphere-method)
* [Drone.hemisphere0() method](#dronehemisphere0-method)
+ * [Drone.stairs() function](#dronestairs-function)
+ * [Drone Trees methods](#drone-trees-methods)
+ * [Drone.castle() method](#dronecastle-method)
+ * [Drone.chessboard() method](#dronechessboard-method)
+ * [Drone.cottage() method](#dronecottage-method)
+ * [Drone.cottage_road() method](#dronecottage_road-method)
+ * [Drone.dancefloor() method](#dronedancefloor-method)
+ * [Drone.fort() method](#dronefort-method)
+ * [Drone.hangtorch() method](#dronehangtorch-method)
+ * [Drone.lcdclock() method.](#dronelcdclock-method)
+ * [Drone.logojs() method](#dronelogojs-method)
+ * [Drone.maze() method](#dronemaze-method)
+ * [Drone.rainbow() method](#dronerainbow-method)
* [Drone.spiral_stairs() method](#dronespiral_stairs-method)
+ * [Drone.temple() method](#dronetemple-method)
+ * [The at Module](#the-at-module)
+ * [at() function](#at-function)
+ * [Blocks Module](#blocks-module)
+ * [Examples](#examples)
+ * [Fireworks Module](#fireworks-module)
+ * [Examples](#examples-1)
+ * [Classroom Plugin](#classroom-plugin)
+ * [jsp classroom command](#jsp-classroom-command)
+ * [classroom.allowScripting() function](#classroomallowscripting-function)
+ * [Inventory Module](#inventory-module)
+ * [Usage](#usage-2)
+ * [Asynchronous Input Module](#asynchronous-input-module)
+ * [Lightning module](#lightning-module)
+ * [Usage](#usage-3)
+ * [The recipes module](#the-recipes-module)
+ * [Example](#example-1)
+ * [Http Module](#http-module)
+ * [http.request() function](#httprequest-function)
+ * [sc-mqtt module](#sc-mqtt-module)
+ * [Usage](#usage-4)
+ * [Signs Module](#signs-module)
+ * [signs.menu() function](#signsmenu-function)
+ * [signs.getTargetedBy() function](#signsgettargetedby-function)
+ * [The slash Module](#the-slash-module)
+ * [The slash() function](#the-slash-function)
+ * [Sounds Module](#sounds-module)
+ * [Usage (Bukkit) :](#usage-bukkit-)
+ * [Spawn Module](#spawn-module)
+ * [Parameters](#parameters)
+ * [Example](#example-2)
+ * [Teleport Module](#teleport-module)
+ * [Parameters](#parameters-1)
+ * [Example](#example-3)
+ * [Utilities Module](#utilities-module)
+ * [utils.player() function](#utilsplayer-function)
+ * [utils.world( worldName ) function](#utilsworld-worldname--function)
+ * [utils.blockAt( Location ) function](#utilsblockat-location--function)
+ * [utils.locationToJSON() function](#utilslocationtojson-function)
+ * [utils.locationToString() function](#utilslocationtostring-function)
+ * [utils.locationFromJSON() function](#utilslocationfromjson-function)
+ * [utils.getPlayerPos() function](#utilsgetplayerpos-function)
+ * [utils.getMousePos() function](#utilsgetmousepos-function)
+ * [utils.foreach() function](#utilsforeach-function)
+ * [utils.nicely() function](#utilsnicely-function)
+ * [utils.time( world ) function](#utilstime-world--function)
+ * [utils.time24( world ) function](#utilstime24-world--function)
+ * [utils.find() function](#utilsfind-function)
+ * [utils.serverAddress() function](#utilsserveraddress-function)
+ * [utils.array() function](#utilsarray-function)
+ * [utils.players() function](#utilsplayers-function)
+ * [utils.playerNames() function](#utilsplayernames-function)
+ * [utils.stat() function](#utilsstat-function)
+ * [The watcher Module](#the-watcher-module)
+ * [watcher.watchFile() function](#watcherwatchfile-function)
+ * [watcher.watchDir() function](#watcherwatchdir-function)
+ * [watcher.unwatchFile() function](#watcherunwatchfile-function)
+ * [watcher.unwatchDir() function](#watcherunwatchdir-function)
* [Example Plugin #1 - A simple extension to Minecraft.](#example-plugin-1---a-simple-extension-to-minecraft)
- * [Usage:](#usage-3)
+ * [Usage:](#usage-5)
* [Example Plugin #2 - Making extensions available for all players.](#example-plugin-2---making-extensions-available-for-all-players)
- * [Usage:](#usage-4)
+ * [Usage:](#usage-6)
* [Example Plugin #3 - Limiting use of commands to operators only.](#example-plugin-3---limiting-use-of-commands-to-operators-only)
- * [Usage:](#usage-5)
+ * [Usage:](#usage-7)
* [Example Plugin #4 - Using parameters in commands.](#example-plugin-4---using-parameters-in-commands)
- * [Usage:](#usage-6)
+ * [Usage:](#usage-8)
* [Example Plugin #5 - Re-use - Using your own and others modules.](#example-plugin-5---re-use---using-your-own-and-others-modules)
- * [Usage:](#usage-7)
+ * [Usage:](#usage-9)
* [Example Plugin #6 - Re-use - Using 'utils' to get Player objects.](#example-plugin-6---re-use---using-utils-to-get-player-objects)
- * [Usage:](#usage-8)
+ * [Usage:](#usage-10)
* [Example Plugin #7 - Listening for events, Greet players when they join the game.](#example-plugin-7---listening-for-events-greet-players-when-they-join-the-game)
* [Arrows Plugin](#arrows-plugin)
- * [Usage:](#usage-9)
+ * [Usage:](#usage-11)
* [Spawn Plugin](#spawn-plugin)
- * [Usage](#usage-10)
+ * [Usage](#usage-12)
* [alias Plugin](#alias-plugin)
* [Examples](#examples-2)
- * [chat Plugin](#chat-plugin)
- * [Classroom Plugin](#classroom-plugin)
- * [classroom.allowScripting() function](#classroomallowscripting-function)
* [Commando Plugin](#commando-plugin)
* [Description](#description)
* [Example hi-command.js](#example-hi-commandjs)
@@ -287,1283 +495,2914 @@ Walter Higgins
* [Administration options](#administration-options)
* [NumberGuess mini-game:](#numberguess-mini-game)
* [Description](#description-1)
- * [Example](#example-1)
- * [SnowballFight mini-game](#snowballfight-mini-game)
- * [Description](#description-2)
+ * [Example](#example-4)
* [Cow Clicker Mini-Game](#cow-clicker-mini-game)
* [How to Play](#how-to-play)
* [Rules](#rules)
* [Gameplay Mechanics](#gameplay-mechanics)
+ * [Items module (SpigotMC version)](#items-module-spigotmc-version)
+ * [Usage](#usage-13)
+ * [Items module (CanaryMod version)](#items-module-canarymod-version)
+ * [Usage](#usage-14)
+ * [Entities module](#entities-module)
+ * [Usage](#usage-15)
+
+## Modules in Scriptcraft
+
+ScriptCraft has a simple module loading system. In ScriptCraft, files
+and modules are in one-to-one correspondence. As an example, foo.js
+loads the module circle.js in the same directory.
+*ScriptCraft now uses the same module system as Node.js - see [Node.js Modules][njsmod] for more details.*
+
+[njsmod]: http://nodejs.org/api/modules.html
+
+The contents of foo.js:
+
+```javascript
+var circle = require('./circle.js');
+console.log( 'The area of a circle of radius 4 is '
+ + circle.area(4));
+```
+
+The contents of circle.js:
+
+```javascript
+var PI = Math.PI;
+exports.area = function (r) {
+ return PI * r * r;
+};
+exports.circumference = function (r) {
+ return 2 * PI * r;
+};
+```
+
+The module circle.js has exported the functions area() and
+circumference(). To add functions and objects to the root of your
+module, you can add them to the special exports object.
+
+Variables local to the module will be private, as though the module
+was wrapped in a function. In this example the variable PI is private
+to circle.js.
+
+If you want the root of your module's export to be a function (such as
+a constructor) or if you want to export a complete object in one
+assignment instead of building it one property at a time, assign it to
+module.exports instead of exports.
+
+## Module Loading
+
+When the ScriptCraft Java plugin is first installed, a new
+`scriptcraft` subdirectory is created. If your minecraft server
+directory is called 'mcserver' then the new subdirectories will be ...
+
+ * mcserver/scriptcraft/
+ * mcserver/scriptcraft/plugins
+ * mcserver/scriptcraft/modules
+ * mcserver/scriptcraft/lib
+
+... The `plugins`, `modules` and `lib` directories each serve a different purpose.
+
+### The plugins directory
+
+At server startup the ScriptCraft Java plugin is loaded and begins
+automatically loading and executing all of the modules (javascript
+files with the extension `.js`) it finds in the `scriptcraft/plugins`
+directory. All modules in the plugins directory are automatically
+loaded into the `global` namespace. What this means is that anything a
+module in the `plugins` directory exports becomes a global
+variable. For example, if you have a module greeting.js in the plugins
+directory....
+
+```javascript
+exports.greet = function(player) {
+ echo(player, 'Hello ' + player.name);
+};
+```
+
+... then `greet` becomes a global function and can be used at the
+in-game (or server) command prompt like so...
+
+ /js greet(self)
+
+... This differs from how modules (in NodeJS and commonJS
+environments) normally work. If you want your module to be exported
+globally, put it in the `plugins` directory. If you don't want your
+module to be exported globally but only want it to be used by other
+modules, then put it in the `modules` directory instead. If you've
+used previous versions of ScriptCraft and have put your custom
+javascript modules in the `js-plugins` directory, then put them in the
+`scriptcraft/plugins` directory. To summarise, modules in this directory are ...
+
+ * Automatically loaded and run at server startup.
+ * Anything exported by modules becomes a global variable.
+
+### The modules directory
+
+The module directory is where you should place your modules if you
+don't want to export globally. In javascript, it's considered best
+practice not to have too many global variables, so if you want to
+develop modules for others to use, or want to develop more complex
+mods then your modules should be placed in the `modules` directory.
+*Modules in the `modules` directory are not automatically loaded at
+startup*, instead, they are loaded and used by other modules/plugins
+using the standard `require()` function. This is the key difference
+between modules in the `plugins` directory and modules in the
+`modules` directory. Modules in the `plugins` directory are
+automatically loaded and exported in to the global namespace at server
+startup, modules in the `modules` directory are not.
+
+### The lib directory
+
+Modules in the `lib` directory are for use by ScriptCraft and some
+core functions for use by module and plugin developers are also
+provided. The `lib` directory is for internal use by ScriptCraft.
+Modules in this directory are not automatically loaded nor are they
+globally exported.
+
+### plugins sub-directories
+
+As of December 24 2013, the `scriptcraft/plugins` directory has the following sub-directories...
+
+ * drone - Contains the drone module and drone extensions. Drone was the first scriptcraft module.
+ * mini-games - Contains mini-games
+ * arrows - The arrows module - Changes the behaviour of Arrows: Explosive, Fireworks, Teleportation etc.
+ * signs - The signs module (includes example signs) - create interactive signs.
+ * chat - The chat plugin/module
+ * alias - The alias plugin/module - for creating custom aliases for commonly-used commands.
+ * home - The home module - for setting homes and visiting other homes.
+
+## Global variables
+
+There are a couple of special javascript variables available in ScriptCraft...
+
+### __plugin variable
+The ScriptCraft JavaPlugin object.
+
+### server variable
+The Minecraft Server object
+
+### self variable
+The current player. (Note - this value should not be used in multi-threaded scripts or event-handling code - it's not thread-safe). This variable is only safe to use at the in-game prompt and should *never* be used in modules. For example you can use it here...
+
+ /js console.log(self.name)
+
+... but not in any javascript module you create yourself or in any
+event handling code. `self` is a temporary short-lived variable which
+only exists in the context of the in-game or server command prompts.
+
+### config variable
+ScriptCraft configuration - this object is loaded and saved at startup/shutdown.
+
+### events variable
+The events object is used to add new event handlers to Minecraft.
+
+## Module variables
+The following variables are available only within the context of Modules. (not available at in-game prompt).
+
+### __filename variable
+The current file - this variable is only relevant from within the context of a Javascript module.
+
+### __dirname variable
+The current directory - this variable is only relevant from within the context of a Javascript module.
+
+## Global functions
+
+ScripCraft provides some global functions which can be used by all plugins/modules...
+
+### echo function
+
+The `echo()` function displays a message on the in-game screen.
+
+#### Example
+
+ /js echo( self, 'Hello World')
+
+For programmers familiar with Javascript web programming, an `alert`
+function is also provided. `alert` works exactly the same as `echo`
+e.g. `alert( self, 'Hello World')`.
+
+### require() function
+
+ScriptCraft's `require()` function is used to load modules. The `require()` function takes a module name as a parameter and will try to load the named module.
+
+#### Parameters
+
+ * modulename - The name of the module to be loaded. Can be one of the following...
+
+ - A relative file path (with or without `.js` suffix)
+ - An absolute file path (with or without `.js` suffix)
+ - A relative directory path (uses node.js rules for directories)
+ - An absolute directory path (uses node.js rules for directories)
+ - A name of the form `'events'` - in which case the `lib` directory and `modules` directories are searched for the module.
+
+#### Return
+
+require() will return the loaded module's exports.
+
+### scload() function
+
+#### No longer recommended for use by Plugin/Module developers (deprecated)
+
+scload() should only be used to load .json data.
+
+#### Parameters
+
+ * filename - The name of the file to load.
+ * warnOnFileNotFound (optional - default: false) - warn if the file was not found.
+
+#### Returns
+
+scload() will return the result of the last statement evaluated in the file.
+
+#### Example
+
+ scload("myFile.js"); // loads a javascript file and evaluates it.
+
+ var myData = scload("myData.json"); // loads a javascript file and evaluates it - eval'd contents are returned.
+
+##### myData.json contents...
+
+ { players: {
+ walterh: {
+ h: ["jsp home {1}"],
+ sunny:["time set 0",
+ "weather clear"]
+ }
+ }
+ }
+
+### scsave() function
+
+The scsave() function saves an in-memory javascript object to a
+specified file. Under the hood, scsave() uses JSON (specifically
+json2.js) to save the object. There will usually be no need to call
+this function directly - If you want to have a javascript object
+automatically loaded at startup and saved on shutdown then use the
+`persist()` module. The `persist()` module uses scsave and scload
+under the hood. Any in-memory object saved using the `scsave()`
+function can later be restored using the `scload()` function.
+
+#### Parameters
+
+ * objectToSave : The object you want to save.
+ * filename : The name of the file you want to save it to.
+
+#### Example
+
+```javascript
+var myObject = { name: 'John Doe',
+ aliases: ['John Ray', 'John Mee'],
+ date_of_birth: '1982/01/31' };
+scsave(myObject, 'johndoe.json');
+```
+
+##### johndoe.json contents...
+
+ { "name": "John Doe",
+ "aliases": ["John Ray", "John Mee"],
+ "date_of_birth": "1982/01/31"
+ };
+
+### plugin() function
+
+#### Update April 2015
+The `plugin()` function is deprecated. Please refer to [Anatomy of a
+ScriptCraft Plugin][anatomy] for an up-to-date step-by-step guide to
+creating a plugin which uses persistence (loading and saving data).
+
+#### Deprecated
+The `plugin()` function should be used to declare a javascript module
+whose state you want to have managed by ScriptCraft - that is - a
+Module whose state will be loaded at start up and saved at shut down.
+A plugin is just a regular javascript object whose state is managed by
+ScriptCraft. The only member of the plugin which whose persistence is
+managed by Scriptcraft is `store` - this special member will be
+automatically saved at shutdown and loaded at startup by
+ScriptCraft. This makes it easier to write plugins which need to
+persist data.
+
+#### Parameters
+
+ * pluginName (String) : The name of the plugin - this becomes a global variable.
+ * pluginDefinition (Object) : The various functions and members of the plugin object.
+ * isPersistent (boolean - optional) : Specifies whether or not the
+ plugin/object state should be loaded and saved by ScriptCraft.
+
+#### Example
+
+See chat/color.js for an example of a simple plugin - one which lets
+players choose a default chat color. See also [Anatomy of a
+ScriptCraft Plugin][anatomy].
+
+[anatomy]: ./Anatomy-of-a-Plugin.md
+
+### command() function
+
+The `command()` function is used to expose javascript functions for use by non-operators (regular players). Only operators should be allowed use raw javascript using the `/js ` command because it is too powerful for use by regular players and can be easily abused. However, the `/jsp ` command lets you (the operator / server administrator / plugin author) safely expose javascript functions for use by players.
+
+#### Parameters
+
+ * commandFunction: The named javascript function which will be invoked when the command is invoked by a player. The name of the function will be used as the command name so name this function accordingly. The callback function in turn takes 2 parameters...
+
+ * params : An Array of type String - the list of parameters passed to the command.
+ * sender : The [CommandSender][bukcs] object that invoked the command (this is usually a Player object but can be a Block ([BlockCommandSender][bukbcs]).
+
+ * options (Array|Function - optional) : An array of command options/parameters which the player can supply (It's useful to supply an array so that Tab-Completion works for the `/jsp ` commands. If a function is supplied instead of an array then the function will be invoked at TAB-completion time and should return an array of strings.
+ * intercepts (boolean - optional) : Indicates whether this command can intercept Tab-Completion of the `/jsp ` command - advanced usage - see alias/alias.js for example.
+
+#### Example
+
+ // javascript code
+ function boo( params, sender) {
+ echo( sender, params[0] );
+ }
+ command( boo );
+
+ # in-game execution
+ /jsp boo Hi!
+ > Hi!
+
+To use a callback for options (TAB-Completion) ...
+
+ var utils = require('utils');
+ function boo( params, sender ) {
+ var receiver = server.getPlayer( params[0] );
+ if ( receiver ){
+ echo( receiver, sender.name + ' says boo!');
+ }
+ }
+ command( boo, utils.playerNames );
+
+See chat/colors.js or alias/alias.js or homes/homes.js for more examples of how to use the `command()` function.
+
+### setTimeout() function
+
+This function mimics the setTimeout() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setTimeout() in the browser returns a numeric value which can be subsequently passed to clearTimeout(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearTimeout() implementation.
+
+If Node.js supports setTimeout() then it's probably good for ScriptCraft to support it too.
+
+#### Example
+
+```javascript
+//
+// start a storm in 5 seconds
+//
+setTimeout( function() {
+ var world = server.worlds.get(0);
+ world.setStorm(true);
+}, 5000);
+```
+
+### clearTimeout() function
+
+A scriptcraft implementation of clearTimeout().
+
+### setInterval() function
+
+This function mimics the setInterval() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setInterval() in the browser returns a numeric value which can be subsequently passed to clearInterval(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearInterval() implementation.
+
+### clearInterval() function
+
+A scriptcraft implementation of clearInterval().
+
+### refresh() function
+
+The refresh() function can be used to only reload the ScriptCraft plugin (it's like the `reload` command except it only reloads ScriptCraft). The refresh() function will ...
+
+1. Disable the ScriptCraft plugin.
+2. Unload all event listeners associated with the ScriptCraft plugin.
+3. Cancel all timed tasks (created by `setInterval` & `setTimeout`)
+3. Enable the ScriptCraft plugin.
+
+... refresh() can be used during development to reload only scriptcraft javascript files.
+See [issue #69][issue69] for more information.
+
+By default, if `self` is defined at runtime, it checks, whether `self` is server operator, otherwise fails with message. This behavivor can be modified using `skipOpCheck` parameter (useful, if you are doing some custom premission checks before calling this function).
+
+#### Parameters
+
+ * skipOpCheck (boolean - optional) : If true, the function won't check if `self` is server operator.
+
+[issue69]: https://github.com/walterhiggins/ScriptCraft/issues/69
+
+### addUnloadHandler() function
+
+The addUnloadHandler() function takes a callback function as a parameter. The callback will be called when the ScriptCraft plugin is unloaded (usually as a result of a a `reload` command or server shutdown).
+
+This function provides a way for ScriptCraft modules to do any required cleanup/housekeeping just prior to the ScriptCraft Plugin unloading.
+
+### isOp() function
+
+This function takes a single parameter and returns true if it's an operator or has operator-level privileges.
+
+## require - Node.js-style module loading in ScriptCraft
+
+Node.js is a server-side javascript environment with an excellent
+module loading system based on CommonJS. Modules in Node.js are really
+simple. Each module is in its own javascript file and all variables
+and functions within the file are private to that file/module only.
+There is a very concise explanation of CommonJS modules at...
+
+[http://wiki.commonjs.org/wiki/Modules/1.1.1.][cjsmodules]
+
+Node.js also has good documentation on [Modules][njsmod].
+
+If you want to export a variable or function you use the module.export
+property.
+
+For example imagine you have 3 files program.js, inc.js and math.js ...
+
+### math.js
+
+```javascript
+exports.add = function(a,b){
+ return a + b;
+}
+```
+
+### inc.js
+
+```javascript
+var math = require('./math');
+exports.increment = function(n){
+ return math.add(n, 1);
+}
+```
+
+### program.js
+
+```javascript
+var inc = require('./inc').increment;
+var a = 7;
+a = inc(a);
+print(a);
+```
+
+You can see from the above sample code that programs can use modules
+and modules themeselves can use other modules. Modules have full
+control over what functions and properties they want to provide to
+others.
+
+### Important
+
+Although ScriptCraft now supports Node.js style modules, it does not
+support node modules. Node.js and Rhino are two very different
+Javascript environments. ScriptCraft uses Rhino Javascript, not
+Node.js. Standard Node.js modules such as `'fs'` are not available in ScriptCraft.
+
+Modules can be loaded using relative or absolute paths. Per the CommonJS
+module specification, the '.js' suffix is optional.
+
+[cjsmodules]: http://wiki.commonjs.org/wiki/Modules/1.1.1.
+
+### module name resolution
+
+When resolving module names to file paths, ScriptCraft uses the following rules...
+
+ 1. if the module does not begin with './' or '/' then ...
+
+ 1.1 Look in the 'scriptcraft/lib' directory. If it's not there then...
+ 1.2 Look in the 'scriptcraft/modules' directory. If it's not there then
+ Throw an Error.
+
+ 2. If the module begins with './' or '/' then ...
+
+ 2.1 if the module begins with './' then it's treated as a file path. File paths are
+ always relative to the module from which the require() call is being made.
+
+ 2.2 If the module begins with '/' then it's treated as an absolute path.
+
+ If the module does not have a '.js' suffix, and a file with the same name and a .js sufix exists,
+ then the file will be loaded.
+
+ 3. If the module name resolves to a directory then...
+
+ 3.1 look for a package.json file in the directory and load the `main` property e.g.
+
+ // package.json located in './some-library/'
+ {
+ "main": './some-lib.js',
+ "name": 'some-library'
+ }
+
+ 3.2 if no package.json file exists then look for an index.js file in the directory
+
+## events Module
+
+The Events module provides a thin wrapper around CanaryMod's or
+Bukkit's Event-handling API. The Java-based CanaryMod and Bukkit
+Events APIs make use of Java Annotations which are not available in
+Javascript, so this module provides a simple way to listen to
+minecraft events in javascript.
+
+### events.on() static method
+
+This method is used to register event listeners. This method is called by all of the Event Helper methods.
+The `events` object has functions for registering listeners for each type of event. For example, you can register a block-break listener using events.on:
+
+```javascript
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
+} );
+```
+
+or you can (and probably should) use the more succinct:
+
+```javascript
+events.blockDestroy( function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
+} );
+```
+
+The events.on method can be used to register standard CanaryMod/Bukkit
+events and can also be used to register non-standard events - that is
+- events provided by plugins.
+
+#### Parameters
+
+ * eventType - A Java class. See the [CanaryMod Hook API][cmEvtApi] or [Bukkit Event API][buk] for details of the many event types.
+
+ * callback - A function which will be called whenever the event
+ fires. The callback in turn takes 2 parameters:
+
+ - event : the event fired
+ - cancel : a function which if invoked will cancel the event - not all event types are cancelable; this function only cancels cancelable events).
+
+ * priority (optional - default: "CRITICAL" for CanaryMod or "HIGHEST" for Bukkit) -
+ The priority the listener/callback takes over other listeners to the same event.
+ Possible values for CanaryMod are "CRITICAL", "HIGH", "LOW", "NORMAL" and "PASSIVE".
+ For an explanation of what the different CanaryMod Hook priorities
+ mean, refer to CanaryMod's [Hook Priority class][cmPriority].
+ Possible values for Bukkit are "HIGH", "HIGHEST", "LOW", "LOWEST", "NORMAL", "MONITOR".
+ For an explanation of what the different Bukkit Event priorities
+ mean, refer to bukkit's [Event API Reference][buk2].
+
+#### Returns
+
+An object which can be used to unregister the listener.
+
+#### Example:
+
+The following code will print a message on screen every time a block is broken in the game
+
+```javascript
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
+} );
+```
+
+To handle an event only once and unregister from further events...
+
+```javascript
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo( evt.player, evt.player.name + ' broke a block!');
+ this.unregister();
+} );
+```
+
+The `this` keyword when used inside the callback function refers to
+the Listener object created by ScriptCraft. It has 2 methods
+`unregister()` which can be used to stop listening and `cancel()`
+which can be used to cancel the current event. The object returned by
+`events.on()` only has the `unregister()` method, the `cancel()`
+method is only available from within the event handling function.
+
+To unregister a listener *outside* of the listener function...
+
+```javascript
+var myBlockBreakListener = events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt ) { ... } );
+...
+myBlockBreakListener.unregister();
+```
+
+[buk2]: http://wiki.bukkit.org/Event_API_Reference
+[buk]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/event/Event.html
+[cmEvtApi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/Hook.html
+[cmPriority]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/plugin/Priority.html
+
+## Events Helper Module (CanaryMod version)
+The Events helper module provides a suite of functions - one for each possible event.
+For example, the events.blockDestroy() function is just a wrapper function which calls events.on(net.canarymod.hook.player.BlockDestroyHook, callback, priority)
+This module is a convenience wrapper for easily adding new event handling functions in Javascript.
+At the in-game or server-console prompt, players/admins can type `events.` and use TAB completion
+to choose from any of the approx. 160 different event types to listen to.
+
+### Usage
+
+ events.blockDestroy( function( event ) {
+ echo( event.player, 'You broke a block!');
+ });
+
+The crucial difference is that the events module now has functions for each of the built-in events. The functions are accessible via TAB-completion so will help beginning programmers to explore the events at the server console window.
+
+### events.minecartActivate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.MinecartActivateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/MinecartActivateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.villagerTradeUnlock()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VillagerTradeUnlockHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VillagerTradeUnlockHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.mobTarget()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.MobTargetHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/MobTargetHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chickenLayEgg()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.ChickenLayEggHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/ChickenLayEggHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.potionEffectFinish()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.PotionEffectFinishHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/PotionEffectFinishHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityMove()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityMoveHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityMoveHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.hangingEntityDestroy()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.HangingEntityDestroyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/HangingEntityDestroyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleCollision()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleCollisionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleCollisionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.potionEffectApplied()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.PotionEffectAppliedHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/PotionEffectAppliedHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleDestroy()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleDestroyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleDestroyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleEnter()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleEnterHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleEnterHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.damage()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.DamageHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/DamageHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityMount()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityMountHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityMountHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.slimeSplit()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.SlimeSplitHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/SlimeSplitHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.endermanDropBlock()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EndermanDropBlockHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EndermanDropBlockHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemTouchGround()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.ItemTouchGroundHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/ItemTouchGroundHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entitySpawn()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntitySpawnHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntitySpawnHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.endermanPickupBlock()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EndermanPickupBlockHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EndermanPickupBlockHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleDamage()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleDamageHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleDamageHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityLightningStruck()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityLightningStruckHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityLightningStruckHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityDespawn()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityDespawnHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityDespawnHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleMove()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleMoveHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleMoveHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.projectileHit()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.ProjectileHitHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/ProjectileHitHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityDeath()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityDeathHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityDeathHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityTame()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityTameHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/EntityTameHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.vehicleExit()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.VehicleExitHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/VehicleExitHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.dimensionSwitch()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.DimensionSwitchHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/entity/DimensionSwitchHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.foodLevel()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.FoodLevelHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/FoodLevelHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.bookEdit()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BookEditHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BookEditHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.eat()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.EatHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/EatHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerList()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerListHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerListHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerIdle()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerIdleHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerIdleHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.enchant()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.EnchantHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/EnchantHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerArmSwing()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerArmSwingHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerArmSwingHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.teleport()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.TeleportHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/TeleportHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.anvilUse()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.AnvilUseHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/AnvilUseHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.portalUse()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PortalUseHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PortalUseHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.foodSaturation()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.FoodSaturationHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/FoodSaturationHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.connection()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ConnectionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ConnectionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerRespawned()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerRespawnedHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerRespawnedHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.armorBroken()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ArmorBrokenHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ArmorBrokenHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.levelUp()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.LevelUpHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/LevelUpHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockRightClick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BlockRightClickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BlockRightClickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemDrop()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ItemDropHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ItemDropHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemFrameRotate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ItemFrameRotateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ItemFrameRotateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerRespawning()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerRespawningHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerRespawningHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.craft()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.CraftHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/CraftHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.experience()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ExperienceHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ExperienceHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.signChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.SignChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/SignChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.healthChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.HealthChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/HealthChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.disconnection()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.DisconnectionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/DisconnectionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.gameModeChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.GameModeChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/GameModeChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.preConnection()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PreConnectionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PreConnectionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.villagerTrade()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.VillagerTradeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/VillagerTradeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.returnFromIdle()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ReturnFromIdleHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ReturnFromIdleHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.armorStandModify()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ArmorStandModifyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ArmorStandModifyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.slotClick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.SlotClickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/SlotClickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemFrameSetItem()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ItemFrameSetItemHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ItemFrameSetItemHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityRightClick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.EntityRightClickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/EntityRightClickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.foodExhaustion()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.FoodExhaustionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/FoodExhaustionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chat()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ChatHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ChatHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemPickup()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ItemPickupHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ItemPickupHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.bedExit()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BedExitHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BedExitHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPlace()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BlockPlaceHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BlockPlaceHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.heldItemChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.HeldItemChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/HeldItemChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.toolBroken()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ToolBrokenHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ToolBrokenHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.kick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.KickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/KickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerDeath()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerDeathHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerDeathHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockLeftClick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BlockLeftClickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BlockLeftClickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockDestroy()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BlockDestroyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BlockDestroyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.bedEnter()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BedEnterHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BedEnterHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.signShow()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.SignShowHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/SignShowHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventory()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.InventoryHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/InventoryHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerMove()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.PlayerMoveHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/PlayerMoveHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemUse()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.ItemUseHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/ItemUseHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.ban()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.BanHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/BanHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.statGained()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [player.StatGainedHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/player/StatGainedHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.smeltBegin()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.SmeltBeginHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/SmeltBeginHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.treeGrow()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.TreeGrowHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/TreeGrowHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chunkCreated()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.ChunkCreatedHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/ChunkCreatedHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.liquidDestroy()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.LiquidDestroyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/LiquidDestroyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chunkLoaded()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.ChunkLoadedHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/ChunkLoadedHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.pistonRetract()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.PistonRetractHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/PistonRetractHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.smelt()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.SmeltHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/SmeltHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockUpdate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.BlockUpdateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/BlockUpdateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.portalDestroy()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.PortalDestroyHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/PortalDestroyHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.ignition()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.IgnitionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/IgnitionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.redstoneChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.RedstoneChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/RedstoneChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.weatherChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.WeatherChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/WeatherChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chunkCreation()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.ChunkCreationHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/ChunkCreationHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.hopperTransfer()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.HopperTransferHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/HopperTransferHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.chunkUnload()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.ChunkUnloadHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/ChunkUnloadHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockGrow()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.BlockGrowHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/BlockGrowHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.dispense()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.DispenseHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/DispenseHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockDropXp()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.BlockDropXpHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/BlockDropXpHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.fireworkExplode()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.FireworkExplodeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/FireworkExplodeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.leafDecay()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.LeafDecayHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/LeafDecayHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.pistonExtend()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.PistonExtendHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/PistonExtendHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.noteBlockPlay()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.NoteBlockPlayHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/NoteBlockPlayHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.lightningStrike()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.LightningStrikeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/LightningStrikeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.decorate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.DecorateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/DecorateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.explosion()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.ExplosionHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/ExplosionHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.tNTActivate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.TNTActivateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/TNTActivateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.timeChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.TimeChangeHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/TimeChangeHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.flow()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.FlowHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/FlowHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.portalCreate()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.PortalCreateHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/PortalCreateHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPhysics()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [world.BlockPhysicsHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/world/BlockPhysicsHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.playerCommand()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [command.PlayerCommandHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/command/PlayerCommandHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.consoleCommand()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [command.ConsoleCommandHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/command/ConsoleCommandHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.commandBlockCommand()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [command.CommandBlockCommandHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/command/CommandBlockCommandHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.loadWorld()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.LoadWorldHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/LoadWorldHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.permissionCheck()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.PermissionCheckHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/PermissionCheckHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.serverGuiStart()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.ServerGuiStartHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/ServerGuiStartHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.unloadWorld()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.UnloadWorldHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/UnloadWorldHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.pluginDisable()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.PluginDisableHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/PluginDisableHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.pluginEnable()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.PluginEnableHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/PluginEnableHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.serverTick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.ServerTickHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/ServerTickHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.serverListPing()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.ServerListPingHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/ServerListPingHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.serverShutdown()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [system.ServerShutdownHook event](https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/system/ServerShutdownHook.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+## Events Helper Module (SpigotMC version)
+The Events helper module provides a suite of functions - one for each possible event.
+For example, the events.blockBreak() function is just a wrapper function which calls events.on(org.bukkit.event.block.BlockBreakEvent, callback, priority)
+This module is a convenience wrapper for easily adding new event handling functions in Javascript.
+At the in-game or server-console prompt, players/admins can type `events.` and use TAB completion
+to choose from any of the approx. 160 different event types to listen to.
+
+### Usage
+
+ events.blockBreak( function( event ) {
+ echo( event.player, 'You broke a block!');
+ });
+
+The crucial difference is that the events module now has functions for each of the built-in events. The functions are accessible via TAB-completion so will help beginning programmers to explore the events at the server console window.
+
+### events.blockFade()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockFadeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockFadeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockFromTo()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockFromToEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockFromToEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.notePlay()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.NotePlayEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/NotePlayEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPlace()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockPlaceEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockPlaceEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockGrow()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockGrowEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockGrowEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityBlockForm()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.EntityBlockFormEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/EntityBlockFormEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPistonExtend()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockPistonExtendEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockPistonExtendEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPistonRetract()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockPistonRetractEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockPistonRetractEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockSpread()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockSpreadEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockSpreadEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockBurn()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockBurnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockBurnEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockDamage()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockDamageEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockDamageEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.leavesDecay()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.LeavesDecayEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/LeavesDecayEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockDispense()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockDispenseEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockDispenseEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockForm()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockFormEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockFormEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockMultiPlace()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockMultiPlaceEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockMultiPlaceEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockIgnite()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockIgniteEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockIgniteEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockPhysics()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockPhysicsEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockPhysicsEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockRedstone()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockRedstoneEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockRedstoneEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.cauldronLevelChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.CauldronLevelChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/CauldronLevelChangeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockCanBuild()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockCanBuildEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockCanBuildEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.signChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.SignChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/SignChangeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockExp()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockExpEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockExpEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockExplode()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockExplodeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockExplodeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.blockBreak()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [block.BlockBreakEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/block/BlockBreakEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.prepareAnvil()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.PrepareAnvilEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/PrepareAnvilEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.brew()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.BrewEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/BrewEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventoryClose()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryCloseEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryCloseEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventoryDrag()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryDragEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryDragEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventoryCreative()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryCreativeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryCreativeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.prepareItemCraft()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.PrepareItemCraftEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/PrepareItemCraftEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.craftItem()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.CraftItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/CraftItemEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventoryClick()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryClickEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryClickEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.brewingStandFuel()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.BrewingStandFuelEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/BrewingStandFuelEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventoryPickupItem()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryPickupItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryPickupItemEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.furnaceExtract()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.FurnaceExtractEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/FurnaceExtractEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.inventory()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [inventory.InventoryEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.furnaceBurn()
+
+#### Parameters
-## Modules in Scriptcraft
+ * callback - A function which is called whenever the [inventory.FurnaceBurnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/FurnaceBurnEvent.html) is fired
-ScriptCraft has a simple module loading system. In ScriptCraft, files
-and modules are in one-to-one correspondence. As an example, foo.js
-loads the module circle.js in the same directory.
-*ScriptCraft now uses the same module system as Node.js - see [Node.js Modules][njsmod] for more details.*
+ * priority - optional - see events.on() for more information.
-[njsmod]: http://nodejs.org/api/modules.html
+### events.furnaceSmelt()
-The contents of foo.js:
+#### Parameters
-```javascript
-var circle = require('./circle.js');
-console.log( 'The area of a circle of radius 4 is '
- + circle.area(4));
-```
+ * callback - A function which is called whenever the [inventory.FurnaceSmeltEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/FurnaceSmeltEvent.html) is fired
-The contents of circle.js:
+ * priority - optional - see events.on() for more information.
-```javascript
-var PI = Math.PI;
-exports.area = function (r) {
- return PI * r * r;
-};
-exports.circumference = function (r) {
- return 2 * PI * r;
-};
-```
+### events.inventoryOpen()
-The module circle.js has exported the functions area() and
-circumference(). To add functions and objects to the root of your
-module, you can add them to the special exports object.
+#### Parameters
-Variables local to the module will be private, as though the module
-was wrapped in a function. In this example the variable PI is private
-to circle.js.
+ * callback - A function which is called whenever the [inventory.InventoryOpenEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryOpenEvent.html) is fired
-If you want the root of your module's export to be a function (such as
-a constructor) or if you want to export a complete object in one
-assignment instead of building it one property at a time, assign it to
-module.exports instead of exports.
+ * priority - optional - see events.on() for more information.
-## Module Loading
+### events.inventoryMoveItem()
-When the ScriptCraft Java plugin is first installed, a new
-subdirectory is created in the craftbukkit/plugins directory. If your
-craftbukkit directory is called 'craftbukkit' then the new
-subdirectories will be ...
+#### Parameters
- * craftbukkit/plugins/scriptcraft/
- * craftbukkit/plugins/scriptcraft/plugins
- * craftbukkit/plugins/scriptcraft/modules
- * craftbukkit/plugins/scriptcraft/lib
+ * callback - A function which is called whenever the [inventory.InventoryMoveItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/inventory/InventoryMoveItemEvent.html) is fired
-... The `plugins`, `modules` and `lib` directories each serve a different purpose.
+ * priority - optional - see events.on() for more information.
-### The plugins directory
+### events.hangingPlace()
-At server startup the ScriptCraft Java plugin is loaded and begins
-automatically loading and executing all of the modules (javascript
-files with the extension `.js`) it finds in the `scriptcraft/plugins`
-directory. All modules in the plugins directory are automatically
-loaded into the `global` namespace. What this means is that anything a
-module in the `plugins` directory exports becomes a global
-variable. For example, if you have a module greeting.js in the plugins
-directory....
+#### Parameters
-```javascript
-exports.greet = function(player) {
- player.sendMessage('Hello ' + player.name);
-};
-```
+ * callback - A function which is called whenever the [hanging.HangingPlaceEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/hanging/HangingPlaceEvent.html) is fired
-... then `greet` becomes a global function and can be used at the
-in-game (or server) command prompt like so...
+ * priority - optional - see events.on() for more information.
- /js greet(self)
+### events.hangingBreak()
-... This differs from how modules (in NodeJS and commonJS
-environments) normally work. If you want your module to be exported
-globally, put it in the `plugins` directory. If you don't want your
-module to be exported globally but only want it to be used by other
-modules, then put it in the `modules` directory instead. If you've
-used previous versions of ScriptCraft and have put your custom
-javascript modules in the `js-plugins` directory, then put them in the
-`scriptcraft/plugins` directory. To summarise, modules in this directory are ...
+#### Parameters
- * Automatically loaded and run at server startup.
- * Anything exported by modules becomes a global variable.
+ * callback - A function which is called whenever the [hanging.HangingBreakEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/hanging/HangingBreakEvent.html) is fired
-### The modules directory
+ * priority - optional - see events.on() for more information.
-The module directory is where you should place your modules if you
-don't want to export globally. In javascript, it's considered best
-practice not to have too many global variables, so if you want to
-develop modules for others to use, or want to develop more complex
-mods then your modules should be placed in the `modules` directory.
-*Modules in the `modules` directory are not automatically loaded at
-startup*, instead, they are loaded and used by other modules/plugins
-using the standard `require()` function. This is the key difference
-between modules in the `plugins` directory and modules in the
-`modules` directory. Modules in the `plugins` directory are
-automatically loaded and exported in to the global namespace at server
-startup, modules in the `modules` directory are not.
+### events.hangingBreakByEntity()
-### The lib directory
+#### Parameters
-Modules in the `lib` directory are for use by ScriptCraft and some
-core functions for use by module and plugin developers are also
-provided. The `lib` directory is for internal use by ScriptCraft.
-Modules in this directory are not automatically loaded nor are they
-globally exported.
+ * callback - A function which is called whenever the [hanging.HangingBreakByEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/hanging/HangingBreakByEntityEvent.html) is fired
-### plugins sub-directories
+ * priority - optional - see events.on() for more information.
-As of December 24 2013, the `scriptcraft/plugins` directory has the following sub-directories...
+### events.entityToggleGlide()
- * drone - Contains the drone module and drone extensions. Drone was the first scriptcraft module.
- * mini-games - Contains mini-games
- * arrows - The arrows module - Changes the behaviour of Arrows: Explosive, Fireworks, Teleportation etc.
- * signs - The signs module (includes example signs) - create interactive signs.
- * chat - The chat plugin/module
- * alias - The alias plugin/module - for creating custom aliases for commonly used commands.
- * home - The home module - for setting homes and visiting other homes.
+#### Parameters
-## Global variables
+ * callback - A function which is called whenever the [entity.EntityToggleGlideEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityToggleGlideEvent.html) is fired
-There are a couple of special javascript variables available in ScriptCraft...
-
-### __plugin variable
-The ScriptCraft JavaPlugin object.
+ * priority - optional - see events.on() for more information.
-### server variable
-The Minecraft Server object
+### events.itemSpawn()
-### self variable
-The current player. (Note - this value should not be used in
-multi-threaded scripts or event-handling code - it's not
-thread-safe). This variable is only safe to use at the in-game prompt
-and should *never* be used in modules. For example you can use it here...
+#### Parameters
- /js console.log(self.name)
+ * callback - A function which is called whenever the [entity.ItemSpawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ItemSpawnEvent.html) is fired
-... but not in any javascript module you create yourself or in any
-event handling code. `self` is a temporary short-lived variable which
-only exists in the context of the in-game or server command prompts.
+ * priority - optional - see events.on() for more information.
-### config variable
-ScriptCraft configuration - this object is loaded and saved at startup/shutdown.
+### events.spawnerSpawn()
-### events variable
-The events object is used to add new event handlers to Minecraft.
+#### Parameters
-## Module variables
-The following variables are available only within the context of Modules. (not available at in-game prompt).
+ * callback - A function which is called whenever the [entity.SpawnerSpawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/SpawnerSpawnEvent.html) is fired
-### __filename variable
-The current file - this variable is only relevant from within the context of a Javascript module.
+ * priority - optional - see events.on() for more information.
-### __dirname variable
-The current directory - this variable is only relevant from within the context of a Javascript module.
+### events.foodLevelChange()
-## Global functions
+#### Parameters
-ScripCraft provides some global functions which can be used by all plugins/modules...
+ * callback - A function which is called whenever the [entity.FoodLevelChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/FoodLevelChangeEvent.html) is fired
-### echo function
+ * priority - optional - see events.on() for more information.
-The `echo()` function displays a message on the in-game screen. The
-message is displayed to the `self` player (this is usually the player
-who issued the `/js` or `/jsp` command).
+### events.lingeringPotionSplash()
-#### Example
+#### Parameters
- /js echo('Hello World')
+ * callback - A function which is called whenever the [entity.LingeringPotionSplashEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/LingeringPotionSplashEvent.html) is fired
-For programmers familiar with Javascript web programming, an `alert`
-function is also provided. `alert` works exactly the same as `echo`
-e.g. `alert('Hello World')`.
+ * priority - optional - see events.on() for more information.
-#### Notes
+### events.entityShootBow()
-The `echo` and `alert` functions are provided as convenience functions
-for beginning programmers. The use of these 2 functions is not
-recommended in event-handling code or multi-threaded code. In such
-cases, if you want to send a message to a given player then use the
-Bukkit API's [Player.sendMessage()][plsm] function instead.
+#### Parameters
-[plsm]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/CommandSender.html#sendMessage(java.lang.String)
+ * callback - A function which is called whenever the [entity.EntityShootBowEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityShootBowEvent.html) is fired
-### require() function
+ * priority - optional - see events.on() for more information.
-ScriptCraft's `require()` function is used to load modules. The
-`require()` function takes a module name as a parameter and will try
-to load the named module.
+### events.entityTargetLivingEntity()
-#### Parameters
+#### Parameters
- * modulename - The name of the module to be loaded. Can be one of the following...
+ * callback - A function which is called whenever the [entity.EntityTargetLivingEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityTargetLivingEntityEvent.html) is fired
- - A relative file path (with or without `.js` suffix)
- - An absolute file path (with or without `.js` suffix)
- - A relative directory path (uses node.js rules for directories)
- - An absolute directory path (uses node.js rules for directories)
- - A name of the form `'events'` - in which case the `lib` directory and `modules` directories are searched for the module.
+ * priority - optional - see events.on() for more information.
-#### Return
+### events.entityCombust()
-require() will return the loaded module's exports.
+#### Parameters
-### scload() function
+ * callback - A function which is called whenever the [entity.EntityCombustEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityCombustEvent.html) is fired
-#### No longer recommended for use by Plugin/Module developers (deprecated)
+ * priority - optional - see events.on() for more information.
-scload() should only be used to load .json data.
+### events.entityBreed()
-#### Parameters
+#### Parameters
- * filename - The name of the file to load.
- * warnOnFileNotFound (optional - default: false) - warn if the file was not found.
+ * callback - A function which is called whenever the [entity.EntityBreedEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityBreedEvent.html) is fired
-#### Returns
+ * priority - optional - see events.on() for more information.
-scload() will return the result of the last statement evaluated in the file.
+### events.expBottle()
-#### Example
+#### Parameters
- scload("myFile.js"); // loads a javascript file and evaluates it.
+ * callback - A function which is called whenever the [entity.ExpBottleEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ExpBottleEvent.html) is fired
- var myData = scload("myData.json"); // loads a javascript file and evaluates it - eval'd contents are returned.
+ * priority - optional - see events.on() for more information.
-##### myData.json contents...
+### events.slimeSplit()
- { players: {
- walterh: {
- h: ["jsp home {1}"],
- sunny:["time set 0",
- "weather clear"]
- }
- }
- }
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.SlimeSplitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/SlimeSplitEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityDeath()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityDeathEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDeathEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityAirChange()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityAirChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityAirChangeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.projectileLaunch()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.ProjectileLaunchEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ProjectileLaunchEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityPortal()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityPortalEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityPortalEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.horseJump()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.HorseJumpEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/HorseJumpEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.fireworkExplode()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.FireworkExplodeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/FireworkExplodeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entityExplode()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntityExplodeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityExplodeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.itemMerge()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.ItemMergeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ItemMergeEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.entitySpawn()
+
+#### Parameters
+
+ * callback - A function which is called whenever the [entity.EntitySpawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntitySpawnEvent.html) is fired
+
+ * priority - optional - see events.on() for more information.
+
+### events.projectileHit()
-### scsave() function
+#### Parameters
-The scsave() function saves an in-memory javascript object to a
-specified file. Under the hood, scsave() uses JSON (specifically
-json2.js) to save the object. Again, there will usually be no need to
-call this function directly as all javascript plugins' state are saved
-automatically if they are declared using the `plugin()` function. Any
-in-memory object saved using the `scsave()` function can later be
-restored using the `scload()` function.
+ * callback - A function which is called whenever the [entity.ProjectileHitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ProjectileHitEvent.html) is fired
-#### Parameters
+ * priority - optional - see events.on() for more information.
- * objectToSave : The object you want to save.
- * filename : The name of the file you want to save it to.
+### events.creatureSpawn()
-#### Example
+#### Parameters
-```javascript
-var myObject = { name: 'John Doe',
- aliases: ['John Ray', 'John Mee'],
- date_of_birth: '1982/01/31' };
-scsave(myObject, 'johndoe.json');
-```
+ * callback - A function which is called whenever the [entity.CreatureSpawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/CreatureSpawnEvent.html) is fired
-##### johndoe.json contents...
+ * priority - optional - see events.on() for more information.
- { "name": "John Doe",
- "aliases": ["John Ray", "John Mee"],
- "date_of_birth": "1982/01/31"
- };
+### events.entityPortalExit()
-### plugin() function
+#### Parameters
-The `plugin()` function should be used to declare a javascript module
-whose state you want to have managed by ScriptCraft - that is - a
-Module whose state will be loaded at start up and saved at shut down.
-A plugin is just a regular javascript object whose state is managed by
-ScriptCraft. The only member of the plugin which whose persistence is
-managed by Scriptcraft is `store` - this special member will be
-automatically saved at shutdown and loaded at startup by
-ScriptCraft. This makes it easier to write plugins which need to
-persist data.
+ * callback - A function which is called whenever the [entity.EntityPortalExitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityPortalExitEvent.html) is fired
-#### Parameters
-
- * pluginName (String) : The name of the plugin - this becomes a global variable.
- * pluginDefinition (Object) : The various functions and members of the plugin object.
- * isPersistent (boolean - optional) : Specifies whether or not the
- plugin/object state should be loaded and saved by ScriptCraft.
+ * priority - optional - see events.on() for more information.
-#### Example
+### events.entityTame()
-See chat/color.js for an example of a simple plugin - one which lets
-players choose a default chat color. See also [Anatomy of a
-ScriptCraft Plugin][anatomy].
-
-[anatomy]: ./Anatomy-of-a-Plugin.md
+#### Parameters
-### command() function
+ * callback - A function which is called whenever the [entity.EntityTameEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityTameEvent.html) is fired
-The `command()` function is used to expose javascript functions for
-use by non-operators (regular players). Only operators should be
-allowed use raw javascript using the `/js ` command because it is too
-powerful for use by regular players and can be easily abused. However,
-the `/jsp ` command lets you (the operator / server administrator /
-plugin author) safely expose javascript functions for use by players.
+ * priority - optional - see events.on() for more information.
-#### Parameters
-
- * commandName : The name to give your command - the command will
- be invoked like this by players `/jsp commandName`
- * commandFunction: The javascript function which will be invoked when
- the command is invoked by a player. The callback function in turn
- takes 2 parameters...
-
- * params : An Array of type String - the list of parameters
- passed to the command.
- * sender : The [CommandSender][bukcs] object that invoked the
- command (this is usually a Player object but can be a Block
- ([BlockCommandSender][bukbcs]).
-
- * options (Array - optional) : An array of command options/parameters
- which the player can supply (It's useful to supply an array so that
- Tab-Completion works for the `/jsp ` commands.
- * intercepts (boolean - optional) : Indicates whether this command
- can intercept Tab-Completion of the `/jsp ` command - advanced
- usage - see alias/alias.js for example.
+### events.entityPortalEnter()
-#### Example
+#### Parameters
-See chat/colors.js or alias/alias.js or homes/homes.js for examples of
-how to use the `command()` function.
+ * callback - A function which is called whenever the [entity.EntityPortalEnterEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityPortalEnterEvent.html) is fired
-### setTimeout() function
+ * priority - optional - see events.on() for more information.
-This function mimics the setTimeout() function used in browser-based
-javascript. However, the function will only accept a function
-reference, not a string of javascript code. Where setTimeout() in the
-browser returns a numeric value which can be subsequently passed to
-clearTimeout(), This implementation returns a [BukkitTask][btdoc]
-object which can be subsequently passed to ScriptCraft's own
-clearTimeout() implementation.
+### events.playerLeashEntity()
-If Node.js supports setTimeout() then it's probably good for ScriptCraft to support it too.
+#### Parameters
-[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
+ * callback - A function which is called whenever the [entity.PlayerLeashEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/PlayerLeashEntityEvent.html) is fired
-#### Example
+ * priority - optional - see events.on() for more information.
-```javascript
-//
-// start a storm in 5 seconds
-//
-setTimeout( function() {
- var world = server.worlds.get(0);
- world.setStorm(true);
-}, 5000);
-```
+### events.entityDamage()
-### clearTimeout() function
+#### Parameters
-A scriptcraft implementation of clearTimeout().
+ * callback - A function which is called whenever the [entity.EntityDamageEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDamageEvent.html) is fired
-### setInterval() function
+ * priority - optional - see events.on() for more information.
-This function mimics the setInterval() function used in browser-based
-javascript. However, the function will only accept a function
-reference, not a string of javascript code. Where setInterval() in
-the browser returns a numeric value which can be subsequently passed
-to clearInterval(), This implementation returns a [BukkitTask][btdoc]
-object which can be subsequently passed to ScriptCraft's own
-clearInterval() implementation.
+### events.pigZap()
-If Node.js supports setInterval() then it's probably good for
-ScriptCraft to support it too.
+#### Parameters
-[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
+ * callback - A function which is called whenever the [entity.PigZapEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/PigZapEvent.html) is fired
-### clearInterval() function
+ * priority - optional - see events.on() for more information.
-A scriptcraft implementation of clearInterval().
+### events.entityCombustByEntity()
-### refresh() function
+#### Parameters
-The refresh() function can be used to only reload the ScriptCraft
-plugin (it's like the `reload` command except it only reloads
-ScriptCraft). The refresh() function will ...
+ * callback - A function which is called whenever the [entity.EntityCombustByEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityCombustByEntityEvent.html) is fired
-1. Disable the ScriptCraft plugin.
-2. Unload all event listeners associated with the ScriptCraft plugin.
-3. Enable the ScriptCraft plugin.
+ * priority - optional - see events.on() for more information.
-... refresh() can be used during development to reload only scriptcraft javascript files.
-See [issue #69][issue69] for more information.
+### events.entityChangeBlock()
-[issue69]: https://github.com/walterhiggins/ScriptCraft/issues/69
+#### Parameters
-### addUnloadHandler() function
+ * callback - A function which is called whenever the [entity.EntityChangeBlockEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityChangeBlockEvent.html) is fired
-The addUnloadHandler() function takes a callback function as a
-parameter. The callback will be called when the ScriptCraft plugin is
-unloaded (usually as a result of a a `reload` command or server
-shutdown).
+ * priority - optional - see events.on() for more information.
-This function provides a way for ScriptCraft modules to do any
-required cleanup/housekeeping just prior to the ScriptCraft Plugin
-unloading.
+### events.areaEffectCloudApply()
-## require - Node.js-style module loading in ScriptCraft
+#### Parameters
-Node.js is a server-side javascript environment with an excellent
-module loading system based on CommonJS. Modules in Node.js are really
-simple. Each module is in its own javascript file and all variables
-and functions within the file are private to that file/module only.
-There is a very concise explanation of CommonJS modules at...
+ * callback - A function which is called whenever the [entity.AreaEffectCloudApplyEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/AreaEffectCloudApplyEvent.html) is fired
-[http://wiki.commonjs.org/wiki/Modules/1.1.1.][cjsmodules]
+ * priority - optional - see events.on() for more information.
-Node.js also has good documentation on [Modules][njsmod].
+### events.creeperPower()
-If you want to export a variable or function you use the module.export
-property.
+#### Parameters
-For example imagine you have 3 files program.js, inc.js and math.js ...
+ * callback - A function which is called whenever the [entity.CreeperPowerEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/CreeperPowerEvent.html) is fired
-### math.js
+ * priority - optional - see events.on() for more information.
-```javascript
-exports.add = function(a,b){
- return a + b;
-}
-```
+### events.sheepDyeWool()
-### inc.js
+#### Parameters
-```javascript
-var math = require('./math');
-exports.increment = function(n){
- return math.add(n, 1);
-}
-```
+ * callback - A function which is called whenever the [entity.SheepDyeWoolEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/SheepDyeWoolEvent.html) is fired
-### program.js
+ * priority - optional - see events.on() for more information.
-```javascript
-var inc = require('./inc').increment;
-var a = 7;
-a = inc(a);
-print(a);
-```
+### events.playerDeath()
-You can see from the above sample code that programs can use modules
-and modules themeselves can use other modules. Modules have full
-control over what functions and properties they want to provide to
-others.
+#### Parameters
-### Important
+ * callback - A function which is called whenever the [entity.PlayerDeathEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/PlayerDeathEvent.html) is fired
-Although ScriptCraft now supports Node.js style modules, it does not
-support node modules. Node.js and Rhino are two very different
-Javascript environments. ScriptCraft uses Rhino Javascript, not
-Node.js. Standard Node.js modules such as `'fs'` are not available in ScriptCraft.
+ * priority - optional - see events.on() for more information.
-Modules can be loaded using relative or absolute paths. Per the CommonJS
-module specification, the '.js' suffix is optional.
+### events.villagerReplenishTrade()
-[cjsmodules]: http://wiki.commonjs.org/wiki/Modules/1.1.1.
+#### Parameters
-### module name resolution
+ * callback - A function which is called whenever the [entity.VillagerReplenishTradeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/VillagerReplenishTradeEvent.html) is fired
-When resolving module names to file paths, ScriptCraft uses the following rules...
+ * priority - optional - see events.on() for more information.
- 1. if the module does not begin with './' or '/' then ...
+### events.entityCombustByBlock()
- 1.1 Look in the 'scriptcraft/lib' directory. If it's not there then...
- 1.2 Look in the 'scriptcraft/modules' directory. If it's not there then
- Throw an Error.
+#### Parameters
- 2. If the module begins with './' or '/' then ...
-
- 2.1 if the module begins with './' then it's treated as a file path. File paths are
- always relative to the module from which the require() call is being made.
+ * callback - A function which is called whenever the [entity.EntityCombustByBlockEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityCombustByBlockEvent.html) is fired
- 2.2 If the module begins with '/' then it's treated as an absolute path.
+ * priority - optional - see events.on() for more information.
- If the module does not have a '.js' suffix, and a file with the same name and a .js sufix exists,
- then the file will be loaded.
+### events.entityResurrect()
- 3. If the module name resolves to a directory then...
-
- 3.1 look for a package.json file in the directory and load the `main` property e.g.
-
- // package.json located in './some-library/'
- {
- "main": './some-lib.js',
- "name": 'some-library'
- }
-
- 3.2 if no package.json file exists then look for an index.js file in the directory
+#### Parameters
-## events Module
+ * callback - A function which is called whenever the [entity.EntityResurrectEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityResurrectEvent.html) is fired
-The Events module provides a thin wrapper around Bukkit's
-Event-handling API. Bukkit's Events API makes use of Java Annotations
-which are not available in Javascript, so this module provides a
-simple way to listen to minecraft events in javascript.
+ * priority - optional - see events.on() for more information.
-### events.on() static method
+### events.villagerAcquireTrade()
-This method is used to register event listeners.
+#### Parameters
-#### Parameters
+ * callback - A function which is called whenever the [entity.VillagerAcquireTradeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/VillagerAcquireTradeEvent.html) is fired
- * eventName - A string or java class. If a string is supplied it must
- be part of the Bukkit event class name. See [Bukkit API][buk] for
- details of the many bukkit event types. When a string is supplied
- there is no need to provide the full class name - you should omit
- the 'org.bukkit.event' prefix. e.g. if the string
- "block.BlockBreakEvent" is supplied then it's converted to the
- org.bukkit.event.block.BlockBreakEvent class .
-
- If a java class is provided (say in the case where you've defined
- your own custom event) then provide the full class name (without
- enclosing quotes).
+ * priority - optional - see events.on() for more information.
- * callback - A function which will be called whenever the event
- fires. The callback should take a single parameter, event (the event fired).
+### events.enderDragonChangePhase()
- * priority (optional - default: "HIGHEST") - The priority the
- listener/callback takes over other listeners to the same
- event. Possible values are "HIGH", "HIGHEST", "LOW", "LOWEST",
- "NORMAL", "MONITOR". For an explanation of what the different
- priorities mean refer to bukkit's [Event API Reference][buk2].
+#### Parameters
-#### Returns
+ * callback - A function which is called whenever the [entity.EnderDragonChangePhaseEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EnderDragonChangePhaseEvent.html) is fired
-An object which can be used to unregister the listener.
+ * priority - optional - see events.on() for more information.
-#### Example:
+### events.entityCreatePortal()
-The following code will print a message on screen every time a block is broken in the game
+#### Parameters
-```javascript
-events.on( 'block.BlockBreakEvent', function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
-} );
-```
+ * callback - A function which is called whenever the [entity.EntityCreatePortalEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityCreatePortalEvent.html) is fired
-To handle an event only once and unregister from further events...
+ * priority - optional - see events.on() for more information.
-```javascript
-events.on( 'block.BlockBreakEvent', function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
- this.unregister();
-} );
-```
+### events.sheepRegrowWool()
-The `this` keyword when used inside the callback function refers to
-the Listener object created by ScriptCraft. It has a single method
-`unregister()` which can be used to stop listening. This is the same
-object which is returned by the `events.on()` function.
+#### Parameters
-To unregister a listener *outside* of the listener function...
+ * callback - A function which is called whenever the [entity.SheepRegrowWoolEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/SheepRegrowWoolEvent.html) is fired
-```javascript
-var myBlockBreakListener = events.on( 'block.BlockBreakEvent', function( evt ) { ... } );
-...
-myBlockBreakListener.unregister();
-```
+ * priority - optional - see events.on() for more information.
-To listen for events using a full class name as the `eventName` parameter...
+### events.entityRegainHealth()
-```javascript
-events.on( org.bukkit.event.block.BlockBreakEvent, function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
-} );
-```
+#### Parameters
-[buk2]: http://wiki.bukkit.org/Event_API_Reference
-[buk]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/event/Event.html
+ * callback - A function which is called whenever the [entity.EntityRegainHealthEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityRegainHealthEvent.html) is fired
-## console global variable
+ * priority - optional - see events.on() for more information.
-ScriptCraft provides a `console` global variable with the followng methods...
+### events.entityInteract()
- * log()
- * info()
- * warn()
- * error()
+#### Parameters
-The ScriptCraft console methods work like the [Web API implementation][webcons].
+ * callback - A function which is called whenever the [entity.EntityInteractEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityInteractEvent.html) is fired
-### Example
+ * priority - optional - see events.on() for more information.
- console.log('Hello %s', 'world');
+### events.potionSplash()
-Basic variable substitution is supported (ScriptCraft's implementation
-of console uses the Bukkit Plugin [Logger][lgr] under the hood and
-uses [java.lang.String.format()][strfmt] for variable
-substitution. All output will be sent to the server console (not
-in-game).
+#### Parameters
-### Using string substitutions
+ * callback - A function which is called whenever the [entity.PotionSplashEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/PotionSplashEvent.html) is fired
-ScriptCraft uses Java's [String.format()][strfmt] so any string substitution identifiers supported by
-`java.lang.String.format()` are supported (e.g. %s , %d etc).
+ * priority - optional - see events.on() for more information.
- for (var i=0; i<5; i++) {
- console.log("Hello, %s. You've called me %d times.", "Bob", i+1);
- }
+### events.entityTarget()
-[lgr]: http://jd.bukkit.org/beta/apidocs/org/bukkit/plugin/PluginLogger.html
-[strfmt]: http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)
-[webcons]: https://developer.mozilla.org/en-US/docs/Web/API/console
+#### Parameters
-## Events Helper Module
-The Events helper module provides a suite of functions - one for each possible event.
-For example, the events.blockBreak() function is just a wrapper function which calls events.on(org.bukkit.event.block.BlockBreakEvent, callback, priority)
-This module is a convenience wrapper for easily adding new event handling functions in Javascript.
-At the in-game or server-console prompt, players/admins can type `events.` and use TAB completion
-to choose from any of the approx. 160 different event types to listen to.
+ * callback - A function which is called whenever the [entity.EntityTargetEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityTargetEvent.html) is fired
-### Usage
+ * priority - optional - see events.on() for more information.
- events.blockBreak(function(evt){
- evt.player.sendMessage("You broke a block!");
- });
+### events.entityBreakDoor()
-... which is just a shorter and less error-prone way of writing ...
+#### Parameters
- events.on("block.BlockBreakEvent",function(evt){
- evt.player.sendMessage("You broke a block!");
- });
+ * callback - A function which is called whenever the [entity.EntityBreakDoorEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityBreakDoorEvent.html) is fired
-The crucial difference is that the events module now has functions for each
-of the built-in events. The functions are accessible via tab-completion so will help
-beginning programmers to explore the events at the server console window.
+ * priority - optional - see events.on() for more information.
-### events.worldUnload()
+### events.entityUnleash()
#### Parameters
- * callback - A function which is called whenever the world.WorldUnloadEvent event is fired
+ * callback - A function which is called whenever the [entity.EntityUnleashEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityUnleashEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.worldLoad()
+### events.entityDamageByBlock()
#### Parameters
- * callback - A function which is called whenever the world.WorldLoadEvent event is fired
+ * callback - A function which is called whenever the [entity.EntityDamageByBlockEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDamageByBlockEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.chunkLoad()
+### events.entityTeleport()
#### Parameters
- * callback - A function which is called whenever the world.ChunkLoadEvent event is fired
+ * callback - A function which is called whenever the [entity.EntityTeleportEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityTeleportEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.chunkPopulate()
+### events.itemDespawn()
#### Parameters
- * callback - A function which is called whenever the world.ChunkPopulateEvent event is fired
+ * callback - A function which is called whenever the [entity.ItemDespawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ItemDespawnEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.portalCreate()
+### events.explosionPrime()
#### Parameters
- * callback - A function which is called whenever the world.PortalCreateEvent event is fired
+ * callback - A function which is called whenever the [entity.ExplosionPrimeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/ExplosionPrimeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.spawnChange()
+### events.entityDamageByEntity()
#### Parameters
- * callback - A function which is called whenever the world.SpawnChangeEvent event is fired
+ * callback - A function which is called whenever the [entity.EntityDamageByEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/entity/EntityDamageByEntityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.chunkUnload()
+### events.portalCreate()
#### Parameters
- * callback - A function which is called whenever the world.ChunkUnloadEvent event is fired
+ * callback - A function which is called whenever the [world.PortalCreateEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/PortalCreateEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.worldInit()
+### events.worldSave()
#### Parameters
- * callback - A function which is called whenever the world.WorldInitEvent event is fired
+ * callback - A function which is called whenever the [world.WorldSaveEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/WorldSaveEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.horseJump()
+### events.worldLoad()
#### Parameters
- * callback - A function which is called whenever the entity.HorseJumpEvent event is fired
+ * callback - A function which is called whenever the [world.WorldLoadEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/WorldLoadEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityCombust()
+### events.chunkLoad()
#### Parameters
- * callback - A function which is called whenever the entity.EntityCombustEvent event is fired
+ * callback - A function which is called whenever the [world.ChunkLoadEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/ChunkLoadEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityRegainHealth()
+### events.chunkPopulate()
#### Parameters
- * callback - A function which is called whenever the entity.EntityRegainHealthEvent event is fired
+ * callback - A function which is called whenever the [world.ChunkPopulateEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/ChunkPopulateEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.playerLeashEntity()
+### events.chunkUnload()
#### Parameters
- * callback - A function which is called whenever the entity.PlayerLeashEntityEvent event is fired
+ * callback - A function which is called whenever the [world.ChunkUnloadEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/ChunkUnloadEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.pigZap()
+### events.structureGrow()
#### Parameters
- * callback - A function which is called whenever the entity.PigZapEvent event is fired
+ * callback - A function which is called whenever the [world.StructureGrowEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/StructureGrowEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.itemDespawn()
+### events.worldInit()
#### Parameters
- * callback - A function which is called whenever the entity.ItemDespawnEvent event is fired
+ * callback - A function which is called whenever the [world.WorldInitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/WorldInitEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityTarget()
+### events.spawnChange()
#### Parameters
- * callback - A function which is called whenever the entity.EntityTargetEvent event is fired
+ * callback - A function which is called whenever the [world.SpawnChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/SpawnChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.slimeSplit()
+### events.worldUnload()
#### Parameters
- * callback - A function which is called whenever the entity.SlimeSplitEvent event is fired
+ * callback - A function which is called whenever the [world.WorldUnloadEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/world/WorldUnloadEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityChangeBlock()
+### events.playerShearEntity()
#### Parameters
- * callback - A function which is called whenever the entity.EntityChangeBlockEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerShearEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerShearEntityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityPortalEnter()
+### events.playerVelocity()
#### Parameters
- * callback - A function which is called whenever the entity.EntityPortalEnterEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerVelocityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerVelocityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.creeperPower()
+### events.playerBucketFill()
#### Parameters
- * callback - A function which is called whenever the entity.CreeperPowerEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerBucketFillEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerBucketFillEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityDeath()
+### events.playerFish()
#### Parameters
- * callback - A function which is called whenever the entity.EntityDeathEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerFishEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerFishEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.projectileHit()
+### events.playerGameModeChange()
#### Parameters
- * callback - A function which is called whenever the entity.ProjectileHitEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerGameModeChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerGameModeChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityTame()
+### events.playerCommandPreprocess()
#### Parameters
- * callback - A function which is called whenever the entity.EntityTameEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerCommandPreprocessEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerCommandPreprocessEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.potionSplash()
+### events.playerInteract()
#### Parameters
- * callback - A function which is called whenever the entity.PotionSplashEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerInteractEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerInteractEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.expBottle()
+### events.playerInteractAtEntity()
#### Parameters
- * callback - A function which is called whenever the entity.ExpBottleEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerInteractAtEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerInteractAtEntityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityExplode()
+### events.playerPortal()
#### Parameters
- * callback - A function which is called whenever the entity.EntityExplodeEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerPortalEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerPortalEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.creatureSpawn()
+### events.playerPreLogin()
#### Parameters
- * callback - A function which is called whenever the entity.CreatureSpawnEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerPreLoginEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerPreLoginEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.foodLevelChange()
+### events.playerMove()
#### Parameters
- * callback - A function which is called whenever the entity.FoodLevelChangeEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerMoveEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerMoveEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityInteract()
+### events.playerQuit()
#### Parameters
- * callback - A function which is called whenever the entity.EntityInteractEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerQuitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerQuitEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityCreatePortal()
+### events.playerToggleSneak()
#### Parameters
- * callback - A function which is called whenever the entity.EntityCreatePortalEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerToggleSneakEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerToggleSneakEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.sheepRegrowWool()
+### events.playerPickupArrow()
#### Parameters
- * callback - A function which is called whenever the entity.SheepRegrowWoolEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerPickupArrowEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerPickupArrowEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.explosionPrime()
+### events.playerBedLeave()
#### Parameters
- * callback - A function which is called whenever the entity.ExplosionPrimeEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerBedLeaveEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerBedLeaveEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityUnleash()
+### events.playerChangedMainHand()
#### Parameters
- * callback - A function which is called whenever the entity.EntityUnleashEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerChangedMainHandEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerChangedMainHandEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityShootBow()
+### events.playerEditBook()
#### Parameters
- * callback - A function which is called whenever the entity.EntityShootBowEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerEditBookEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerEditBookEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.projectileLaunch()
+### events.playerAnimation()
#### Parameters
- * callback - A function which is called whenever the entity.ProjectileLaunchEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerAnimationEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerAnimationEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.itemSpawn()
+### events.playerResourcePackStatus()
#### Parameters
- * callback - A function which is called whenever the entity.ItemSpawnEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerResourcePackStatusEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerResourcePackStatusEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.sheepDyeWool()
+### events.playerItemDamage()
#### Parameters
- * callback - A function which is called whenever the entity.SheepDyeWoolEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerItemDamageEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerItemDamageEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityTeleport()
+### events.playerBucketEmpty()
#### Parameters
- * callback - A function which is called whenever the entity.EntityTeleportEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerBucketEmptyEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerBucketEmptyEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockFade()
+### events.playerExpChange()
#### Parameters
- * callback - A function which is called whenever the block.BlockFadeEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerExpChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerExpChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockDamage()
+### events.asyncPlayerChat()
#### Parameters
- * callback - A function which is called whenever the block.BlockDamageEvent event is fired
+ * callback - A function which is called whenever the [player.AsyncPlayerChatEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/AsyncPlayerChatEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockPistonExtend()
+### events.playerItemBreak()
#### Parameters
- * callback - A function which is called whenever the block.BlockPistonExtendEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerItemBreakEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerItemBreakEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockExp()
+### events.playerUnleashEntity()
#### Parameters
- * callback - A function which is called whenever the block.BlockExpEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerUnleashEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerUnleashEntityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockGrow()
+### events.playerRespawn()
#### Parameters
- * callback - A function which is called whenever the block.BlockGrowEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerRespawnEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerRespawnEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockPistonRetract()
+### events.playerDropItem()
#### Parameters
- * callback - A function which is called whenever the block.BlockPistonRetractEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerDropItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerDropItemEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockDispense()
+### events.playerItemHeld()
#### Parameters
- * callback - A function which is called whenever the block.BlockDispenseEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerItemHeldEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerItemHeldEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.paintingPlace()
+### events.playerTeleport()
#### Parameters
- * callback - A function which is called whenever the painting.PaintingPlaceEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerTeleportEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerTeleportEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.lightningStrike()
+### events.playerInteractEntity()
#### Parameters
- * callback - A function which is called whenever the weather.LightningStrikeEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerInteractEntityEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerInteractEntityEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleEnter()
+### events.playerEggThrow()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleEnterEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerEggThrowEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerEggThrowEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleMove()
+### events.playerChat()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleMoveEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerChatEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerChatEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleCreate()
+### events.playerRegisterChannel()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleCreateEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerRegisterChannelEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerRegisterChannelEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.asyncPlayerPreLogin()
+### events.playerSwapHandItems()
#### Parameters
- * callback - A function which is called whenever the player.AsyncPlayerPreLoginEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerSwapHandItemsEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerSwapHandItemsEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.playerPreLogin()
+### events.playerChangedWorld()
#### Parameters
- * callback - A function which is called whenever the player.PlayerPreLoginEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerChangedWorldEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerChangedWorldEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventoryPickupItem()
+### events.playerLevelChange()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryPickupItemEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerLevelChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerLevelChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventoryMoveItem()
+### events.playerPickupItem()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryMoveItemEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerPickupItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerPickupItemEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.furnaceBurn()
+### events.asyncPlayerPreLogin()
#### Parameters
- * callback - A function which is called whenever the inventory.FurnaceBurnEvent event is fired
+ * callback - A function which is called whenever the [player.AsyncPlayerPreLoginEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/AsyncPlayerPreLoginEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventory()
+### events.playerUnregisterChannel()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerUnregisterChannelEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerUnregisterChannelEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.brew()
+### events.playerStatisticIncrement()
#### Parameters
- * callback - A function which is called whenever the inventory.BrewEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerStatisticIncrementEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerStatisticIncrementEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.furnaceSmelt()
+### events.playerBedEnter()
#### Parameters
- * callback - A function which is called whenever the inventory.FurnaceSmeltEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerBedEnterEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerBedEnterEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventoryClose()
+### events.playerKick()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryCloseEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerKickEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerKickEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventoryDrag()
+### events.playerToggleSprint()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryDragEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerToggleSprintEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerToggleSprintEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.inventoryClick()
+### events.playerAchievementAwarded()
#### Parameters
- * callback - A function which is called whenever the inventory.InventoryClickEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerAchievementAwardedEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerAchievementAwardedEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.hangingPlace()
+### events.playerJoin()
#### Parameters
- * callback - A function which is called whenever the hanging.HangingPlaceEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerJoinEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerJoinEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.hangingBreak()
+### events.playerToggleFlight()
#### Parameters
- * callback - A function which is called whenever the hanging.HangingBreakEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerToggleFlightEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerToggleFlightEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.worldSave()
+### events.playerArmorStandManipulate()
#### Parameters
- * callback - A function which is called whenever the world.WorldSaveEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerArmorStandManipulateEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerArmorStandManipulateEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.structureGrow()
+### events.playerLogin()
#### Parameters
- * callback - A function which is called whenever the world.StructureGrowEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerLoginEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerLoginEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityDamage()
+### events.playerItemConsume()
#### Parameters
- * callback - A function which is called whenever the entity.EntityDamageEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerItemConsumeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerItemConsumeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityPortal()
+### events.playerChatTabComplete()
#### Parameters
- * callback - A function which is called whenever the entity.EntityPortalEvent event is fired
+ * callback - A function which is called whenever the [player.PlayerChatTabCompleteEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerChatTabCompleteEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.entityPortalExit()
+### events.weatherChange()
#### Parameters
- * callback - A function which is called whenever the entity.EntityPortalExitEvent event is fired
+ * callback - A function which is called whenever the [weather.WeatherChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/weather/WeatherChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.signChange()
+### events.thunderChange()
#### Parameters
- * callback - A function which is called whenever the block.SignChangeEvent event is fired
+ * callback - A function which is called whenever the [weather.ThunderChangeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/weather/ThunderChangeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.leavesDecay()
+### events.lightningStrike()
#### Parameters
- * callback - A function which is called whenever the block.LeavesDecayEvent event is fired
+ * callback - A function which is called whenever the [weather.LightningStrikeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/weather/LightningStrikeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockRedstone()
+### events.vehicleMove()
#### Parameters
- * callback - A function which is called whenever the block.BlockRedstoneEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleMoveEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleMoveEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockCanBuild()
+### events.vehicleDamage()
#### Parameters
- * callback - A function which is called whenever the block.BlockCanBuildEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleDamageEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleDamageEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockBurn()
+### events.vehicleDestroy()
#### Parameters
- * callback - A function which is called whenever the block.BlockBurnEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleDestroyEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleDestroyEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockPhysics()
+### events.vehicleUpdate()
#### Parameters
- * callback - A function which is called whenever the block.BlockPhysicsEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleUpdateEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleUpdateEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockIgnite()
+### events.vehicleCreate()
#### Parameters
- * callback - A function which is called whenever the block.BlockIgniteEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleCreateEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleCreateEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.notePlay()
+### events.vehicleEntityCollision()
#### Parameters
- * callback - A function which is called whenever the block.NotePlayEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleEntityCollisionEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleEntityCollisionEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockPlace()
+### events.vehicleBlockCollision()
#### Parameters
- * callback - A function which is called whenever the block.BlockPlaceEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleBlockCollisionEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleBlockCollisionEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockFromTo()
+### events.vehicleEnter()
#### Parameters
- * callback - A function which is called whenever the block.BlockFromToEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleEnterEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleEnterEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockForm()
+### events.vehicleExit()
#### Parameters
- * callback - A function which is called whenever the block.BlockFormEvent event is fired
+ * callback - A function which is called whenever the [vehicle.VehicleExitEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/vehicle/VehicleExitEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.blockSpread()
+### events.prepareItemEnchant()
#### Parameters
- * callback - A function which is called whenever the block.BlockSpreadEvent event is fired
+ * callback - A function which is called whenever the [enchantment.PrepareItemEnchantEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/enchantment/PrepareItemEnchantEvent.html) is fired
* priority - optional - see events.on() for more information.
@@ -1571,2007 +3410,2418 @@ beginning programmers to explore the events at the server console window.
#### Parameters
- * callback - A function which is called whenever the enchantment.EnchantItemEvent event is fired
+ * callback - A function which is called whenever the [enchantment.EnchantItemEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/enchantment/EnchantItemEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.prepareItemEnchant()
+### events.pluginDisable()
#### Parameters
- * callback - A function which is called whenever the enchantment.PrepareItemEnchantEvent event is fired
+ * callback - A function which is called whenever the [server.PluginDisableEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/PluginDisableEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.paintingBreak()
+### events.serverListPing()
#### Parameters
- * callback - A function which is called whenever the painting.PaintingBreakEvent event is fired
+ * callback - A function which is called whenever the [server.ServerListPingEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/ServerListPingEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.weatherChange()
+### events.serviceRegister()
#### Parameters
- * callback - A function which is called whenever the weather.WeatherChangeEvent event is fired
+ * callback - A function which is called whenever the [server.ServiceRegisterEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/ServiceRegisterEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.thunderChange()
+### events.serverCommand()
#### Parameters
- * callback - A function which is called whenever the weather.ThunderChangeEvent event is fired
+ * callback - A function which is called whenever the [server.ServerCommandEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/ServerCommandEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleEntityCollision()
+### events.remoteServerCommand()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleEntityCollisionEvent event is fired
+ * callback - A function which is called whenever the [server.RemoteServerCommandEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/RemoteServerCommandEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleBlockCollision()
+### events.pluginEnable()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleBlockCollisionEvent event is fired
+ * callback - A function which is called whenever the [server.PluginEnableEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/PluginEnableEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleExit()
+### events.serviceUnregister()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleExitEvent event is fired
+ * callback - A function which is called whenever the [server.ServiceUnregisterEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/ServiceUnregisterEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleUpdate()
+### events.tabComplete()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleUpdateEvent event is fired
+ * callback - A function which is called whenever the [server.TabCompleteEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/TabCompleteEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleDamage()
+### events.mapInitialize()
#### Parameters
- * callback - A function which is called whenever the vehicle.VehicleDamageEvent event is fired
+ * callback - A function which is called whenever the [server.MapInitializeEvent event](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/server/MapInitializeEvent.html) is fired
* priority - optional - see events.on() for more information.
-### events.vehicleDestroy()
+## console global variable
-#### Parameters
+ScriptCraft provides a `console` global variable with the followng methods...
+
+ * log()
+ * info()
+ * warn()
+ * error()
+
+The ScriptCraft console methods work like the [Web API implementation][webcons].
+
+### Example
+
+ console.log('Hello %s', 'world');
+
+Basic variable substitution is supported (ScriptCraft's implementation
+of console uses the Bukkit Plugin [Logger][lgr] or Canary Plugin [Logman][cmlgr] under the hood and
+uses [java.lang.String.format()][strfmt] for variable
+substitution. All output will be sent to the server console (not
+in-game).
+
+### Using string substitutions
+
+ScriptCraft uses Java's [String.format()][strfmt] so any string substitution identifiers supported by
+`java.lang.String.format()` are supported (e.g. %s , %d etc).
+
+ for (var i=0; i<5; i++) {
+ console.log("Hello, %s. You've called me %d times.", "Bob", i+1);
+ }
+
+[lgr]: http://jd.bukkit.org/beta/apidocs/org/bukkit/plugin/PluginLogger.html
+[cmlgr]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/logger/Logman.html
+[strfmt]: http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)
+[webcons]: https://developer.mozilla.org/en-US/docs/Web/API/console
+
+## Drone Plugin
+
+The Drone is a convenience class for building.
+
+It uses a fluent interface which means all of the Drone's methods return `this` and can be chained together like so...
+
+ var theDrone = new Drone(self);
+ theDrone.up().left().box(blocks.oak).down().fwd(3).cylinder0(blocks.lava,8);
+
+### Constructing a Drone Object
+
+Drones can be created in any of the following ways...
+
+ 1. Calling any one of the methods listed below will return a Drone object. For example...
+
+ var d = box( blocks.oak )
+
+ ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all of the Drone class's methods are also global functions that return new Drone objects. This is short-hand for creating drones and is useful for playing around with Drones at the in-game command prompt. It's shorter than typing ...
+
+ var d = new Drone(self).box( blocks.oak )
+
+ ... All of the Drone's methods return `this` so you can chain operations together like this...
+
+ var d = box( blocks.oak )
+ .up()
+ .box( blocks.oak ,3,1,3)
+ .down()
+ .fwd(2)
+ .box( blocks.oak )
+ .turn()
+ .fwd(2)
+ .box( blocks.oak )
+ .turn()
+ .fwd(2)
+ .box( blocks.oak );
+
+ 2. Using the following form...
+
+ d = new Drone(self)
+
+ ...will create a new Drone taking the current player as the parameter. If the player's cross-hairs are pointing at a block at the time then, that block's location becomes the drone's starting point. If the cross-hairs are _not_ pointing at a block, then the drone's starting location will be 2 blocks directly in front of the player. TIP: Building always happens right and front of the drone's position...
+
+ Plan View:
+
+ ^
+ |
+ |
+ D---->
+
+ For convenience you can use a _corner stone_ to begin building. The corner stone should be located just above ground level. If the cross-hair is point at or into ground level when you create a new Drone() with either a player or location given as a parameter, then building begins at the location the player was looking at or at the location. You can get around this by pointing at a 'corner stone' just above ground level or alternatively use the following statement...
+
+ d = new Drone(self).up();
+
+ ... which will move the drone up one block as soon as it's created.
+
+ 
+
+ 3. Or by using the following form...
+
+ d = new Drone(x,y,z,direction,world);
+
+ This will create a new Drone at the location you specified using x, y, z In minecraft, the X axis runs west to east and the Z axis runs north to south. The direction parameter says what direction you want the drone to face: 0 = east, 1 = south, 2 = west, 3 = north. If the direction parameter is omitted, the player's direction is used instead. Both the `direction` and `world` parameters are optional.
+
+ 4. Create a new Drone based on a Location object...
+
+ d = new Drone(location);
+
+ This is useful when you want to create a drone at a given `org.bukkit.Location` . The `Location` class is used throughout the bukkit API. For example, if you want to create a drone when a block is broken at the block's location you would do so like this...
- * callback - A function which is called whenever the vehicle.VehicleDestroyEvent event is fired
+ events.blockBreak( function( event ) {
+ var location = event.block.location;
+ var drone = new Drone(location);
+ // do more stuff with the drone here...
+ });
- * priority - optional - see events.on() for more information.
+#### Parameters
-### events.playerExpChange()
+ * Player : If a player reference is given as the sole parameter then the block the player was looking at will be used as the starting point for the drone. If the player was not looking at a block then the player's location will be used as the starting point. If a `Player` object is provided as a paramter then it should be the only parameter.
+ * location : *NB* If a `Location` object is provided as a parameter, then it should be the only parameter.
+ * x : The x coordinate of the Drone (x,y,z,direction and world are not needed if either a player or location parameter is provided)
+ * y : The y coordinate of the Drone
+ * z : The z coordinate of the Drone
+ * direction : The direction in which the Drone is facing. Possible values are 0 (east), 1 (south), 2 (west) or 3 (north)
+ * world : The world in which the drone is created.
+
+### Drone.box() method
-#### Parameters
+the box() method is a convenience method for building things. (For the more performance-oriented method - see cuboid)
- * callback - A function which is called whenever the player.PlayerExpChangeEvent event is fired
+#### parameters
- * priority - optional - see events.on() for more information.
+ * b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * w (optional - default 1) - the width of the structure
+ * h (optional - default 1) - the height of the structure
+ * d (optional - default 1) - the depth of the structure - NB this is not how deep underground the structure lies - this is how far away (depth of field) from the drone the structure will extend.
-### events.playerRespawn()
+#### Example
-#### Parameters
+To create a black structure 4 blocks wide, 9 blocks tall and 1 block long...
+
+ box(blocks.wool.black, 4, 9, 1);
- * callback - A function which is called whenever the player.PlayerRespawnEvent event is fired
+... or the following code does the same but creates a variable that can be used for further methods...
- * priority - optional - see events.on() for more information.
+ var drone = new Drone(self);
+ drone.box(blocks.wool.black, 4, 9, 1);
-### events.playerCommandPreprocess()
+
+
+### Drone.box0() method
-#### Parameters
+Another convenience method - this one creates 4 walls with no floor or ceiling.
- * callback - A function which is called whenever the player.PlayerCommandPreprocessEvent event is fired
+#### Parameters
- * priority - optional - see events.on() for more information.
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * width (optional - default 1) - the width of the structure
+ * height (optional - default 1) - the height of the structure
+ * length (optional - default 1) - the length of the structure - how far
+ away (depth of field) from the drone the structure will extend.
-### events.playerPickupItem()
+#### Example
-#### Parameters
+To create a stone building with the insided hollowed out 7 wide by 3 tall by 6 long...
- * callback - A function which is called whenever the player.PlayerPickupItemEvent event is fired
+ box0( blocks.stone, 7, 3, 6);
- * priority - optional - see events.on() for more information.
+
+
+### Drone.boxa() method
-### events.playerInventory()
+Construct a cuboid using an array of blocks. As the drone moves first along the width axis, then the height (y axis) then the length, each block is picked from the array and placed.
-#### Parameters
+#### Parameters
- * callback - A function which is called whenever the player.PlayerInventoryEvent event is fired
+ * blocks - An array of blocks - each block in the array will be placed in turn.
+ * width
+ * height
+ * length
- * priority - optional - see events.on() for more information.
+#### Example
-### events.playerFish()
+Construct a rainbow-colored road 100 blocks long...
-#### Parameters
+ var rainbowColors = [blocks.wool.red, blocks.wool.orange, blocks.wool.yellow, blocks.wool.lime,
+ blocks.wool.lightblue, blocks.wool.blue, blocks.wool.purple];
+
+ boxa(rainbowColors,7,1,30);
- * callback - A function which is called whenever the player.PlayerFishEvent event is fired
+
- * priority - optional - see events.on() for more information.
+### Chaining
-### events.playerBedEnter()
+All of the Drone methods return a Drone object, which means methods can be 'chained' together so instead of writing this...
-#### Parameters
+ drone = new Drone( self );
+ drone.fwd( 3 );
+ drone.left( 2 );
+ drone.box( blocks.grass ); // create a grass block
+ drone.up();
+ drone.box( blocks.grass ); // create another grass block
+ drone.down();
- * callback - A function which is called whenever the player.PlayerBedEnterEvent event is fired
+...you could simply write ...
+
+ var drone = new Drone(self).fwd(3).left(2).box(blocks.grass).up().box(blocks.grass).down();
- * priority - optional - see events.on() for more information.
+... since each Drone method is also a global function that constructs a drone if none is supplied, you can shorten even further to just...
+
+ fwd(3).left(2).box(blocks.grass).up().box(blocks.grass).down()
-### events.playerLogin()
+The Drone object uses a [Fluent Interface][fl] to make ScriptCraft scripts more concise and easier to write and read. Minecraft's in-game command prompt is limited to about 80 characters so chaining drone commands together means more can be done before hitting the command prompt limit. For complex building you should save your commands in a new script file and load it using /js load()
-#### Parameters
+[fl]: http://en.wikipedia.org/wiki/Fluent_interface
- * callback - A function which is called whenever the player.PlayerLoginEvent event is fired
+### Drone Properties
- * priority - optional - see events.on() for more information.
+ * x - The Drone's position along the west-east axis (x increases as you move east)
+ * y - The Drone's position along the vertical axis (y increses as you move up)
+ * z - The Drone's position along the north-south axis (z increases as you move south)
+ * dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north.
-### events.playerDropItem()
+### Extending Drone
-#### Parameters
+The Drone object can be easily extended - new buidling recipes/blueprints can be added and can become part of a Drone's chain using the *static* method `Drone.extend`.
- * callback - A function which is called whenever the player.PlayerDropItemEvent event is fired
+### Drone.extend() static method
- * priority - optional - see events.on() for more information.
+Use this method to add new methods (which also become chainable global functions) to the Drone object.
-### events.playerLevelChange()
+#### Parameters
-#### Parameters
+ * name - The name of the new method e.g. 'pyramid'.
+ * function - The method body.
- * callback - A function which is called whenever the player.PlayerLevelChangeEvent event is fired
+Alternatively if you provide just a function as a parameter, then the function name will be used as the new method name. For example the following two approaches are both valid.
- * priority - optional - see events.on() for more information.
+#### Example 1 Using name and function as parameters
-### events.playerVelocity()
+ // submitted by [edonaldson][edonaldson]
+ var Drone = require('drone');
+ Drone.extend('pyramid', function( block, height ) {
+ this.chkpt('pyramid');
+ for ( var i = height; i > 0; i -= 2) {
+ this.box(block, i, 1, i).up().right().fwd();
+ }
+ return this.move('pyramid');
+ });
-#### Parameters
+#### Example 2 Using just a named function as a parameter
- * callback - A function which is called whenever the player.PlayerVelocityEvent event is fired
+ var Drone = require('drone');
+ function pyramid( block, height ) {
+ this.chkpt('pyramid');
+ for ( var i = height; i > 0; i -= 2) {
+ this.box(block, i, 1, i).up().right().fwd();
+ }
+ return this.move('pyramid');
+ }
+ Drone.extend( pyramid );
- * priority - optional - see events.on() for more information.
+Once the method is defined (it can be defined in a new pyramid.js file) it can be used like so...
-### events.playerInteract()
+ var d = new Drone(self);
+ d.pyramid(blocks.brick.stone, 12);
-#### Parameters
+... or simply ...
- * callback - A function which is called whenever the player.PlayerInteractEvent event is fired
+ pyramid(blocks.brick.stone, 12);
- * priority - optional - see events.on() for more information.
+[edonaldson]: https://github.com/edonaldson
-### events.playerQuit()
+### Drone Constants
-#### Parameters
+#### Drone.PLAYER_STAIRS_FACING
- * callback - A function which is called whenever the player.PlayerQuitEvent event is fired
+An array which can be used when constructing stairs facing in the Drone's direction...
- * priority - optional - see events.on() for more information.
+ var d = new Drone(self);
+ d.box(blocks.stairs.oak + ':' + Drone.PLAYER_STAIRS_FACING[d.dir]);
-### events.playerChatTabComplete()
+... will construct a single oak stair block facing the drone.
-#### Parameters
+#### Drone.PLAYER_SIGN_FACING
- * callback - A function which is called whenever the player.PlayerChatTabCompleteEvent event is fired
+An array which can be used when placing signs so they face in a given direction. This is used internally by the Drone.sign() method. It should also be used for placing any of the following blocks...
- * priority - optional - see events.on() for more information.
+ * chest
+ * ladder
+ * furnace
+ * dispenser
-### events.playerEggThrow()
+By default, chests, dispensers, signs, ladders and furnaces are placed facing towards the drone so to place a chest facing the Drone just use:
-#### Parameters
+ drone.box( blocks.chest );
- * callback - A function which is called whenever the player.PlayerEggThrowEvent event is fired
+To place a chest facing _away_ from the Drone:
- * priority - optional - see events.on() for more information.
+ drone.box( blocks.chest + ':' + Drone.PLAYER_SIGN_FACING[(drone.dir + 2) % 4]);
-### events.playerChat()
+#### Drone.PLAYER_TORCH_FACING
-#### Parameters
+Used when placing torches. By default torches will be placed facing up. If you want to place a torch so that it faces towards the drone:
- * callback - A function which is called whenever the player.PlayerChatEvent event is fired
+ drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[drone.dir]);
- * priority - optional - see events.on() for more information.
+If you want to place a torch so it faces _away_ from the drone:
-### events.playerAchievementAwarded()
+ drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[(drone.dir + 2) % 4]);
-#### Parameters
+#### Drone.MAX_SIDE
- * callback - A function which is called whenever the player.PlayerAchievementAwardedEvent event is fired
+Specifies the maximum length (in any dimension) when calling the Drone.cuboidX (box) method.
+The default value is 1,000 blocks.
- * priority - optional - see events.on() for more information.
+If you see an error message in the console `Build too big!` It's because the width, height or length paramete was greater than the Drone.MAX_SIDE value.
-### events.playerBedLeave()
+#### Drone.MAX_VOLUME
-#### Parameters
+Specifies the maximum value for any call to Drone.cuboidX (box) method.
+The default value is 1,000,000 (1 million) blocks.
- * callback - A function which is called whenever the player.PlayerBedLeaveEvent event is fired
+If the volume (width X height X length) of any single call to the Drone.cuboidX() method exceeds this value, you will see an error message in the console `Build too big!` .
- * priority - optional - see events.on() for more information.
+The values of both the `Drone.MAX_SiDE` and `Drone.MAX_VOLUME` variables _can_ be overridden but it's not recommended.
-### events.playerChannel()
+### Drone.times() Method
-#### Parameters
+The `times()` method makes building multiple copies of buildings
+easy. It's possible to create rows or grids of buildings without
+resorting to `for` or `while` loops.
- * callback - A function which is called whenever the player.PlayerChannelEvent event is fired
+#### Parameters
- * priority - optional - see events.on() for more information.
+ * numTimes : The number of times you want to repeat the preceding statements.
-### events.playerStatisticIncrement()
+#### Limitation
-#### Parameters
+For now, don't use `times()` inside a Drone method implementation – only use it at the in-game prompt as a short-hand workaround for loops.
- * callback - A function which is called whenever the player.PlayerStatisticIncrementEvent event is fired
+#### Example
- * priority - optional - see events.on() for more information.
+Say you want to do the same thing over and over. You have a couple of options:
-### events.playerToggleSprint()
+ * You can use a `for` loop …
-#### Parameters
+ d = new Drone(); for ( var i = 0; i < 4; i++ ) { d.cottage().right(8); }
- * callback - A function which is called whenever the player.PlayerToggleSprintEvent event is fired
+While this will fit on the in-game prompt, it's awkward. You need to
+declare a new Drone object first, then write a `for` loop to create the
+4 cottages. It's also error prone – even the `for` loop is too much
+syntax for what should really be simple.
- * priority - optional - see events.on() for more information.
+ * You can use a `while` loop …
+
+ d = new Drone(); var i=4; while (i--) { d.cottage().right(8); }
-### events.playerInteractEntity()
+… which is slightly shorter but still too much syntax. Each of the
+above statements is fine for creating a 1-dimensional array of
+structures. But what if you want to create a 2-dimensional or
+3-dimensional array of structures? Enter the `times()` method.
-#### Parameters
+The `times()` method lets you repeat commands in a chain any number of
+times. So to create 4 cottages in a row you would use the following
+statement:
- * callback - A function which is called whenever the player.PlayerInteractEntityEvent event is fired
+ cottage().right(8).times(4);
- * priority - optional - see events.on() for more information.
+… which will build a cottage, then move right 8 blocks, then do it
+again 4 times over so that at the end you will have 4 cottages in a
+row. What's more, the `times()` method can be called more than once in
+a chain. So if you wanted to create a *grid* of 20 houses ( 4 x 5 ),
+you would do so using the following statement:
-### events.playerEditBook()
+ cottage().right(8).times(4).fwd(8).left(32).times(5);
-#### Parameters
+… breaking it down …
- * callback - A function which is called whenever the player.PlayerEditBookEvent event is fired
+ 1. The first 3 calls in the chain ( `cottage()`, `right(8)`, `times(4)` ) build a single row of 4 cottages.
- * priority - optional - see events.on() for more information.
+ 2. The last 3 calls in the chain ( `fwd(8)`, `left(32)`, `times(5)` ) move the drone forward 8 then left 32 blocks (4 x 8) to return to the original X coordinate, then everything in the chain is repeated again 5 times so that in the end, we have a grid of 20 cottages, 4 x 5. Normally this would require a nested loop but the `times()` method does away with the need for loops when repeating builds.
-### events.playerKick()
+Another example: This statement creates a row of trees 2 by 3:
-#### Parameters
+ oak().right(10).times(2).left(20).fwd(10).times(3)
- * callback - A function which is called whenever the player.PlayerKickEvent event is fired
+… You can see the results below.
- * priority - optional - see events.on() for more information.
+
-### events.playerItemHeld()
+### Drone.arc() method
-#### Parameters
+The arc() method can be used to create 1 or more 90 degree arcs in the
+horizontal or vertical planes. This method is called by cylinder() and
+cylinder0() and the sphere() and sphere0() methods.
- * callback - A function which is called whenever the player.PlayerItemHeldEvent event is fired
+#### Parameters
- * priority - optional - see events.on() for more information.
+arc() takes a single parameter - an object with the following named properties...
-### events.playerItemConsume()
+ * radius - The radius of the arc.
+ * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv].
+ * meta - The metadata value. See [Data Values][dv].
+ * orientation (default: 'horizontal' ) - the orientation of the arc - can be 'vertical' or 'horizontal'.
+ * stack (default: 1 ) - the height or length of the arc (depending on the orientation - if orientation is horizontal then this parameter refers to the height, if vertical then it refers to the length ).
+ * strokeWidth (default: 1 ) - the width of the stroke (how many blocks) - if drawing nested arcs it's usually a good idea to set strokeWidth to at least 2 so that there are no gaps between each arc. The arc method uses a [bresenham algorithm][bres] to plot points along the circumference.
+ * fill - If true (or present) then the arc will be filled in.
+ * quadrants (default: `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An object with 4 properties indicating which of the 4 quadrants of a circle to draw. If the quadrants property is absent then all 4 quadrants are drawn.
-#### Parameters
+#### Examples
- * callback - A function which is called whenever the player.PlayerItemConsumeEvent event is fired
+To draw a 1/4 circle (top right quadrant only) with a radius of 10 and
+stroke width of 2 blocks ...
- * priority - optional - see events.on() for more information.
+ arc({blockType: blocks.iron,
+ meta: 0,
+ radius: 10,
+ strokeWidth: 2,
+ quadrants: { topright: true },
+ orientation: 'vertical',
+ stack: 1,
+ fill: false
+ } );
-### events.playerGameModeChange()
+
-#### Parameters
+[bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+[dv]: http://www.minecraftwiki.net/wiki/Data_values
- * callback - A function which is called whenever the player.PlayerGameModeChangeEvent event is fired
+### Drone.bed() method
- * priority - optional - see events.on() for more information.
+Creates a bed. The foot of the bed will be at the drone's location and
+the head of the bed will extend away from the drone.
-### events.playerItemBreak()
+#### Example
+To create a bed at the in-game prompt, look at a block then type:
-#### Parameters
+```javascript
+/js bed()
+```
- * callback - A function which is called whenever the player.PlayerItemBreakEvent event is fired
+Like most Drone methods, this returns the drone so it can be chained like so:
- * priority - optional - see events.on() for more information.
+```javascript
+this
+ .fwd(3)
+ .bed()
+ .back(3)
+```
+### Drone.blocktype() method
-### events.playerToggleFlight()
+Creates the text out of blocks. Useful for large-scale in-game signs.
-#### Parameters
+#### Parameters
+
+ * message - The message to create - (use `\n` for newlines)
+ * foregroundBlock (default: black wool) - The block to use for the foreground
+ * backgroundBlock (default: none) - The block to use for the background
- * callback - A function which is called whenever the player.PlayerToggleFlightEvent event is fired
+#### Example
- * priority - optional - see events.on() for more information.
+To create a 2-line high message using glowstone...
-### events.playerAnimation()
+ blocktype('Hello\nWorld', blocks.glowstone);
-#### Parameters
+![blocktype example][imgbt1]
- * callback - A function which is called whenever the player.PlayerAnimationEvent event is fired
+[imgbt1]: img/blocktype1.png
- * priority - optional - see events.on() for more information.
+### Copy & Paste using Drone
-### events.asyncPlayerChat()
+A drone can be used to copy and paste areas of the game world.
-#### Parameters
+#### Deprecated
+As of January 10 2015 the copy-paste functions in Drone are no longer
+supported. Copy/Paste is:
- * callback - A function which is called whenever the player.AsyncPlayerChatEvent event is fired
+1. Difficult to do correctly in a way which works for both Minecraft 1.7 and 1.8
+ due to how blocks changed in 1.8
+2. Not aligned with the purpose of ScriptCraft's Drone module which is to provide
+ a simple set of functions for scripting and in-game building.
- * priority - optional - see events.on() for more information.
+### Drone.copy() method
-### events.playerMove()
+Copies an area so it can be pasted elsewhere. The name can be used for
+pasting the copied area elsewhere...
-#### Parameters
+#### Parameters
- * callback - A function which is called whenever the player.PlayerMoveEvent event is fired
+ * name - the name to be given to the copied area (used by `paste`)
+ * width - the width of the area to copy
+ * height - the height of the area to copy
+ * length - the length of the area (extending away from the drone) to copy
- * priority - optional - see events.on() for more information.
+#### Example
-### events.playerTeleport()
+ drone.copy('somethingCool',10,5,10 ).right(12 ).paste('somethingCool' );
-#### Parameters
+### Drone.paste() method
- * callback - A function which is called whenever the player.PlayerTeleportEvent event is fired
+Pastes a copied area to the current location.
- * priority - optional - see events.on() for more information.
+#### Example
-### events.playerBucketFill()
+To copy a 10x5x10 area (using the drone's coordinates as the starting
+point) into memory. the copied area can be referenced using the name
+'somethingCool'. The drone moves 12 blocks right then pastes the copy.
-#### Parameters
+ drone.copy('somethingCool',10,5,10 )
+ .right(12 )
+ .paste('somethingCool' );
- * callback - A function which is called whenever the player.PlayerBucketFillEvent event is fired
+### Drone.cylinder() method
- * priority - optional - see events.on() for more information.
+A convenience method for building cylinders. Building begins radius blocks to the right and forward.
-### events.playerJoin()
+#### Parameters
-#### Parameters
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * radius
+ * height
- * callback - A function which is called whenever the player.PlayerJoinEvent event is fired
+#### Example
- * priority - optional - see events.on() for more information.
+To create a cylinder of Iron 7 blocks in radius and 1 block high...
-### events.playerShearEntity()
+ cylinder(blocks.iron, 7 , 1);
-#### Parameters
+
- * callback - A function which is called whenever the player.PlayerShearEntityEvent event is fired
+### Drone.cylinder0() method
- * priority - optional - see events.on() for more information.
+A version of cylinder that hollows out the middle.
-### events.playerToggleSneak()
+#### Example
-#### Parameters
+To create a hollow cylinder of Iron 7 blocks in radius and 1 block high...
- * callback - A function which is called whenever the player.PlayerToggleSneakEvent event is fired
+ cylinder0(blocks.iron, 7, 1);
- * priority - optional - see events.on() for more information.
+
-### events.playerChangedWorld()
+### Drone.door() method
-#### Parameters
+create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created.
- * callback - A function which is called whenever the player.PlayerChangedWorldEvent event is fired
+#### Parameters
- * priority - optional - see events.on() for more information.
+ * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
-### events.serverCommand()
+#### Example
-#### Parameters
+To create a wooden door at the crosshairs/drone's location...
- * callback - A function which is called whenever the server.ServerCommandEvent event is fired
+ var drone = new Drone(self);
+ drone.door();
- * priority - optional - see events.on() for more information.
+To create an iron door...
-### events.remoteServerCommand()
+ drone.door( blocks.door_iron );
-#### Parameters
+
- * callback - A function which is called whenever the server.RemoteServerCommandEvent event is fired
+### Drone.door_iron() method
- * priority - optional - see events.on() for more information.
+create an Iron door.
-### events.mapInitialize()
+### Drone.door2() method
-#### Parameters
+Create double doors (left and right side)
- * callback - A function which is called whenever the server.MapInitializeEvent event is fired
+#### Parameters
- * priority - optional - see events.on() for more information.
+ * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
-### events.serviceRegister()
+#### Example
-#### Parameters
+To create double-doors at the cross-hairs/drone's location...
- * callback - A function which is called whenever the server.ServiceRegisterEvent event is fired
+ drone.door2();
- * priority - optional - see events.on() for more information.
+
-### events.serverListPing()
+### Drone.door2_iron() method
-#### Parameters
+Create double iron doors
- * callback - A function which is called whenever the server.ServerListPingEvent event is fired
+### Drone.firework() method
- * priority - optional - see events.on() for more information.
+Launches a firework at the drone's location.
-### events.serviceUnregister()
+#### Example
-#### Parameters
+To launch a firework:
- * callback - A function which is called whenever the server.ServiceUnregisterEvent event is fired
+ var drone = new Drone(self);
+ drone.firework();
- * priority - optional - see events.on() for more information.
+### Drone.garden() method
-### events.prepareItemCraft()
+places random flowers and long grass (similar to the effect of placing bonemeal on grass)
-#### Parameters
+#### Parameters
- * callback - A function which is called whenever the inventory.PrepareItemCraftEvent event is fired
+ * width - the width of the garden
+ * length - how far from the drone the garden extends
- * priority - optional - see events.on() for more information.
+#### Example
-### events.inventoryOpen()
+To create a garden 10 blocks wide by 5 blocks long...
-#### Parameters
+ garden(10,5);
- * callback - A function which is called whenever the inventory.InventoryOpenEvent event is fired
+
- * priority - optional - see events.on() for more information.
+### Drone.ladder() method
-### events.playerBucketEmpty()
+Creates a ladder extending skyward.
-#### Parameters
+#### Parameters
- * callback - A function which is called whenever the player.PlayerBucketEmptyEvent event is fired
+ * height (optional - default 1)
- * priority - optional - see events.on() for more information.
+#### Example
-### events.playerPortal()
+To create a ladder extending 10 blocks high:
-#### Parameters
+ var drone = new Drone(self);
+ drone.ladder(10)
- * callback - A function which is called whenever the player.PlayerPortalEvent event is fired
+At the in-game prompt, look at a block and then type:
- * priority - optional - see events.on() for more information.
+ /js ladder(10)
-### events.pluginDisable()
+A ladder 10 blocks high will be created at the point you were looking at.
-#### Parameters
+#### Since
+##### 3.0.3
+### Drone Movement
- * callback - A function which is called whenever the server.PluginDisableEvent event is fired
+Drones can move freely in minecraft's 3-D world. You control the
+Drone's movement using any of the following methods..
- * priority - optional - see events.on() for more information.
+ * up()
+ * down()
+ * left()
+ * right()
+ * fwd()
+ * back()
+ * turn()
-### events.pluginEnable()
+... Each of these methods takes a single optional parameter
+`numBlocks` - the number of blocks to move in the given direction. If
+no parameter is given, the default is 1.
-#### Parameters
+To change direction use the `turn()` method which also takes a single
+optional parameter (numTurns) - the number of 90 degree turns to
+make. Turns are always clock-wise. If the drone is facing north, then
+drone.turn() will make the turn face east. If the drone is facing east
+then drone.turn(2) will make the drone turn twice so that it is facing
+west.
- * callback - A function which is called whenever the server.PluginEnableEvent event is fired
+### Drone Positional Info
- * priority - optional - see events.on() for more information.
+ * getLocation() - Returns a native Java Location object for the drone
-## Blocks Module
+### Drone Markers
-You hate having to lookup [Data Values][dv] when you use ScriptCraft's
-Drone() functions. So do I. So I created this blocks object which is
-a helper object for use in construction.
+Markers are useful when your Drone has to do a lot of work. You can
+set a check-point and return to the check-point using the move()
+method. If your drone is about to undertake a lot of work -
+e.g. building a road, skyscraper or forest you should set a
+check-point before doing so if you want your drone to return to its
+current location.
-### Examples
+A 'start' checkpoint is automatically created when the Drone is first created.
- box( blocks.oak ); // creates a single oak wood block
- box( blocks.sand, 3, 2, 1 ); // creates a block of sand 3 wide x 2 high x 1 long
- box( blocks.wool.green, 2 ); // creates a block of green wool 2 blocks wide
+Markers are created and returned to using the followng two methods...
-Color aliased properties that were a direct descendant of the blocks
-object are no longer used to avoid confusion with carpet and stained
-clay blocks. In addition, there's a convenience array `blocks.rainbow`
-which is an array of the 7 colors of the rainbow (or closest
-approximations).
+ * chkpt - Saves the drone's current location so it can be returned to later.
+ * move - moves the drone to a saved location. Alternatively you can provide a Java Location object or x,y,z and direction parameters.
-The blocks module is globally exported by the Drone module.
+#### Parameters
-## Fireworks Module
+ * name - the name of the checkpoint to save or return to.
-The fireworks module makes it easy to create fireworks using
-ScriptCraft. The module has a single function `firework` which takes
-a `org.bukkit.Location` as its 1 and only parameter.
+#### Example
-### Examples
+ drone.chkpt('town-square');
+ //
+ // the drone can now go off on a long excursion
+ //
+ for ( i = 0; i< 100; i++) {
+ drone.fwd(12).box(6);
+ }
+ //
+ // return to the point before the excursion
+ //
+ drone.move('town-square');
-The module also extends the `Drone` object adding a `firework` method
-so that fireworks can be created as a part of a Drone chain. For
-Example....
+### Drone.prism() method
- /js firework()
+Creates a prism. This is useful for roofs on houses.
-... creates a single firework, while ....
+#### Parameters
- /js firework().fwd(3).times(5)
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
+ Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * width - the width of the prism
+ * length - the length of the prism (will be 2 time its height)
-... creates 5 fireworks in a row. Fireworks have also been added as a
-possible option for the `arrow` module. To have a firework launch
-where an arrow strikes...
+#### Example
- /js arrows.firework()
+ prism(blocks.oak,3,12);
-To call the fireworks.firework() function directly, you must provide a
-location. For example...
+
- /js var fireworks = require('fireworks');
- /js fireworks.firework( self.location );
+### Drone.prism0() method
-
+A variation on `prism` which hollows out the inside of the prism. It
+uses the same parameters as `prism`.
-## Asynchronous Input Module
+### Drone.rand() method
-The `input` module provides a simple way to prompt players for input at the
-in-game prompt. In Javascript browser environments the `prompt()` function provides
-a way to block execution and ask the user for input. Execution is blocked until the user
-provides input using the modal dialog and clicks OK. Unfortunately Minecraft provides no
-equivalent modal dialog which can be used to gather player text input. The only way to gather text
-input from the player in Minecraft is to do so asynchronously. That is - a prompt message can be
-sent to the player but the player is not obliged to provide input immediately, nor does the program
-execution block until the player does so.
+rand takes either an array (if each blockid has the same chance of occurring) or an object where each property is a blockid and the value is it's weight (an integer)
-So ScriptCraft has no `prompt()` implementation because `prompt()` is a synchronous function and
-Minecraft's API provides no equivalent functions or classes which can be used to implement this synchronously.
-The Minecraft API does however have a 'Conversation' API which allows for prompting of the player and asynchronously gathering text input from the player.
+#### Example
-This new `input()` function is best illustrated by example. The following code is for a number-guessing game:
+place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked)
-```javascript
-var input = require('input');
-exports.numberguess = function(player){
- var randomNumber = Math.ceil(Math.random() * 10);
- input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, repeat ) {
- if ( guess == 'q'){
- return;
- }
- if ( +guess !== randomNumber ) {
- if (+guess < randomNumber ) {
- player.sendMessage('Too low - guess again');
- }
- if (+guess > randomNumber ) {
- player.sendMessage('Too high - guess again');
- }
- repeat();
- } else {
- player.sendMessage('You guessed correctly');
- }
- });
-};
-```
+ rand( [blocks.brick.stone, blocks.brick.mossy, blocks.brick.cracked ],w,d,h)
-The `input()` function takes 3 parameters, the player, a prompt message and a callback which will be invoked when the player has entered some text at the in-game command prompt.
-The callback is bound to an object which has the following properties:
+to place random blocks stone has a 50% chance of being picked,
- * sender : The player who input the text
- * value : The value of the text which has been input.
- * message: The message prompt.
- * repeat: A function which when invoked will repeat the original prompt. (this is for flow control)
+ var distribution = {};
+ distribution[ blocks.brick.stone ] = 5;
+ distribution[ blocks.brick.mossy ] = 3;
+ distribution[ blocks.brick.cracked ] = 2;
-The callback function as well as being bound to an object with the above properties (so you can use this.value inside your callback to get the value which has just been input), can also take the following parameters (in exact order):
+ rand( distribution, width, height, depth)
- * value
- * repeat
- * sender
+regular stone has a 50% chance, mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked.
-The `value` parameter will be the same as `this.value`, the `repeat` parameter will be the same as `this.repeat` and so on.
+### Drone.wallsign() method
-## Http Module
+Creates a wall sign (A sign attached to a wall)
-For handling http requests. Not to be confused with the more robust
-and functional 'http' module bundled with Node.js.
+#### Parameters
-### http.request() function
+ * message - can be a string or an array of strings
-The http.request() function will fetch a web address asynchronously (on a
-separate thread)and pass the URL's response to a callback function
-which will be executed synchronously (on the main thread). In this
-way, http.request() can be used to fetch web content without blocking the
-main thread of execution.
+#### Example
-#### Parameters
+ drone.wallsign(['Welcome','to','Scriptopia']);
- * request: The request details either a plain URL e.g. "http://scriptcraft.js/sample.json" or an object with the following properties...
+
- - url: The URL of the request.
- - method: Should be one of the standard HTTP methods, GET, POST, PUT, DELETE (defaults to GET).
- - params: A Javascript object with name-value pairs. This is for supplying parameters to the server.
+### Drone.signpost() method
- * callback: The function to be called when the Web request has completed. This function takes the following parameters...
- - responseCode: The numeric response code from the server. If the server did not respond with 200 OK then the response parameter will be undefined.
- - response: A string (if the response is of type text) or object containing the HTTP response body.
+Creates a free-standing signpost
+
+#### Parameters
+
+ * message - can be a string or an array of strings
#### Example
-The following example illustrates how to use http.request to make a request to a JSON web service and evaluate its response...
+ drone.signpost(['Hello','World']);
- var jsResponse;
- var http = require('./http/request');
- http.request("http://scriptcraftjs.org/sample.json",function(responseCode, responseBody){
- jsResponse = eval("(" + responseBody + ")");
- });
+
-... The following example illustrates a more complex use-case POSTing parameters to a CGI process on a server...
+### Drone.sign() method
- var http = require('./http/request');
- http.request(
- {
- url: 'http://pixenate.com/pixenate/pxn8.pl',
- method: 'POST',
- params: {script: '[]'}
- },
- function( responseCode, responseBody ) {
- var jsObj = eval('(' + responseBody + ')');
- });
+Deprecated: Use signpost() or wallsign() methods instead.
-## sc-mqtt module
+Signs must use block 63 (stand-alone signs) or 68 (signs on walls)
-This module provides a simple way to communicate with devices (such as Arduino)
-using the popular lightweight [MQTT protocol][mqtt].
+#### Parameters
-### Usage
+ * message - can be a string or an array of strings.
+ * block - can be 63 or 68
-This module can only be used if the separate `sc-mqtt.jar` file is
-present in the CraftBukkit classpath. To use this module, you should
-...
+#### Example
- 1. Download sc-mqtt.jar from
- 2. Save the file to the same directory where craftbukkit.jar resides.
- 3. Create a new batch file (windows-only) called
- craftbukkit-sc-mqtt.bat and edit it to include the following
- command...
+To create a free-standing sign...
- ```sh
- java -classpath sc-mqtt.jar;craftbukkit.jar org.bukkit.craftbukkit.Main
- ```
+ drone.sign(["Hello","World"], blocks.sign_post);
- If you're using Mac OS, create a new craftbukkit-sc-mqtt.command
- file and edit it (using TextWrangler or another text editor) ...
+
- ```sh
- java -classpath sc-mqtt.jar:craftbukkit.jar org.bukkit.craftbukkit.Main
- ```
+... to create a wall mounted sign...
- 4. Execute the craftbukkit-sc-mqtt batch file / command file to start
- Craftbukkit. You can now begin using this module to send and receive
- messages to/from a Net-enabled Arduino or any other device which uses
- the [MQTT protocol][mqtt]
-
- ```javascript
- var mqtt = require('sc-mqtt');
- // create a new client
- var client = mqtt.client( 'tcp://localhost:1883', 'uniqueClientId' );
- // connect to the broker
- client.connect( { keepAliveInterval: 15 } );
- // publish a message to the broker
- client.publish( 'minecraft', 'loaded' );
- // subscribe to messages on 'arduino' topic
- client.subscribe( 'arduino' );
- // do something when an incoming message arrives...
- client.onMessageArrived( function( topic, message ) {
- console.log( 'Message arrived: topic=' + topic + ', message=' + message );
- });
+ drone.sign(["Welcome","to","Scriptopia"], blocks.sign );
- ```
+
-The `sc-mqtt` module provides a very simple minimal wrapper around the
-[Eclipse Paho MQTT Version 3 Client][pahodocs] java-based MQTT
-library.
+### Drone.sphere() method
-[pahodocs]: http://pic.dhe.ibm.com/infocenter/wmqv7/v7r5/index.jsp?topic=/com.ibm.mq.javadoc.doc/WMQMQxrClasses/org/eclipse/paho/client/mqttv3/package-summary.html
-[mqtt]: http://mqtt.org/
+Creates a sphere.
-## Signs Module
+#### Parameters
+
+ * block - The block the sphere will be made of.
+ * radius - The radius of the sphere.
-The Signs Module can be used by plugin authors to create interactive
-signs - that is - signs which display a list of choices which can be
-changed by interacting (right-clicking) with the sign.
+#### Example
-### signs.menu() function
+To create a sphere of Iron with a radius of 10 blocks...
-This function is used to construct a new interactive menu on top of an
-existing sign in the game world.
+ sphere( blocks.iron, 10);
-#### Parameters
+
- * Label : A string which will be displayed in the topmost line of the
- sign. This label is not interactive.
- * options : An array of strings which can be selected on the sign by
- right-clicking/interacting.
- * callback : A function which will be called whenever a player
- interacts (changes selection) on a sign. This callback in turn
- takes as its parameter, an object with the following properties...
+Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
+server to be very busy for a couple of minutes while doing so.
- * player : The player who interacted with the sign.
- * sign : The [org.bukkit.block.Sign][buksign] which the player interacted with.
- * text : The text for the currently selected option on the sign.
- * number : The index of the currently selected option on the sign.
-
- * selectedIndex : optional: A number (starting at 0) indicating which
- of the options should be selected by default. 0 is the default.
+### Drone.sphere0() method
-#### Returns
-This function does not itself do much. It does however return a
-function which when invoked with a given
-[org.bukkit.block.Sign][buksign] object, will convert that sign into
-an interactive sign.
+Creates an empty sphere.
-#### Example: Create a sign which changes the time of day.
+#### Parameters
+
+ * block - The block the sphere will be made of.
+ * radius - The radius of the sphere.
-##### plugins/signs/time-of-day.js
-
-```javascript
-var utils = require('utils'),
- signs = require('signs');
+#### Example
-var onTimeChoice = function(event){
- var selectedIndex = event.number;
- // convert to Minecraft time 0 = Dawn, 6000 = midday, 12000 = dusk, 18000 = midnight
- var time = selectedIndex * 6000;
- event.player.location.world.setTime(time);
-};
+To create a sphere of Iron with a radius of 10 blocks...
-// signs.menu returns a function which can be called for one or more signs in the game.
-var convertToTimeMenu = signs.menu('Time of Day',
- ['Dawn', 'Midday', 'Dusk', 'Midnight'],
- onTimeChoice);
-
-exports.time_sign = function( player ){
- var sign = signs.getTargetedBy(player);
- if ( !sign ) {
- throw new Error('You must look at a sign');
- }
- convertToTimeMenu(sign);
-};
-```
+ sphere0( blocks.iron, 10);
-To use the above function at the in-game prompt, look at an existing
-sign and type...
+Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
+server to be very busy for a couple of minutes while doing so.
- /js time_sign(self);
+### Drone.hemisphere() method
-... and the sign you're looking at will become an interactive sign
-which changes the time each time you interact (right-click) with it.
+Creates a hemisphere. Hemispheres can be either north or south.
-### signs.getTargetedBy() function
+#### Parameters
-This function takes a [org.bukkit.entity.LivingEntity][bukle] as a
-parameter and returns a [org.bukkit.block.Sign][buksign] object which
-the entity has targeted. It is a utility function for use by plugin authors.
+ * block - the block the hemisphere will be made of.
+ * radius - the radius of the hemisphere
+ * northSouth - whether the hemisphere is 'north' or 'south'
#### Example
-```javascript
-var signs = require('signs'),
- utils = require('utils');
-var player = utils.player('tom1234');
-var sign = signs.getTargetedBy( player );
-if ( !sign ) {
- player.sendMessage('Not looking at a sign');
-}
-```
+To create a wood 'north' hemisphere with a radius of 7 blocks...
-[buksign]: http://jd.bukkit.org/dev/apidocs/org/bukkit/block/Sign.html
+ hemisphere(blocks.oak, 7, 'north');
-## Sounds Module
+
-This module is a simple wrapper around the Bukkit Sound class and provides
-a simpler way to play sounds. All of the org.bukkit.Sound Enum values are attached.
+### Drone.hemisphere0() method
-### Usage:
+Creates a hollow hemisphere. Hemispheres can be either north or south.
- var sounds = require('sounds');
- sounds.play( self, sounds.VILLAGER_NO , 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch
- sounds.play( self, sounds.VILLAGER_NO ); // same as previous statement
+#### Parameters
-The play() function takes either a Location object or any object which has a location.
-The volume parameter is in the range 0 to 1 and the pitch parameter is in the range 0 to 4.
-String class extensions
------------------------
-The following chat-formatting methods are added to the javascript String class..
+ * block - the block the hemisphere will be made of.
+ * radius - the radius of the hemisphere
+ * northSouth - whether the hemisphere is 'north' or 'south'
- * aqua()
- * black()
- * blue()
- * bold()
- * brightgreen()
- * darkaqua()
- * darkblue()
- * darkgray()
- * darkgreen()
- * purple()
- * darkpurple()
- * darkred()
- * gold()
- * gray()
- * green()
- * italic()
- * lightpurple()
- * indigo()
- * green()
- * red()
- * pink()
- * yellow()
- * white()
- * strike()
- * random()
- * magic()
- * underline()
- * reset()
+#### Example
-Example
--------
+To create a glass 'north' hemisphere with a radius of 20 blocks...
- /js var boldGoldText = "Hello World".bold().gold();
- /js self.sendMessage( boldGoldText );
+ hemisphere0(blocks.glass, 20, 'north');
-Hello World
+
-## Utilities Module
+### Drone.stairs() function
-The `utils` module is a storehouse for various useful utility
-functions which can be used by plugin and module authors. It contains
-miscellaneous utility functions and classes to help with programming.
+The stairs() function will build a flight of stairs
-### utils.player() function
+#### Parameters
-The utils.player() function will return a [bukkit Player][bkpl] object
-with the given name. This function takes a single parameter
-`playerName` which can be either a String or a [Player][bkpl] object -
-if it's a Player object, then the same object is returned. If it's a
-String, then it tries to find the player with that name.
+ * blockType - should be one of the following:
-#### Parameters
+ * blocks.stairs.oak
+ * blocks.stairs.cobblestone
+ * blocks.stairs.brick
+ * blocks.stairs.stone
+ * blocks.stairs.nether
+ * blocks.stairs.sandstone
+ * blocks.stairs.spruce
+ * blocks.stairs.birch
+ * blocks.stairs.jungle
+ * blocks.stairs.quartz
- * playerName : A String or Player object. If no parameter is provided
- then player() will try to return the `self` variable . It is
- strongly recommended to provide a parameter.
+ * width - The width of the staircase - default is 1
+ * height - The height of the staircase - default is 1
#### Example
-```javascript
-var utils = require('utils');
-var name = 'walterh';
-var player = utils.player(name);
-if ( player ) {
- player.sendMessage('Got ' + name);
-} else {
- console.log('No player named ' + name);
-}
-```
+To build an oak staircase 3 blocks wide and 5 blocks tall:
-[bkpl]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html
-[bkloc]: http://jd.bukkit.org/dev/apidocs/org/bukkit/Location.html
+ /js stairs(blocks.stairs.oak, 3, 5)
-### utils.locationToJSON() function
+Staircases do not have any blocks beneath them.
-utils.locationToJSON() returns a [org.bukkit.Location][bkloc] object in JSON form...
+### Drone Trees methods
- { world: 'world5',
- x: 56.9324,
- y: 103.9954,
- z: 43.1323,
- yaw: 0.0,
- pitch: 0.0
- }
+ * oak()
+ * spruce()
+ * birch()
+ * jungle()
-This can be useful if you write a plugin that needs to store location data since bukkit's Location object is a Java object which cannot be serialized to JSON by default.
+#### Example
-#### Parameters
-
- * location: An object of type [org.bukkit.Location][bkloc]
+To create 4 trees in a row, point the cross-hairs at the ground then type `/js ` and ...
-#### Returns
+ up( ).oak( ).right(8 ).spruce( ).right(8 ).birch( ).right(8 ).jungle( );
-A JSON object in the above form.
-
-### utils.locationToString() function
+Trees won't always generate unless the conditions are right. You
+should use the tree methods when the drone is directly above the
+ground. Trees will usually grow if the drone's current location is
+occupied by Air and is directly above an area of grass (That is why
+the `up()` method is called first).
-The utils.locationToString() function returns a
-[org.bukkit.Location][bkloc] object in string form...
+
- '{"world":"world5",x:56.9324,y:103.9954,z:43.1323,yaw:0.0,pitch:0.0}'
+None of the tree methods require parameters. Tree methods will only be
+successful if the tree is placed on grass in a setting where trees can
+grow.
-... which can be useful if you write a plugin which uses Locations as
-keys in a lookup table.
+### Drone.castle() method
+
+Creates a Castle. A castle is just a big wide fort with 4 taller forts at each corner.
+See also Drone.fort() method.
+
+#### Parameters
+
+ * side - How many blocks wide and long the castle will be (default: 24. Must be greater than 19)
+ * height - How tall the castle will be (default: 10. Must be geater than 7)
#### Example
-```javascript
-var utils = require('utils');
-...
-var key = utils.locationToString(player.location);
-lookupTable[key] = player.name;
+At the in-game prompt you can create a castle by looking at a block and typing:
+
+```javascript
+/js castle()
```
-### utils.locationFromJSON() function
+Alternatively you can create a new Drone object from a Player or Location object and call the castle() method.
-This function reconstructs an [org.bukkit.Location][bkloc] object from
-a JSON representation. This is the counterpart to the
-`locationToJSON()` function. It takes a JSON object of the form
-returned by locationToJSON() and reconstructs and returns a bukkit
-Location object.
+```javascript
+var d = new Drone(player);
+d.castle();
+```
+
-### utils.getPlayerPos() function
+### Drone.chessboard() method
-This function returns the player's [Location][bkloc] (x, y, z, pitch
-and yaw) for a named player. If the "player" is in fact a
-[org.bukkit.command.BlockCommandSender][bkbcs] then the attached
-Block's location is returned.
+Creates a tile pattern of given block types and size
#### Parameters
- * player : A [org.bukkit.command.CommandSender][bkbcs] (Player or BlockCommandSender) or player name (String).
+ * whiteBlock - (optional: default blocks.wool.white)
+ * blackBlock - (optional: default blocks.wool.black)
+ * width - width of the chessboard
+ * length - length of the chessboard
-#### Returns
+#### Example
-An [org.bukkit.Location][bkloc] object.
+At the in-game prompt you can create a chessboard by looking at a block and typing:
-[bkbcs]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/BlockCommandSender.html
-[bksndr]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/command/CommandSender.html
-### utils.getMousePos() function
+```javascript
+/js chessboard()
+```
-This function returns a [org.bukkit.Location][bkloc] object (the
-x,y,z) of the current block being targeted by the named player. This
-is the location of the block the player is looking at (targeting).
+Alternatively you can create a new Drone object from a Player or Location object and call the chessboard() method.
-#### Parameters
+```javascript
+var d = new Drone(player);
+d.chessboard();
+```
+
- * player : The player whose targeted location you wish to get.
+### Drone.cottage() method
+
+Creates a simple but cosy dwelling.
#### Example
-The following code will strike lightning at the location the player is looking at...
+At the in-game prompt you can create a cottage by looking at a block and typing:
```javascript
-var utils = require('utils');
-var playerName = 'walterh';
-var targetPos = utils.getMousePos(playerName);
-if (targetPos){
- targetPos.world.strikeLightning(targetPos);
-}
+/js cottage()
```
-### utils.foreach() function
+Alternatively you can create a new Drone object from a Player or Location object and call the cottage() method.
-The utils.foreach() function is a utility function for iterating over
-an array of objects (or a java.util.Collection of objects) and processing each object in turn. Where
-utils.foreach() differs from other similar functions found in
-javascript libraries, is that utils.foreach can process the array
-immediately or can process it *nicely* by processing one item at a
-time then delaying processing of the next item for a given number of
-server ticks (there are 20 ticks per second on the minecraft main
-thread). This method relies on Bukkit's [org.bukkit.scheduler][sched]
-package for scheduling processing of arrays.
+```javascript
+var d = new Drone(player);
+d.cottage();
+```
+
-[sched]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/package-summary.html
+### Drone.cottage_road() method
+
+Creates a tree-lined avenue with cottages on both sides.
#### Parameters
+
+ * numberOfCottages: The number of cottages to build in total (optional: default 6)
- * array : The array to be processed - It can be a javascript array, a java array or java.util.Collection
- * callback : The function to be called to process each item in the
- array. The callback function should have the following signature
- `callback(item, index, object, array)`. That is the callback will
- be called with the following parameters....
+#### Example
- - item : The item in the array
- - index : The index at which the item can be found in the array.
- - object : Additional (optional) information passed into the foreach method.
- - array : The entire array.
+At the in-game prompt you can create a cottage road by looking at a block and typing:
- * context (optional) : An object which may be used by the callback.
- * delay (optional, numeric) : If a delay is specified (in ticks - 20
- ticks = 1 second), then the processing will be scheduled so that
- each item will be processed in turn with a delay between the completion of each
- item and the start of the next. This is recommended for big builds (say 200 x 200 x 200
- blocks) or any CPU-intensive process.
- * onDone (optional, function) : A function to be executed when all processing
- is complete. This parameter is only used when the processing is delayed. (It's optional even if a
- delay parameter is supplied).
+```javascript
+/js cottage_road()
+```
-If called with a delay parameter then foreach() will return
-immediately after processing just the first item in the array (all
-subsequent items are processed later). If your code relies on the
-completion of the array processing, then provide an `onDone` parameter
-and put the code there.
+Alternatively you can create a new Drone object from a Player or Location object and call the cottage_road() method.
+
+```javascript
+var d = new Drone(player);
+d.cottage_road();
+```
+
+
+### Drone.dancefloor() method
+Create an animated dance floor of colored tiles some of which emit light.
+The tiles change color every second creating a strobe-lit dance-floor effect.
+See it in action here [http://www.youtube.com/watch?v=UEooBt6NTFo][ytdance]
+
+#### Parameters
+
+ * width - how wide the dancefloor should be (optional: default 5)
+ * length - how long the dancefloor should be (optional: default 5)
+ * duration - the time duration for which the lights should change (optional: default 30 seconds)
#### Example
-The following example illustrates how to use foreach for immediate processing of an array...
+At the in-game prompt you can create a dancefloor by looking at a block and typing:
```javascript
-var utils = require('utils');
-var players = ['moe', 'larry', 'curly'];
-utils.foreach (players, function(item){
- server.getPlayer(item).sendMessage('Hi ' + item);
-});
+/js dancefloor()
```
-... The `utils.foreach()` function can work with Arrays or any Java-style collection. This is important
-because many objects in the Bukkit API use Java-style collections...
+Alternatively you can create a new Drone object from a Player or Location object and call the dancefloor() method.
```javascript
-utils.foreach( server.onlinePlayers, function(player){
- player.chat('Hello!');
-});
+var d = new Drone(player);
+d.dancefloor();
```
-... the above code sends a 'Hello!' to every online player.
+[ytdance]: http://www.youtube.com/watch?v=UEooBt6NTFo
+
+### Drone.fort() method
-The following example is a more complex use case - The need to build an enormous structure
-without hogging CPU usage...
+Constructs a medieval fort.
-```javascript
-// build a structure 200 wide x 200 tall x 200 long
-// (That's 8 Million Blocks - enough to tax any machine!)
-var utils = require('utils');
+#### Parameters
+
+ * side - How many blocks whide and long the fort will be (default: 18 . Must be greater than 9)
+ * height - How tall the fort will be (default: 6 . Must be greater than 3)
-var a = [];
-a.length = 200;
-var drone = new Drone();
-var processItem = function(item, index, object, array){
- // build a box 200 wide by 200 long then move up
- drone.box(blocks.wood, 200, 1, 200).up();
-};
-// by the time the job's done 'self' might be someone else
-// assume this code is within a function/closure
-var player = self;
-var onDone = function(){
- player.sendMessage('Job Done!');
-};
-utils.foreach (a, processItem, null, 10, onDone);
+#### Example
+
+At the in-game prompt you can create a fort by looking at a block and typing:
+
+```javascript
+/js fort()
```
-
-### utils.nicely() function
-The utils.nicely() function is for performing processing using the
-[org.bukkit.scheduler][sched] package/API. utils.nicely() lets you
-process with a specified delay between the completion of each `next()`
-function and the start of the next `next()` function.
-`utils.nicely()` is a recursive function - that is - it calls itself
-(schedules itself actually) repeatedly until `hasNext` returns false.
+Alternatively you can create a new Drone object from a Player or Location object and call the fort() method.
-#### Parameters
+```javascript
+var d = new Drone(player);
+d.fort();
+```
+
- * next : A function which will be called if processing is to be done.
- * hasNext : A function which is called to determine if the `next`
- callback should be invoked. This should return a boolean value -
- true if the `next` function should be called (processing is not
- complete), false otherwise.
- * onDone : A function which is to be called when all processing is complete (hasNext returned false).
- * delay : The delay (in server ticks - 20 per second) between each call.
+### Drone.hangtorch() method
+
+Adds a hanging torch to a wall. This method will try to hang a torch
+against a wall. It will traverse backwards until it finds a block
+adjacent to air and hang the torch. If it can't find a block next to
+air it will log a message in the server.
#### Example
-See the source code to utils.foreach for an example of how utils.nicely is used.
+At the in-game prompt you can create a hanging torch by looking at a
+block and typing:
-### utils.at() function
+```javascript
+/js hangtorch()
+```
-The utils.at() function will perform a given task at a given time every
-(minecraft) day.
+Alternatively you can create a new Drone object from a Player or
+Location object and call the hangtorch() method.
+
+```javascript
+var d = new Drone(player);
+d.hangtorch();
+```
+
+### Drone.lcdclock() method.
+
+Constructs a large LCD Clock. The clock will display the current time of day.
+The clock can be stopped by calling the stopLCD() method of the Drone which created the clock.
#### Parameters
- * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while
- 9:30 pm is '21:30', midnight is '00:00' and midday is '12:00'
- * callback : A javascript function which will be invoked at the given time.
- * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used.
+ * foregroundBlock (Optional - default is blocks.glowstone)
+ * backgroundBlock (Optional - default is blocks.wool.black)
+ * borderBlock (Optional - a border around the LCD display - default none)
#### Example
-To warn players when night is approaching...
+At the in-game prompt you can create a LCD clock by looking at a block and typing:
```javascript
-var utils = require('utils');
-
-utils.at( '19:00', function() {
+/js var clock = lcdclock()
+/js clock.stopLCD()
+```
- utils.foreach( server.onlinePlayers, function( player ) {
- player.chat( 'The night is dark and full of terrors!' );
- });
+Alternatively you can create a new Drone object from a Player or Location object and call the lcdclock() method.
-});
+```javascript
+var d = new Drone(player);
+d.lcdclock();
+d.stopLCD();
```
-
-### utils.find() function
+
+### Drone.logojs() method
-The utils.find() function will return a list of all files starting at
-a given directory and recursiving trawling all sub-directories.
+Constructs a large Javascript Logo (black JS on Yellow background)
+See: https://raw.github.com/voodootikigod/logo.js/master/js.png
#### Parameters
- * dir : The starting path. Must be a string.
- * filter : (optional) A [FilenameFilter][fnfltr] object to return only files matching a given pattern.
+ * foregroundBlock (Optional - default is blocks.wool.gray)
+ * backgroundBlock (Optional - default is blocks.gold)
-[fnfltr]: http://docs.oracle.com/javase/6/docs/api/java/io/FilenameFilter.html
+### Drone.maze() method
+
+Maze generation based on http://rosettacode.org/wiki/Maze_generation#JavaScript
+
+#### Parameters
+
+ * width (optional - default 10)
+ * length (optional - default 10)
#### Example
+At the in-game prompt you can create a maze by looking at a block and typing:
+
```javascript
-var utils = require('utils');
-var jsFiles = utils.find('./', function(dir,name){
- return name.match(/\.js$/);
-});
+/js maze()
```
-### utils.serverAddress() function
-
-The utils.serverAddress() function returns the IP(v4) address of the server.
+Alternatively you can create a new Drone object from a Player or Location object and call the maze() method.
```javascript
-var utils = require('utils');
-var serverAddress = utils.serverAddress();
-console.log(serverAddress);
+var d = new Drone(player);
+d.maze();
```
-### utils.watchFile() function
+
-Watches for changes to the given file or directory and calls the function provided
-when the file changes.
+### Drone.rainbow() method
+
+Creates a Rainbow.
#### Parameters
-
- * File - the file to watch (can be a file or directory)
- * Callback - The callback to invoke when the file has changed. The callback takes the
- changed file as a parameter.
+
+ * radius (optional - default:18) - The radius of the rainbow
#### Example
+At the in-game prompt you can create a rainbow by looking at a block and typing:
```javascript
-var utils = require('utils');
-utils.watchFile( 'test.txt', function( file ) {
- console.log( file + ' has changed');
-});
+/js rainbow()
```
-### utils.unwatchFile() function
-Removes a file from the watch list.
+Alternatively you can create a new Drone object from a Player or Location object and call the rainbow() method.
-#### Example
-```javascript
-var utils = require('utils');
-utils.unwatchFile( 'test.txt');
+```javascript
+var d = new Drone(player);
+d.rainbow(30);
```
-## Drone Plugin
+
-The Drone is a convenience class for building. It can be used for...
+### Drone.spiral_stairs() method
- 1. Building
- 2. Copying and Pasting
+Constructs a spiral staircase with slabs at each corner.
-It uses a fluent interface which means all of the Drone's methods return `this` and can
-be chained together like so...
+#### Parameters
- var theDrone = new Drone();
- theDrone.up().left().box(blocks.oak).down().fwd(3).cylinder0(blocks.lava,8);
+ * stairBlock - The block to use for stairs, should be one of the following...
+ - 'oak'
+ - 'spruce'
+ - 'birch'
+ - 'jungle'
+ - 'cobblestone'
+ - 'brick'
+ - 'stone'
+ - 'nether'
+ - 'sandstone'
+ - 'quartz'
+ * flights - The number of flights of stairs to build.
-### TLDNR; (Just read this if you're impatient)
+
-At the in-game command prompt type...
-
- /js box( blocks.oak )
-
-... creates a single wooden block at the cross-hairs or player location
-
- /js box( blocks.oak ).right(2).box( blocks.wool.black, 4, 9, 1)
-
-... creates a single wooden block and a 2001 black obelisk that is 4
-wide x 9 tall x 1 long in size. If you want to see what else
-ScriptCraft's Drone can do, read on...
+#### Example
-### Constructing a Drone Object
+To construct a spiral staircase 5 floors high made of oak...
-Drones can be created in any of the following ways...
-
- 1. Calling any one of the methods listed below will return a Drone object. For example...
-
- var d = box( blocks.oak )
+ spiral_stairs('oak', 5);
- ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone
- object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all
- of the Drone class's methods are also global functions that return new Drone objects.
- This is short-hand for creating drones and is useful for playing around with Drones at the in-game
- command prompt. It's shorter than typing ...
-
- var d = new Drone().box( blocks.oak )
-
- ... All of the Drone's methods return `this` so you can chain operations together like this...
-
- var d = box( blocks.oak )
- .up()
- .box( blocks.oak ,3,1,3)
- .down()
- .fwd(2)
- .box( blocks.oak )
- .turn()
- .fwd(2)
- .box( blocks.oak )
- .turn()
- .fwd(2)
- .box( blocks.oak );
-
- 2. Using the following form...
+### Drone.temple() method
- d = new Drone()
-
- ...will create a new Drone. If the cross-hairs are pointing at a
- block at the time then, that block's location becomes the drone's
- starting point. If the cross-hairs are _not_ pointing at a block,
- then the drone's starting location will be 2 blocks directly in
- front of the player. TIP: Building always happens right and front
- of the drone's position...
-
- Plan View:
+Constructs a mayan temple.
- ^
- |
- |
- D---->
-
- For convenience you can use a _corner stone_ to begin building.
- The corner stone should be located just above ground level. If
- the cross-hair is point at or into ground level when you create a
- new Drone(), then building begins at that point. You can get
- around this by pointing at a 'corner stone' just above ground
- level or alternatively use the following statement...
-
- d = new Drone().up();
-
- ... which will move the drone up one block as soon as it's created.
+#### Parameters
+
+ * side - How many blocks wide and long the temple will be (default: 20)
+
+#### Example
+
+At the in-game prompt you can create a temple by looking at a block and typing:
+
+```javascript
+/js temple()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the temple() method.
+
+```javascript
+var d = new Drone(player);
+d.temple();
+```
+
- 
+## The at Module
- 3. Or by using the following form...
-
- d = new Drone(x,y,z,direction,world);
+The at module provides a single function `at()` which can be used to schedule
+repeating (or non-repeating) tasks to be done at a particular time.
- This will create a new Drone at the location you specified using
- x, y, z In minecraft, the X axis runs west to east and the Z axis runs
- north to south. The direction parameter says what direction you want
- the drone to face: 0 = east, 1 = south, 2 = west, 3 = north. If the
- direction parameter is omitted, the player's direction is used
- instead.
+### at() function
- Both the `direction` and `world` parameters are optional.
+The utils.at() function will perform a given task at a given time in the
+(minecraft) day.
- 4. Create a new Drone based on a Bukkit Location object...
+#### Parameters
- d = new Drone(location);
+ * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while
+ 9:30 pm is '21:30', midnight is '00:00' and midday is '12:00'
+ * callback : A javascript function which will be invoked at the given time.
+ * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used.
+ * repeat : (optional) true or false, default is true (repeat the task every day)
- This is useful when you want to create a drone at a given
- `org.bukkit.Location` . The `Location` class is used throughout
- the bukkit API. For example, if you want to create a drone when a
- block is broken at the block's location you would do so like
- this...
+#### Example
- events.blockBreak( function( event) {
- var location = event.block.location;
- var drone = new Drone(location);
- // do more stuff with the drone here...
- });
+To warn players when night is approaching:
-#### Parameters
+```javascript
+var utils = require('utils'),
+ at = require('at');
+function warning(){
+ utils.players(function( player ) {
+ echo( player, 'The night is dark and full of terrors!' );
+ });
+}
+at('19:00', warning);
+```
+To run a task only once at the next given time:
+```javascript
+var utils = require('utils'),
+ at = require('at');
+function wakeup(){
+ utils.players(function( player ) {
+ echo( player, "Wake Up Folks!" );
+ });
+}
+at('06:00', wakeup, null, false);
+```
- * location (optional) : *NB* If an `org.bukkit.Location` object is provided as a parameter, then it should be the only parameter.
- * x (optional) : The x coordinate of the Drone
- * y (optional) : The y coordinate of the Drone
- * z (optional) : The z coordinate of the Drone
- * direction (optional) : The direction in which the Drone is
- facing. Possible values are 0 (east), 1 (south), 2 (west) or 3 (north)
- * world (optional) : The world in which the drone is created.
-
-### Drone.box() method
+## Blocks Module
-the box() method is a convenience method for building things. (For the more performance-oriented method - see cuboid)
+You hate having to lookup [Data Values][dv] when you use ScriptCraft's
+Drone() functions. So do I. So I created this blocks object which is
+a helper object for use in construction.
-#### parameters
+### Examples
- * b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * w (optional - default 1) - the width of the structure
- * h (optional - default 1) - the height of the structure
- * d (optional - default 1) - the depth of the structure - NB this is
- not how deep underground the structure lies - this is how far
- away (depth of field) from the drone the structure will extend.
+ box( blocks.oak ); // creates a single oak wood block
+ box( blocks.sand, 3, 2, 1 ); // creates a block of sand 3 wide x 2 high x 1 long
+ box( blocks.wool.green, 2 ); // creates a block of green wool 2 blocks wide
-#### Example
+Color aliased properties that were a direct descendant of the blocks
+object are no longer used to avoid confusion with carpet and stained
+clay blocks. In addition, there's a convenience array `blocks.rainbow`
+which is an array of the 7 colors of the rainbow (or closest
+approximations).
-To create a black structure 4 blocks wide, 9 blocks tall and 1 block long...
-
- box(blocks.wool.black, 4, 9, 1);
+The blocks module is globally exported by the Drone module.
-... or the following code does the same but creates a variable that can be used for further methods...
+## Fireworks Module
- var drone = new Drone();
- drone.box(blocks.wool.black, 4, 9, 1);
+The fireworks module makes it easy to create fireworks using
+ScriptCraft. The module has a single function `firework` which takes
+a `org.bukkit.Location` as its 1 and only parameter.
-
-
-### Drone.box0() method
+### Examples
-Another convenience method - this one creates 4 walls with no floor or ceiling.
+The module also extends the `Drone` object adding a `firework` method
+so that fireworks can be created as a part of a Drone chain. For
+Example....
-#### Parameters
+ /js firework()
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * width (optional - default 1) - the width of the structure
- * height (optional - default 1) - the height of the structure
- * length (optional - default 1) - the length of the structure - how far
- away (depth of field) from the drone the structure will extend.
+... creates a single firework, while ....
-#### Example
+ /js firework().fwd(3).times(5)
-To create a stone building with the insided hollowed out 7 wide by 3 tall by 6 long...
+... creates 5 fireworks in a row. Fireworks have also been added as a
+possible option for the `arrow` module. To have a firework launch
+where an arrow strikes...
- box0( blocks.stone, 7, 3, 6);
+ /js arrows.firework()
-
-
-### Drone.boxa() method
+To call the fireworks.firework() function directly, you must provide a
+location. For example...
-Construct a cuboid using an array of blocks. As the drone moves first along the width axis,
-then the height (y axis) then the length, each block is picked from the array and placed.
+ /js var fireworks = require('fireworks');
+ /js fireworks.firework( self.location );
-#### Parameters
+
- * blocks - An array of blocks - each block in the array will be placed in turn.
- * width
- * height
- * length
+## Classroom Plugin
-#### Example
+The `classroom` object contains a couple of utility functions for use
+in a classroom setting. The goal of these functions is to make it
+easier for tutors to facilitate ScriptCraft for use by students in a
+classroom environment. Although granting ScriptCraft access to
+students on a shared server is potentially risky (Students can
+potentially abuse it), it is slighlty less risky than granting
+operator privileges to each student. (Enterprising students will
+quickly realise how to grant themselves and others operator privileges
+once they have access to ScriptCraft).
-Construct a rainbow-colored road 100 blocks long...
+The goal of this module is not so much to enforce restrictions
+(security or otherwise) but to make it easier for tutors to setup a
+shared server so students can learn Javascript. When scripting is
+turned on, every player who joins the server will have a dedicated
+directory into which they can save scripts. All scripts in such
+directories are automatically watched and loaded into a global
+variable named after the player.
- var rainbowColors = [blocks.wool.red, blocks.wool.orange, blocks.wool.yellow, blocks.wool.lime,
- blocks.wool.lightblue, blocks.wool.blue, blocks.wool.purple];
-
- boxa(rainbowColors,7,1,30);
+So for example, if player 'walterh' joins the server, a `walterh`
+global variable is created. If a file `greet.js` with the following
+content is dropped into the `scriptcraft/players/walterh`
+directory...
-
+```javascript
+exports.hi = function( player ){
+ echo( player, 'Hi ' + player.name);
+};
+```
-### Drone Movement
+... then it can be invoked like this: `/js walterh.hi( self )` . This
+lets every player/student create their own functions without having
+naming collisions.
-Drones can move freely in minecraft's 3-D world. You control the
-Drone's movement using any of the following methods..
+It's strongly recommended that the
+`scriptcraft/players/` directory is shared so that
+others can connect to it and drop .js files into their student
+directories. On Ubuntu, select the folder in Nautilus (the default
+file browser) then right-click and choose *Sharing Options*, check the
+*Share this folder* checkbox and the *Allow others to create and
+delete files* and *Guest access* checkboxes. Click *Create Share*
+button to close the sharing options dialog. Students can then access
+the shared folder as follows...
- * up()
- * down()
- * left()
- * right()
- * fwd()
- * back()
- * turn()
+ * Windows: Open Explorer, Go to \\{serverAddress}\players\
+ * Macintosh: Open Finder, Go to smb://{serverAddress}/players/
+ * Linux: Open Nautilus, Go to smb://{serverAddress}/players/
-... Each of these methods takes a single optional parameter
-`numBlocks` - the number of blocks to move in the given direction. If
-no parameter is given, the default is 1.
+... where {serverAddress} is the ip address of the server (this is
+displayed to whoever invokes the classroom.allowScripting() function.)
-to change direction use the `turn()` method which also takes a single
-optional parameter (numTurns) - the number of 90 degree turns to make.
-Turns are always clock-wise. If the drone is facing north, then
-drone.turn() will make the turn face east. If the drone is facing east
-then drone.turn(2) will make the drone turn twice so that it is facing
-west.
+### jsp classroom command
+The `jsp classroom` command makes it easy for tutors to turn on or off
+classroom mode. This command can only be used by server operators. To
+turn on classroom mode (enable scripting for all players):
-### Drone Positional Info
+ jsp classroom on
- * getLocation() - Returns a Bukkit Location object for the drone
+To turn off classroom mode (disable scripting for all players):
-### Drone Markers
+ jsp classroom off
-Markers are useful when your Drone has to do a lot of work. You can
-set a check-point and return to the check-point using the move()
-method. If your drone is about to undertake a lot of work -
-e.g. building a road, skyscraper or forest you should set a
-check-point before doing so if you want your drone to return to its
-current location.
+The `jsp classroom` command is provided as an easier way to turn on or
+off classroom mode. This should be used in preference to the
+classroom.allowScripting() function which is provided only for
+programmatically enabling or disabling classroom mode.
-A 'start' checkpoint is automatically created when the Drone is first created.
+### classroom.allowScripting() function
-Markers are created and returned to using the followng two methods...
+Allow or disallow anyone who connects to the server (or is already
+connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
+to every student in a Minecraft classroom environment.
- * chkpt - Saves the drone's current location so it can be returned to later.
- * move - moves the drone to a saved location. Alternatively you can provide an
- org.bukkit.Location object or x,y,z and direction parameters.
+Whenever any file is added/edited or removed from any of the players/
+directories the contents are automatically reloaded. This is to
+facilitate quick turnaround time for students getting to grips with
+Javascript.
#### Parameters
- * name - the name of the checkpoint to save or return to.
+ * canScript : true or false
#### Example
- drone.chkpt('town-square');
- //
- // the drone can now go off on a long excursion
- //
- for ( i = 0; i< 100; i++) {
- drone.fwd(12).box(6);
- }
- //
- // return to the point before the excursion
- //
- drone.move('town-square');
+To allow all players (and any players who connect to the server) to
+use the `js` and `jsp` commands...
-### Drone.prism() method
+ /js classroom.allowScripting( true, self )
-Creates a prism. This is useful for roofs on houses.
+To disallow scripting (and prevent players who join the server from using the commands)...
-#### Parameters
+ /js classroom.allowScripting( false, self )
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * width - the width of the prism
- * length - the length of the prism (will be 2 time its height)
+Only ops users can run the classroom.allowScripting() function - this is so that students
+don't try to bar themselves and each other from scripting.
-#### Example
+## Inventory Module
+This module provides functions to add items to, remove items from and check the
+contents of a player or NPC's inventory.
- prism(blocks.oak,3,12);
+### Usage
+The inventory module is best used in conjunction with the items module. See below for examples of usage.
-
+```javascript
+var inventory = require('inventory');
+var items = require('items');
+var utils = require('utils');
-### Drone.prism0() method
+// gives every player a cookie and a baked potatoe
+utils.players(function(player){
+ inventory(player)
+ .add( items.cookie(1) )
+ .add( items.bakedPotato(1) )
+});
-A variation on `prism` which hollows out the inside of the prism. It uses the same parameters as `prism`.
+// give a player 6 cookies then take away 4 of them
-### Drone.cylinder() method
+inventory(player)
+ .add( items.cookie(6) )
+ .remove ( items.cookie(4) )
-A convenience method for building cylinders. Building begins radius blocks to the right and forward.
+// check if a player has any cookies
-#### Parameters
+var hasCookies = inventory(player).contains( items.cookie(1) );
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * radius
- * height
+```
+The inventory module exposes a single function which when passed a player or NPC will return an object with 3 methods:
-#### Example
+* add : Adds items to the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
+* remove : removes items from the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
+* contains : checks to see if there is the specified type and amount of item in the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
-To create a cylinder of Iron 7 blocks in radius and 1 block high...
+## Asynchronous Input Module
- cylinder(blocks.iron, 7 , 1);
+The `input` module provides a simple way to prompt players for input at the
+in-game prompt. In Javascript browser environments the `prompt()` function provides
+a way to block execution and ask the user for input. Execution is blocked until the user
+provides input using the modal dialog and clicks OK. Unfortunately Minecraft provides no
+equivalent modal dialog which can be used to gather player text input. The only way to gather text
+input from the player in Minecraft is to do so asynchronously. That is - a prompt message can be
+sent to the player but the player is not obliged to provide input immediately, nor does the program
+execution block until the player does so.
-
+So ScriptCraft has no `prompt()` implementation because `prompt()` is a synchronous function and
+Minecraft's API provides no equivalent functions or classes which can be used to implement this synchronously.
+The Minecraft API does however have a 'Conversation' API which allows for prompting of the player and asynchronously gathering text input from the player.
-### Drone.cylinder0() method
+This new `input()` function is best illustrated by example. The following code is for a number-guessing game:
+
+```javascript
+var input = require('input');
+exports.numberguess = function(player){
+ var randomNumber = Math.ceil(Math.random() * 10);
+ input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, guesser, repeat ) {
+ if ( guess == 'q'){
+ return;
+ }
+ if ( +guess !== randomNumber ) {
+ if (+guess < randomNumber ) {
+ echo( guesser, 'Too low - guess again');
+ }
+ if (+guess > randomNumber ) {
+ echo( guesser, 'Too high - guess again');
+ }
+ repeat();
+ } else {
+ echo( guesser, 'You guessed correctly');
+ }
+ });
+};
+```
-A version of cylinder that hollows out the middle.
+The `input()` function takes 3 parameters, the player, a prompt message and a callback which will be invoked when the player has entered some text at the in-game command prompt.
+The callback is bound to an object which has the following properties:
-#### Example
+ * sender : The player who input the text
+ * value : The value of the text which has been input.
+ * message: The message prompt.
+ * repeat: A function which when invoked will repeat the original prompt. (this is for flow control)
-To create a hollow cylinder of Iron 7 blocks in radius and 1 block high...
+The callback function as well as being bound to an object with the above properties (so you can use this.value inside your callback to get the value which has just been input), can also take the following parameters (in exact order):
- cylinder0(blocks.iron, 7, 1);
+ * value
+ * sender
+ * repeat
-
+The `value` parameter will be the same as `this.value`, the `repeat` parameter will be the same as `this.repeat` and so on.
-### Drone.arc() method
+## Lightning module
-The arc() method can be used to create 1 or more 90 degree arcs in the horizontal or vertical planes.
-This method is called by cylinder() and cylinder0() and the sphere() and sphere0() methods.
+Causes a bolt of lightning to strike.
-#### Parameters
+### Usage
+```javascript
+// strike lightning wherever a player's arrow lands
+var lightning = require('lightning');
+events.projectileHit( function( event ){
+ if ( entities.arrow( event.projectile ) // it's an arrow
+ && entities.player( event.projectile.owner ) // it was shot by a player
+ ) {
+ lightning( event.projectile ); // strike lightning at the arrow location
+ }
+});
+```
-arc() takes a single parameter - an object with the following named properties...
+## The recipes module
- * radius - The radius of the arc.
- * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv].
- * meta - The metadata value. See [Data Values][dv].
- * orientation (default: 'horizontal' ) - the orientation of the arc - can be 'vertical' or 'horizontal'.
- * stack (default: 1 ) - the height or length of the arc (depending on
- the orientation - if orientation is horizontal then this parameter
- refers to the height, if vertical then it refers to the length ).
- * strokeWidth (default: 1 ) - the width of the stroke (how many
- blocks) - if drawing nested arcs it's usually a good idea to set
- strokeWidth to at least 2 so that there are no gaps between each
- arc. The arc method uses a [bresenham algorithm][bres] to plot
- points along the circumference.
- * fill - If true (or present) then the arc will be filled in.
- * quadrants (default:
- `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An
- object with 4 properties indicating which of the 4 quadrants of a
- circle to draw. If the quadrants property is absent then all 4
- quadrants are drawn.
+The Recipes module provides convenience functions for adding and removing recipes
+from the game.
-#### Examples
+### Example
+To add an EnderBow to the game (assumes there's an enchanted Item variable called enderBow)...
+
+ var recipes = require('recipes');
+ var items = require('items');
+ ...
+ var enderBowRecipe = recipes.create( {
+ result: enderBow,
+ ingredients: {
+ E: items.enderPearl(1),
+ S: items.stick(1),
+ W: items.string(1)
+ },
+ shape: [ 'ESW',
+ 'SEW',
+ 'ESW' ]
+ } );
+ // add to server
+ var addedRecipe = server.addRecipe( enderBowRecipe );
+ // to remove...
+ server.removeRemove( addedRecipe );
-To draw a 1/4 circle (top right quadrant only) with a radius of 10 and stroke width of 2 blocks ...
+## Http Module
- arc({blockType: blocks.iron,
- meta: 0,
- radius: 10,
- strokeWidth: 2,
- quadrants: { topright: true },
- orientation: 'vertical',
- stack: 1,
- fill: false
- } );
+For handling http requests. Not to be confused with the more robust
+and functional 'http' module bundled with Node.js.
-
+### http.request() function
-[bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
-[dv]: http://www.minecraftwiki.net/wiki/Data_values
+The http.request() function will fetch a web address asynchronously (on a
+separate thread)and pass the URL's response to a callback function
+which will be executed synchronously (on the main thread). In this
+way, http.request() can be used to fetch web content without blocking the
+main thread of execution.
-### Drone.door() method
+#### Parameters
-create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created.
+ * request: The request details either a plain URL e.g. "http://scriptcraft.js/sample.json" or an object with the following properties...
-#### Parameters
+ - url: The URL of the request.
+ - method: Should be one of the standard HTTP methods, GET, POST, PUT, DELETE (defaults to GET).
+ - params: A Javascript object with name-value pairs. This is for supplying parameters to the server.
- * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
+ * callback: The function to be called when the Web request has completed. This function takes the following parameters...
+ - responseCode: The numeric response code from the server. If the server did not respond with 200 OK then the response parameter will be undefined.
+ - response: A string (if the response is of type text) or object containing the HTTP response body.
#### Example
-To create a wooden door at the crosshairs/drone's location...
+The following example illustrates how to use http.request to make a request to a JSON web service and evaluate its response...
- var drone = new Drone();
- drone.door();
+```javascript
+var jsResponse;
+var http = require('http');
+http.request('http://scriptcraftjs.org/sample.json',function(responseCode, responseBody){
+ jsResponse = JSON.parse( responseBody );
+});
+```
+The following example illustrates a more complex use-case POSTing parameters to a CGI process on a server...
-To create an iron door...
+```javascript
+var http = require('http');
+http.request( {
+ url: 'http://pixenate.com/pixenate/pxn8.pl',
+ method: 'POST',
+ params: {script: '[]'}
+ },
+ function( responseCode, responseBody ) {
+ var jsObj = JSON.parse( responseBody );
+ });
+```
- drone.door( blocks.door_iron );
+## sc-mqtt module
-
+This module provides a simple way to communicate with devices (such as Arduino)
+using the popular lightweight [MQTT protocol][mqtt].
-### Drone.door_iron() method
+### Usage
-create an Iron door.
+This module can only be used if the separate `sc-mqtt.jar` file is
+present in the CraftBukkit classpath. To use this module, you should
+...
-### Drone.door2() method
+ 1. Download sc-mqtt.jar from
+ 2. Save the file to the same directory where craftbukkit.jar resides.
+ 3. Create a new batch file (windows-only) called
+ craftbukkit-sc-mqtt.bat and edit it to include the following
+ command...
-Create double doors (left and right side)
+ ```sh
+ java -classpath sc-mqtt.jar;craftbukkit.jar org.bukkit.craftbukkit.Main
+ ```
-#### Parameters
+ If you're using Mac OS, create a new craftbukkit-sc-mqtt.command
+ file and edit it (using TextWrangler or another text editor) ...
- * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
+ ```sh
+ java -classpath sc-mqtt.jar:craftbukkit.jar org.bukkit.craftbukkit.Main
+ ```
-#### Example
+ 4. Execute the craftbukkit-sc-mqtt batch file / command file to start
+ Craftbukkit. You can now begin using this module to send and receive
+ messages to/from a Net-enabled Arduino or any other device which uses
+ the [MQTT protocol][mqtt]
+
+ ```javascript
+ var mqtt = require('sc-mqtt');
+ // create a new client
+ var client = mqtt.client( 'tcp://localhost:1883', 'uniqueClientId' );
+ // connect to the broker
+ client.connect( { keepAliveInterval: 15 } );
+ // publish a message to the broker
+ client.publish( 'minecraft', 'loaded' );
+ // subscribe to messages on 'arduino' topic
+ client.subscribe( 'arduino' );
+ // do something when an incoming message arrives...
+ client.onMessageArrived( function( topic, message ) {
+ console.log( 'Message arrived: topic=' + topic + ', message=' + message );
+ });
-To create double-doors at the cross-hairs/drone's location...
+ ```
- drone.door2();
+The `sc-mqtt` module provides a very simple minimal wrapper around the
+[Eclipse Paho MQTT Version 3 Client][pahodocs] java-based MQTT
+library.
-
+[pahodocs]: http://pic.dhe.ibm.com/infocenter/wmqv7/v7r5/index.jsp?topic=/com.ibm.mq.javadoc.doc/WMQMQxrClasses/org/eclipse/paho/client/mqttv3/package-summary.html
+[mqtt]: http://mqtt.org/
-### Drone.door2_iron() method
+## Signs Module
-Create double iron doors
-
+The Signs Module can be used by plugin authors to create interactive
+signs - that is - signs which display a list of choices which can be
+changed by interacting (right-clicking) with the sign.
-### Drone.sign() method
+### signs.menu() function
-Signs must use block 63 (stand-alone signs) or 68 (signs on walls)
+This function is used to construct a new interactive menu on top of an
+existing sign in the game world.
#### Parameters
- * message - can be a string or an array of strings.
- * block - can be 63 or 68
+ * Label : A string which will be displayed in the topmost line of the
+ sign. This label is not interactive.
+ * options : An array of strings which can be selected on the sign by
+ right-clicking/interacting.
+ * callback : A function which will be called whenever a player
+ interacts (changes selection) on a sign. This callback in turn
+ takes as its parameter, an object with the following properties...
-#### Example
+ * player : The player who interacted with the sign.
+ * sign : The [org.bukkit.block.Sign][buksign] which the player interacted with.
+ * text : The text for the currently selected option on the sign.
+ * number : The index of the currently selected option on the sign.
+
+ * selectedIndex : optional: A number (starting at 0) indicating which
+ of the options should be selected by default. 0 is the default.
-To create a free-standing sign...
+#### Returns
+This function does not itself do much. It does however return a
+function which when invoked with a given
+[org.bukkit.block.Sign][buksign] object, will convert that sign into
+an interactive sign.
- drone.sign(["Hello","World"],63);
+#### Example: Create a sign which changes the time of day.
-
+##### plugins/signs/time-of-day.js
+
+```javascript
+var utils = require('utils'),
+ signs = require('signs');
-... to create a wall mounted sign...
+var onTimeChoice = function(event){
+ var selectedIndex = event.number;
+ // convert to Minecraft time 0 = Dawn, 6000 = midday, 12000 = dusk, 18000 = midnight
+ var time = selectedIndex * 6000;
+ event.player.location.world.setTime(time);
+};
+
+// signs.menu returns a function which can be called for one or more signs in the game.
+var convertToTimeMenu = signs.menu('Time of Day',
+ ['Dawn', 'Midday', 'Dusk', 'Midnight'],
+ onTimeChoice);
+
+exports.time_sign = function( player ){
+ var sign = signs.getTargetedBy(player);
+ if ( !sign ) {
+ throw new Error('You must look at a sign');
+ }
+ convertToTimeMenu(sign);
+};
+```
- drone.sign(["Welcome","to","Scriptopia"], 68 );
+To use the above function at the in-game prompt, look at an existing
+sign and type...
-
+ /js time_sign(self);
-### Drone Trees methods
+... and the sign you're looking at will become an interactive sign
+which changes the time each time you interact (right-click) with it.
- * oak()
- * spruce()
- * birch()
- * jungle()
+### signs.getTargetedBy() function
-#### Example
+This function takes a [org.bukkit.entity.LivingEntity][bukle] as a
+parameter and returns a [org.bukkit.block.Sign][buksign] object which
+the entity has targeted. It is a utility function for use by plugin authors.
-To create 4 trees in a row, point the cross-hairs at the ground then type `/js ` and ...
+#### Example
- up( ).oak( ).right(8 ).spruce( ).right(8 ).birch( ).right(8 ).jungle( );
+```javascript
+var signs = require('signs'),
+ utils = require('utils');
+var player = utils.player('tom1234');
+var sign = signs.getTargetedBy( player );
+if ( !sign ) {
+ echo( player, 'Not looking at a sign');
+}
+```
-Trees won't always generate unless the conditions are right. You
-should use the tree methods when the drone is directly above the
-ground. Trees will usually grow if the drone's current location is
-occupied by Air and is directly above an area of grass (That is why
-the `up( )` method is called first).
+[buksign]: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/block/Sign.html
+[bukle]: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/LivingEntity.html
-
+## The slash Module
-None of the tree methods require parameters. Tree methods will only be successful
-if the tree is placed on grass in a setting where trees can grow.
+This module provides a single function which makes it easy to execute
+minecraft commands via javascript.
-### Drone.garden() method
+### The slash() function
-places random flowers and long grass (similar to the effect of placing bonemeal on grass)
+This function makes it easy to execute one or more minecraft commands.
#### Parameters
- * width - the width of the garden
- * length - how far from the drone the garden extends
+ * commands : A String or Array of strings - each string is a command to be executed.
+ * sender: (optional) The player on whose behalf the commands should be executed. If not specified the commands will be executed as the server console user.
-#### Example
+#### Examples
-To create a garden 10 blocks wide by 5 blocks long...
+Invoke the `/defaultgamemode creative` command (as server).
- garden(10,5);
+```javascript
+var slash = require('slash');
+slash('defaultgamemode creative');
+```
-
+Set the time of day to Midday and toggle downfall (as player 'JohnDoe'):
-### Drone.rand() method
+```javascript
+var slash = require('slash'),
+ utils = require('utils');
+var johnDoe = utils.player('John_Doe');
-rand takes either an array (if each blockid has the same chance of occurring)
-or an object where each property is a blockid and the value is it's weight (an integer)
+slash([
+ 'time set 6000',
+ 'toggledownfall'
+], johnDoe);
+```
-#### Example
+## Sounds Module
-place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked)
+This module is a simple wrapper around the Bukkit Sound class and provides
+a simpler way to play sounds. All of the org.bukkit.Sound Enum values are attached.
- rand( [blocks.brick.stone, blocks.brick.mossy, blocks.brick.cracked ],w,d,h)
+### Usage (Bukkit) :
-to place random blocks stone has a 50% chance of being picked,
+ var sounds = require('sounds');
+ sounds.play( bukkit.sound.VILLAGER_NO , self, 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch
+ sounds.play( bukkit.sound.VILLAGER_NO , self ); // same as previous statement
+
+The play() function takes either a Location object or any object which has a location.
+The volume parameter is in the range 0 to 1 and the pitch parameter is in the range 0 to 4.
+
+In addition, a play function is provided for each possible sound using the following rules:
- var distribution = {};
- distribution[ blocks.brick.stone ] = 5;
- distribution[ blocks.brick.mossy ] = 3;
- distribution[ blocks.brick.cracked ] = 2;
+1. The sound is converted from ALL_CAPS_UNDERSCORE to camelCase so for example there is a sounds.villagerNo() function which will play the VILLAGER_NO sound.
+2. Each such function can take 3 parameters: location (which can be either an actual Location object or an object which has a location), volume and pitch
+3. Or... each such function can be called without parameters meaning the sound will be played for all online players to hear.
- rand( distribution, width, height, depth)
+ sounds.villagerNo(self, 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch at invoker's location
-regular stone has a 50% chance, mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked.
+ sounds.villagerNo(); // plays VILLAGER_NO sound for all players online.
-### Copy & Paste using Drone
+These methods are provided for convenience to help beginners explore sounds using TAB completion.
+## Spawn Module
-A drone can be used to copy and paste areas of the game world.
+Provides a single function to 'spawn' an entity at a given location.
-### Drone.copy() method
+### Parameters
-Copies an area so it can be pasted elsewhere. The name can be used for
-pasting the copied area elsewhere...
+ * entityType - The type of entity to spawn. This can be a string (see entities module for reference) or a framework-specific object type (see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/EntityType.html). A list of [all possible entities][ents] functions (equivalent to the EntityType enum).
-#### Parameters
+ * location - where the entity should be spawned.
- * name - the name to be given to the copied area (used by `paste`)
- * width - the width of the area to copy
- * height - the height of the area to copy
- * length - the length of the area (extending away from the drone) to copy
+[ents]: #entities-module
-#### Example
+### Example
- drone.copy('somethingCool',10,5,10 ).right(12 ).paste('somethingCool' );
+Using the entities module as a helper, spawn a new polar bear at the world's default spawn location:
-### Drone.paste() method
+```javascript
+var entities = require('entities'),
+ spawn = require('spawn');
+...
+var spawnLocation = world.spawnLocation;
+spawn(entities.polar_bear(), spawnLocation);
+```
-Pastes a copied area to the current location.
+This module is in turn used by the Drone's `spawn()` method and the `jsp spawn` command.
+String class extensions
+-----------------------
+The following chat-formatting methods are added to the javascript String class..
-#### Example
+ * aqua()
+ * black()
+ * blue()
+ * bold()
+ * brightgreen()
+ * darkaqua()
+ * darkblue()
+ * darkgray()
+ * darkgreen()
+ * purple()
+ * darkpurple()
+ * darkred()
+ * gold()
+ * gray()
+ * green()
+ * italic()
+ * lightpurple()
+ * indigo()
+ * green()
+ * red()
+ * pink()
+ * yellow()
+ * white()
+ * strike()
+ * random()
+ * magic()
+ * underline()
+ * reset()
-To copy a 10x5x10 area (using the drone's coordinates as the starting
-point) into memory. the copied area can be referenced using the name
-'somethingCool'. The drone moves 12 blocks right then pastes the copy.
+Example
+-------
- drone.copy('somethingCool',10,5,10 )
- .right(12 )
- .paste('somethingCool' );
+ /js var boldGoldText = "Hello World".bold().gold();
+ /js echo(self, boldGoldText );
-### Chaining
+Hello World
-All of the Drone methods return a Drone object, which means methods
-can be 'chained' together so instead of writing this...
+## Teleport Module
- drone = new Drone();
- drone.fwd(3);
- drone.left(2);
- drone.box(2); // create a grass block
- drone.up();
- drone.box(2); // create another grass block
- drone.down();
+This module provides a function to teleport entities (Players or NPCs).
-...you could simply write ...
-
- var drone = new Drone().fwd(3).left(2).box(2).up().box(2).down();
+### Parameters
-... since each Drone method is also a global function that constructs
-a drone if none is supplied, you can shorten even further to just...
-
- fwd(3).left(2).box(2).up().box(2).down()
+ * entity - The player or NPC to be teleported. If of type String, then a player with that name will be teleported.
+ * destination - The location to which they should be teleported. If not of type Location but is a Player, Block or any
+ object which has a `location` property then that works too. If of type String, then it's assumed that the destination is the player with that name.
-The Drone object uses a [Fluent Interface][fl] to make ScriptCraft
-scripts more concise and easier to write and read. Minecraft's
-in-game command prompt is limited to about 80 characters so chaining
-drone commands together means more can be done before hitting the
-command prompt limit. For complex building you should save your
-commands in a new script file and load it using /js load()
+### Example
-[fl]: http://en.wikipedia.org/wiki/Fluent_interface
+The following code will teleport each player back to their spawn position.
-### Drone Properties
+```javascript
+var teleport = require('teleport'),
+ utils = require('utils'),
+ players = utils.players(),
+ i = 0;
+for ( ; i < players.length; i++ ) {
+ teleport( players[i], players[i].spawnPosition );
+}
+```
- * x - The Drone's position along the west-east axis (x increases as you move east)
- * y - The Drone's position along the vertical axis (y increses as you move up)
- * z - The Drone's position along the north-south axis (z increases as you move south)
- * dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north.
+The following code will teleport 'tom' to 'jane's location.
-### Extending Drone
+```javascript
+var teleport = require('teleport');
+teleport('tom' , 'jane');
+```
+## Utilities Module
-The Drone object can be easily extended - new buidling recipes/blueprints can be added and can
-become part of a Drone's chain using the *static* method `Drone.extend`.
+The `utils` module is a storehouse for various useful utility
+functions which can be used by plugin and module authors. It contains
+miscellaneous utility functions and classes to help with programming.
-### Drone.extend() static method
+### utils.player() function
-Use this method to add new methods (which also become chainable global functions) to the Drone object.
+The utils.player() function will return a [Player][cmpl] object
+with the given name. This function takes a single parameter
+`playerName` which can be either a String or a [Player][cmpl] object -
+if it's a Player object, then the same object is returned. If it's a
+String, then it tries to find the player with that name.
#### Parameters
- * name - The name of the new method e.g. 'pyramid'
- * function - The method body.
+ * playerName : A String or Player object. If no parameter is provided
+ then player() will try to return the `self` variable . It is
+ strongly recommended to provide a parameter.
#### Example
- // submitted by [edonaldson][edonaldson]
- Drone.extend('pyramid', function( block,height) {
- this.chkpt('pyramid');
- for ( var i = height; i > 0; i -= 2) {
- this.box(block, i, 1, i).up().right().fwd();
- }
- return this.move('pyramid');
- });
+```javascript
+var utils = require('utils');
+var name = 'walterh';
+var player = utils.player(name);
+if ( player ) {
+ echo(player, 'Got ' + name);
+} else {
+ console.log('No player named ' + name);
+}
+```
-Once the method is defined (it can be defined in a new pyramid.js file) it can be used like so...
+[bkpl]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html
+[cmpl]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/entity/living/humanoid/Player.html
+[cmloc]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/position/Location.html
+[bkloc]: http://jd.bukkit.org/dev/apidocs/org/bukkit/Location.html
- var d = new Drone();
- d.pyramid(blocks.brick.stone, 12);
+### utils.world( worldName ) function
-... or simply ...
+Returns a World object matching the given name
- pyramid(blocks.brick.stone, 12);
+### utils.blockAt( Location ) function
-[edonaldson]: https://github.com/edonaldson
+Returns the Block at the given location.
-### Drone Constants
+### utils.locationToJSON() function
-#### Drone.PLAYER_STAIRS_FACING
+utils.locationToJSON() returns a [Location][cmloc] object in JSON form...
-An array which can be used when constructing stairs facing in the Drone's direction...
+ { world: 'world5',
+ x: 56.9324,
+ y: 103.9954,
+ z: 43.1323,
+ yaw: 0.0,
+ pitch: 0.0
+ }
- var d = new Drone();
- d.box(blocks.stairs.oak + ':' + Drone.PLAYER_STAIRS_FACING[d.dir]);
+This can be useful if you write a plugin that needs to store location data since bukkit's Location object is a Java object which cannot be serialized to JSON by default.
-... will construct a single oak stair block facing the drone.
+#### Parameters
+
+ * location: An object of type [Location][cmloc]
-#### Drone.PLAYER_SIGN_FACING
+#### Returns
-An array which can be used when placing signs so they face in a given direction.
-This is used internally by the Drone.sign() method. It should also be used for placing
-any of the following blocks...
+A JSON object in the above form.
+
+### utils.locationToString() function
- * chest
- * ladder
- * furnace
- * dispenser
+The utils.locationToString() function returns a
+[Location][cmloc] object in string form...
-To place a chest facing the Drone ...
+ '{"world":"world5",x:56.9324,y:103.9954,z:43.1323,yaw:0.0,pitch:0.0}'
- drone.box( blocks.chest + ':' + Drone.PLAYER_SIGN_FACING[drone.dir]);
+... which can be useful if you write a plugin which uses Locations as
+keys in a lookup table.
-#### Drone.PLAYER_TORCH_FACING
+#### Example
-Used when placing torches so that they face towards the drone.
+```javascript
+var utils = require('utils');
+...
+var key = utils.locationToString(player.location);
+lookupTable[key] = player.name;
+```
- drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[drone.dir]);
+### utils.locationFromJSON() function
-### Drone.times() Method
+This function reconstructs an [Location][cmloc] object from
+a JSON representation. This is the counterpart to the
+`locationToJSON()` function. It takes a JSON object of the form
+returned by locationToJSON() and reconstructs and returns a bukkit
+Location object.
-The times() method makes building multiple copies of buildings easy. It's possible to create rows or grids of buildings without resorting to `for` or `while` loops.
+### utils.getPlayerPos() function
+
+This function returns the player's [Location][cmloc] (x, y, z, pitch
+and yaw) for a named player. If the "player" is in fact a
+[BlockCommand][bkbcs] then the attached Block's location is returned.
#### Parameters
- * numTimes (optional - default 2) : The number of times you want to repeat the preceding statements.
+ * player : A [org.bukkit.command.CommandSender][bkbcs] (Player or BlockCommandSender) or player name (String).
-#### Example
+#### Returns
-Say you want to do the same thing over and over. You have a couple of options...
+A [Location][cmloc] object.
- * You can use a for loop...
+[bkbcs]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/BlockCommandSender.html
+[bksndr]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/command/CommandSender.html
+### utils.getMousePos() function
- d = new Drone(); for ( var i =0;i < 4; i++) { d.cottage().right(8); }
+This function returns a [Location][cmloc] object (the
+x,y,z) of the current block being targeted by the named player. This
+is the location of the block the player is looking at (targeting).
-While this will fit on the in-game prompt, it's awkward. You need to
-declare a new Drone object first, then write a for loop to create the
-4 cottages. It's also error prone, even the `for` loop is too much
-syntax for what should really be simple.
+#### Parameters
- * You can use a while loop...
-
- d = new Drone(); var i=4; while (i--) { d.cottage().right(8); }
+ * player : The player whose targeted location you wish to get.
-... which is slightly shorter but still too much syntax. Each of the
-above statements is fine for creating a 1-dimensional array of
-structures. But what if you want to create a 2-dimensional or
-3-dimensional array of structures? Enter the `times()` method.
+#### Example
-The `times()` method lets you repeat commands in a chain any number of
-times. So to create 4 cottages in a row you would use the following
-statement...
+The following code will strike lightning at the location the player is looking at...
- cottage().right(8).times(4);
+```javascript
+var utils = require('utils');
+var playerName = 'walterh';
+var targetPos = utils.getMousePos(playerName);
+if (targetPos){
+ if (__plugin.canary){
+ targetPos.world.makeLightningBolt(targetPos);
+ }
+ if (__plugin.bukkit){
+ targetPos.world.strikeLightning(targetPos);
+ }
+}
+```
-...which will build a cottage, then move right 8 blocks, then do it
-again 4 times over so that at the end you will have 4 cottages in a
-row. What's more the `times()` method can be called more than once in
-a chain. So if you wanted to create a *grid* of 20 houses ( 4 x 5 ),
-you would do so using the following statement...
+### utils.foreach() function
- cottage().right(8).times(4).fwd(8).left(32).times(5);
+The utils.foreach() function is a utility function for iterating over
+an array of objects (or a java.util.Collection of objects) and
+processing each object in turn. Where utils.foreach() differs from
+other similar functions found in javascript libraries, is that
+utils.foreach can process the array immediately or can process it
+*nicely* by processing one item at a time then delaying processing of
+the next item for a given number of server ticks (there are 20 ticks
+per second on the minecraft main thread). This method relies on
+Bukkit's [org.bukkit.scheduler][sched] package for scheduling
+processing of arrays.
-... breaking it down...
+[sched]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/package-summary.html
- 1. The first 3 calls in the chain ( `cottage()`, `right(8)`,
- `times(4)` ) build a single row of 4 cottages.
+#### Parameters
- 2. The last 3 calls in the chain ( `fwd(8)`, `left(32)`, `times(5)` )
- move the drone forward 8 then left 32 blocks (4 x 8) to return to
- the original x coordinate, then everything in the chain is
- repeated again 5 times so that in the end, we have a grid of 20
- cottages, 4 x 5. Normally this would require a nested loop but
- the `times()` method does away with the need for loops when
- repeating builds.
+ * array : The array to be processed - It can be a javascript array, a java array or java.util.Collection
+ * callback : The function to be called to process each item in the
+ array. The callback function should have the following signature
+ `callback(item, index, object, array)`. That is the callback will
+ be called with the following parameters....
-Another example: This statement creates a row of trees 2 by 3 ...
+ - item : The item in the array
+ - index : The index at which the item can be found in the array.
+ - object : Additional (optional) information passed into the foreach method.
+ - array : The entire array.
- oak().right(10).times(2).left(20).fwd(10).times(3)
+ * context (optional) : An object which may be used by the callback.
+ * delayInMilliseconds (optional, numeric) : If a delay is specified then the processing will be scheduled so that
+ each item will be processed in turn with a delay between the completion of each
+ item and the start of the next. This is recommended for any CPU-intensive process.
+ * onDone (optional, function) : A function to be executed when all processing
+ is complete. This parameter is only used when the processing is delayed. (It's optional even if a
+ delay parameter is supplied).
-... You can see the results below.
+If called with a delay parameter then foreach() will return
+immediately after processing just the first item in the array (all
+subsequent items are processed later). If your code relies on the
+completion of the array processing, then provide an `onDone` parameter
+and put the code there.
-
+#### Example
-### Drone.blocktype() method
+The following example illustrates how to use foreach for immediate processing of an array...
-Creates the text out of blocks. Useful for large-scale in-game signs.
+```javascript
+var utils = require('utils');
+var players = utils.players();
+utils.foreach (players, function( player ) {
+ echo( player , 'Hi ' + player);
+});
+```
+
+... The `utils.foreach()` function can work with Arrays or any
+Java-style collection. This is important because many objects in the
+CanaryMod and Bukkit APIs use Java-style collections.
+### utils.nicely() function
+
+The utils.nicely() function is for performing background processing. utils.nicely() lets you
+process with a specified delay between the completion of each `next()`
+function and the start of the next `next()` function.
+`utils.nicely()` is a recursive function - that is - it calls itself
+(schedules itself actually) repeatedly until `hasNext` returns false.
#### Parameters
-
- * message - The message to create - (use `\n` for newlines)
- * foregroundBlock (default: black wool) - The block to use for the foreground
- * backgroundBlock (default: none) - The block to use for the background
-#### Example
+ * next : A function which will be called if processing is to be done.
+ * hasNext : A function which is called to determine if the `next`
+ callback should be invoked. This should return a boolean value -
+ true if the `next` function should be called (processing is not
+ complete), false otherwise.
+ * onDone : A function which is to be called when all processing is complete (hasNext returned false).
+ * delayInMilliseconds : The delay between each call.
-To create a 2-line high message using glowstone...
+#### Example
- blocktype("Hello\nWorld",blocks.glowstone);
+See the source code to utils.foreach for an example of how utils.nicely is used.
-![blocktype example][imgbt1]
+### utils.time( world ) function
-[imgbt1]: img/blocktype1.png
+Returns the timeofday (in minecraft ticks) for the given world. This function is necessary because
+canarymod and bukkit differ in how the timeofday is calculated.
-### Drone.rainbow() method
+See http://minecraft.gamepedia.com/Day-night_cycle#Conversions
-Creates a Rainbow.
+### utils.time24( world ) function
-#### Parameters
+Returns the timeofday for the given world using 24 hour notation. (number of minutes)
- * radius (optional - default:18) - The radius of the rainbow
+See http://minecraft.gamepedia.com/Day-night_cycle#Conversions
-#### Example
-
- var d = new Drone();
- d.rainbow(30);
+#### Parameters
-
+ * world : the name of the world or world object for which you want to get time
-### Drone.sphere() method
+### utils.find() function
-Creates a sphere.
+The utils.find() function will return a list of all files starting at
+a given directory and recursiving trawling all sub-directories.
#### Parameters
-
- * block - The block the sphere will be made of.
- * radius - The radius of the sphere.
-
-#### Example
-To create a sphere of Iron with a radius of 10 blocks...
+ * dir : The starting path. Must be a string.
+ * filter : (optional) A [FilenameFilter][fnfltr] object to return only files matching a given pattern.
- sphere( blocks.iron, 10);
+[fnfltr]: http://docs.oracle.com/javase/6/docs/api/java/io/FilenameFilter.html
-
+#### Example
-Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
-server to be very busy for a couple of minutes while doing so.
+```javascript
+var utils = require('utils');
+var jsFiles = utils.find('./', function(dir,name){
+ return name.match(/\.js$/);
+});
+```
+### utils.serverAddress() function
-### Drone.sphere0() method
+The utils.serverAddress() function returns the IP(v4) address of the server.
-Creates an empty sphere.
+```javascript
+var utils = require('utils');
+var serverAddress = utils.serverAddress();
+console.log(serverAddress);
+```
+### utils.array() function
-#### Parameters
+Converts Java collection objects to type Javascript array so they can avail of
+all of Javascript's Array goodness.
- * block - The block the sphere will be made of.
- * radius - The radius of the sphere.
-
#### Example
-To create a sphere of Iron with a radius of 10 blocks...
+ var utils = require('utils');
+ var worlds = utils.array(server.worldManager.getAllWorlds());
+
+### utils.players() function
- sphere0( blocks.iron, 10);
+This function returns a javascript array of all online players on the
+server. You can optionally provide a function which will be invoked
+with each player as a parameter. For example, to give each player the
+ability to shoot arrows which launch fireworks:
-Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
-server to be very busy for a couple of minutes while doing so.
+```javascript
+require('utils').players( arrows.firework )
+```
-### Drone.hemisphere() method
+Any players with a bow will be able to launch fireworks by shooting.
-Creates a hemisphere. Hemispheres can be either north or south.
+### utils.playerNames() function
-#### Parameters
+This function returns a javascript array of player names (as javascript strings)
- * block - the block the hemisphere will be made of.
- * radius - the radius of the hemisphere
- * northSouth - whether the hemisphere is 'north' or 'south'
+### utils.stat() function
-#### Example
+This function returns a numeric value for a given player statistic.
-To create a wood 'north' hemisphere with a radius of 7 blocks...
+#### Parameters
- hemisphere(blocks.oak, 7, 'north');
+ * Player - The player object (optional - if only the statistic name parameter is provided then the statistic object is returned)
+ * Statistic - A string whose value should be one of the following (CanaryMod)
+ * ANIMALSBRED
+ * BOATONECM
+ * CLIMBONECM
+ * CROUCHONECM
+ * DAMAGEDEALT
+ * DAMAGETAKEN
+ * DEATHS
+ * DRIVEONECM
+ * DROP
+ * FALLONECM
+ * FISHCAUGHT
+ * FLYONECM
+ * HORSEONECM
+ * JUMP
+ * JUNKFISHED
+ * LEAVEGAME
+ * MINECARTONECM
+ * MOBKILLS
+ * PIGONECM
+ * PLAYERKILLS
+ * PLAYONEMINUTE
+ * SPRINTONECM
+ * SWIMONECM
+ * TALKEDTOVILLAGER
+ * TIMESINCEDEATH
+ * TRADEDWITHVILLAGER
+ * TREASUREFISHED
+ * WALKONECM
+
+See [CanaryMod's Statistic][cmstat] class for an up-to-date list of possible stat values
+
+[cmstat]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/statistics/Statistics.html
+
+#### Example 1 Getting stats for a player
-
+ var utils = require('utils');
+ var jumpCount = utils.stat( player, 'jump');
-### Drone.hemisphere0() method
+#### Example 2 Getting the JUMP statistic object (which can be used elsewhere)
-Creates a hollow hemisphere. Hemispheres can be either north or south.
+ var utils = require('utils');
+ var JUMPSTAT = utils.stat('jump');
+ var jumpCount = player.getStat( JUMPSTAT ); // canary-specific code
-#### Parameters
+This function also contains values for each possible stat so you can get at stats like this...
- * block - the block the hemisphere will be made of.
- * radius - the radius of the hemisphere
- * northSouth - whether the hemisphere is 'north' or 'south'
+ var utils = require('utils');
+ var JUMPSTAT = utils.stat.JUMP; // Accessing the value
+ var jumpCount = player.getStat ( JUMPSTAT ); // canary-specific code
+## The watcher Module
-#### Example
+This module exposes functions for watching for changes to files or directories.
-To create a glass 'north' hemisphere with a radius of 20 blocks...
+### watcher.watchFile() function
- hemisphere0(blocks.glass, 20, 'north');
+Watches for changes to the given file or directory and calls the function provided
+when the file changes.
-
+#### Parameters
+
+ * File - the file to watch (can be a file or directory)
+ * Callback - The callback to invoke when the file has changed. The callback takes the
+ changed file as a parameter.
-### Drone.spiral_stairs() method
+#### Example
-Constructs a spiral staircase with slabs at each corner.
+```javascript
+var watcher = require('watcher');
+watcher.watchFile( 'test.txt', function( file ) {
+ console.log( file + ' has changed');
+});
+```
+### watcher.watchDir() function
+
+Watches for changes to the given directory and calls the function provided
+when the directory changes. It works by calling watchFile/watchDir for each
+file/subdirectory.
#### Parameters
+
+ * Dir - the file to watch (can be a file or directory)
+ * Callback - The callback to invoke when the directory has changed.
+ The callback takes the changed file as a parameter.
+ For each change inside the directory the callback will also
+ be called.
- * stairBlock - The block to use for stairs, should be one of the following...
- - 'oak'
- - 'spruce'
- - 'birch'
- - 'jungle'
- - 'cobblestone'
- - 'brick'
- - 'stone'
- - 'nether'
- - 'sandstone'
- - 'quartz'
- * flights - The number of flights of stairs to build.
+#### Example
-
+```javascript
+var watcher = require('watcher');
+watcher.watchDir( 'players/_ial', function( dir ) {
+ console.log( dir + ' has changed');
+});
+```
+### watcher.unwatchFile() function
+
+Removes a file from the watch list.
#### Example
+```javascript
+var watcher = require('watcher');
+watcher.unwatchFile('test.txt');
+```
-To construct a spiral staircase 5 floors high made of oak...
+### watcher.unwatchDir() function
- spiral_stairs('oak', 5);
+Removes a directory from the watch list and all files inside the directory
+are also "unwatched"
+
+#### Example
+```javascript
+var watcher = require('watcher');
+watcher.unwatchDir ('players/_ial');
+```
+Would cause also
+```javascript
+watcher.unwatchFile (file);
+```
+for each file inside directory (and unwatchDir for each directory inside it)
## Example Plugin #1 - A simple extension to Minecraft.
@@ -3595,7 +5845,7 @@ The `hello` function below is only usable by players with the scriptcraft.evalua
permission since it relies on the `/js` command to execute.
exports.hello = function(player){
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
};
## Example Plugin #2 - Making extensions available for all players.
@@ -3621,7 +5871,7 @@ can use the new extension. Unlike the previous example, the `jsp hello`
command does not evaluate javascript code so this command is much more secure.
command('hello', function (parameters, player) {
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
});
## Example Plugin #3 - Limiting use of commands to operators only.
@@ -3647,11 +5897,11 @@ This differs from example 2 in that the function will only print a
message for operators.
command('op-hello', function (parameters, player) {
- if (!player.op){
- player.sendMessage('Only operators can do this.');
- return;
+ if ( !isOp(player) ){
+ echo( player, 'Only operators can do this.');
+ return;
}
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
});
## Example Plugin #4 - Using parameters in commands.
@@ -3675,7 +5925,7 @@ a fixed 'Hello ' to anything you like by passing a parameter.
command( 'hello-params', function ( parameters, player ) {
var salutation = parameters[0] ;
- player.sendMessage( salutation + ' ' + player.name );
+ echo( player, salutation + ' ' + player.name );
});
## Example Plugin #5 - Re-use - Using your own and others modules.
@@ -3751,7 +6001,7 @@ Source Code ...
if ( recipient ) {
greetings.hello( recipient );
} else {
- sender.sendMessage( 'Player ' + playerName + ' not found.' );
+ echo( sender, 'Player ' + playerName + ' not found.' );
}
});
@@ -3770,87 +6020,49 @@ following reasons...
`/scriptcraft/plugins` directory, it will be loaded automatically
when the server starts up.
- 2. It uses ScriptCraft's `events.on()` function to add a new *Event
- Handler*. An *Event Handler* is a just a function which gets
+ 2. It uses ScriptCraft's `events` module to add a new *Event
+ Handler*. An *Event Handler* is a function that gets
called whenever a particular *event* happens in the game. The
function defined below will only be executed whenever a player
joins the game. This style of program is sometimes refered to as
*Event-Driven Programming*.
-Adding new *Event Handlers* in ScriptCraft is relatively easy. Use the
-`events.on()` function to add a new event handler. It takes 2
-parameters...
+Adding new *Event Handlers* in ScriptCraft is relatively easy. Use one
+of the `events` module's functions to add a new event handler. The
+events module has many functions - one for each type of event. Each
+function takes a single parameter:
- 1. The Event Name, in this case `'player.PlayerJoinEvent'`. You can
- browse [all possible Bukkit events][bkevts] (click the 'Next
- Package' and 'Previous Package' links to browse).
-
- 2. The event handling function (also sometimes refered to as a
- 'callback'). In ScriptCraft, this function takes a single
- parameter, an event object. All of the information about the event
- is in the event object.
+ * The event handling function (also sometimes refered to as a
+ 'callback'). In ScriptCraft, this function takes a single
+ parameter, an event object. All of the information about the event
+ is in the event object.
In the example below, if a player joins the server and is an operator,
then the ScriptCraft plugin information will be displayed to that
player.
-What's also notable about this example is how it uses the [Bukkit
-API][bkapi]. The code...
-
- if (event.player.op)
-
-... is a succinct way of accessing object properties which in Java
-would have to be written as ...
-
- if (event.getPlayer().isOp())
-
-... ScriptCraft uses a special version of JavaScript which comes
-bundled with Java (Minecraft is written in Java) and JavaScript in
-Java can access properties of Java objects more succinctly than in
-Java itself. What this means in practice is that when you're perusing
-the [Bukkit API Reference][bkapi] and come across a method like
-[Player.getAllowFlight()][bkgaf], you can write code like this...
-
- var allowFlight = player.getAllowFlight(); // java style
-
-... or the more succinct ...
-
- var allowFlight = player.allowFlight; // javascript style
-
-... Which style you choose is up to you but `player.allowFlight` is
-cleaner and more readable. Similarly where you see a method like
-[Player.setAllowFlight()][bksaf], you can write ...
-
- player.setAllowFlight(true); // java style
-
-... or the more readable...
-
- player.allowFlight = true; // javascript style
-
-... Which style you choose is up to you.
-
-[bkevts]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/package-summary.html
-[bkgaf]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html#getAllowFlight()
-[bksaf]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html#setAllowFlight()
-[bkapi]: http://jd.bukkit.org/dev/apidocs/
-
- events.on( 'player.PlayerJoinEvent', function( event ) {
- if ( event.player.op ) {
- event.player.sendMessage('Welcome to ' + __plugin);
- }
- });
-
-Update: Since version 2.0.8 the above code can be replaced by the more succinct:
-
- events.playerJoin( function( event ) {
- if ( event.player.op ) {
- event.player.sendMessage('Welcome to ' + __plugin);
- }
- });
-
+```javascript
+function onJoin( event ){
+ if ( isOp(event.player) ) {
+ echo( event.player, 'Welcome to ' + __plugin );
+ }
+}
+events.connection( onJoin );
+```
+First the onJoin() function is defined, this is our event handler -
+the function we wish to be called every time some new player joins the
+game. Then we hook up - or register - that function using the
+events.connection() function. The events.connection function is the
+function responsible for adding new *connection* event handlers - that
+is - functions which should be invoked when there's a new *connection*
+event in the game. A new *connection* event is fired whenever a player
+joins the game. There are many other types of events you can handle in
+Minecraft. You can see [a full list of events here][cmEvtList].
+
+[cmEvtList]: #events-helper-module-canary-version
## Arrows Plugin
-The arrows mod adds fancy arrows to the game. Arrows which...
+The arrows mod adds fancy arrows to the game. Arrows which ...
* Launch fireworks.
* Explode on impact.
@@ -3866,7 +6078,7 @@ The arrows mod adds fancy arrows to the game. Arrows which...
* `/js arrows.flourish(self)` - makes a tree grow where the arrow lands.
* `/js arrows.explosive(self)` - makes arrows explode.
* `/js arrows.normal(self)` sets arrow type to normal.
- * `/js arrows.sign(self)` turns a targeted sign into a Arrows menu
+ * `/js arrows.sign(self)` turns a targeted sign into an Arrows menu
All of the above functions can take an optional player object or name
as a parameter. For example: `/js arrows.explosive('player23')` makes
@@ -3885,7 +6097,9 @@ Allows in-game operators to easily spawn creatures at current location.
This command supports TAB completion so to see a list of possible
entitities, type `/jsp spawn ' at the in-game command prompt, then
press TAB. Visit
-
+ (Bukkit/SpigotMC)
+or (CanaryMod)
+
for a list of possible entities (creatures) which can be spawned.
## alias Plugin
@@ -3938,118 +6152,6 @@ Aliases can be used at the in-game prompt by players or in the server
console. Aliases will not be able to avail of command autocompletion
(pressing the TAB key will have no effect).
-## chat Plugin
-
-This plugin lets players choose a text color to use when chatting. Players can list colors by typing...
-
- /jsp list_colors
-
-... and can set the color to use when chatting by typing...
-
- /jsp chat_color {color}
-
-... where {color} is one of the following colors...
-
- * black
- * blue
- * darkgreen
- * darkaqua
- * darkred
- * purple
- * gold
- * gray
- * darkgray
- * indigo
- * brightgreen
- * aqua
- * red
- * pink
- * yellow
- * white
-
-This plugin's source code is useful to study because it is short and demonstrates use of the `plugin()`, and `command()` functions, persistence and event handling.
-
-## Classroom Plugin
-
-The `classroom` object contains a couple of utility functions for use
-in a classroom setting. The goal of these functions is to make it
-easier for tutors to facilitate ScriptCraft for use by students in a
-classroom environment. Although granting ScriptCraft access to
-students on a shared server is potentially risky (Students can
-potentially abuse it), it is slighlty less risky than granting
-operator privileges to each student. (Enterprising students will
-quickly realise how to grant themselves and others operator privileges
-once they have access to ScriptCraft).
-
-The goal of this module is not so much to enforce restrictions
-(security or otherwise) but to make it easier for tutors to setup a
-shared server so students can learn Javascript. When scripting is
-turned on, every player who joins the server will have a dedicated
-directory into which they can save scripts. All scripts in such
-directories are automatically watched and loaded into a global
-variable named after the player.
-
-So for example, if player 'walterh' joins the server, a `walterh`
-global variable is created. If a file `greet.js` with the following
-content is dropped into the `plugins/scriptcraft/players/walterh`
-directory...
-
-```javascript
-exports.hi = function( player ){
- player.sendMessage('Hi ' + player.name);
-};
-```
-
-... then it can be invoked like this: `/js walterh.hi( self )` . This
-lets every player/student create their own functions without having
-naming collisions.
-
-It's strongly recommended that the
-`craftbukkit/plugins/scriptcraft/players/` directory is shared so that
-others can connect to it and drop .js files into their student
-directories. On Ubuntu, select the folder in Nautilus (the default
-file browser) then right-click and choose *Sharing Options*, check the
-*Share this folder* checkbox and the *Allow others to create and
-delete files* and *Guest access* checkboxes. Click *Create Share*
-button to close the sharing options dialog. Students can then access
-the shared folder as follows...
-
- * Windows: Open Explorer, Go to \\{serverAddress}\players\
- * Macintosh: Open Finder, Go to smb://{serverAddress}/players/
- * Linux: Open Nautilus, Go to smb://{serverAddress}/players/
-
-... where {serverAddress} is the ip address of the server (this is
-displayed to whoever invokes the classroom.allowScripting() function.)
-
-### classroom.allowScripting() function
-
-Allow or disallow anyone who connects to the server (or is already
-connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
-to every student in a Minecraft classroom environment.
-
-Whenever any file is added/edited or removed from any of the players/
-directories the contents are automatically reloaded. This is to
-facilitate quick turnaround time for students getting to grips with
-Javascript.
-
-#### Parameters
-
- * canScript : true or false
-
-#### Example
-
-To allow all players (and any players who connect to the server) to
-use the `js` and `jsp` commands...
-
- /js classroom.allowScripting( true, self )
-
-To disallow scripting (and prevent players who join the server from using the commands)...
-
- /js classroom.allowScripting( false, self )
-
-Only ops users can run the classroom.allowScripting() function - this is so that students
-don't try to bar themselves and each other from scripting.
-
## Commando Plugin
### Description
@@ -4059,7 +6161,7 @@ to Minecraft. Normally ScriptCraft only allows for provision of new
commands as extensions to the jsp command. For example, to create a
new simple command for use by all players...
- /js command('hi', function(args,player){ player.sendMessage('Hi ' + player.name); });
+ /js command('hi', function(args,player){ echo( player, 'Hi ' + player.name); });
... then players can use this command by typing...
@@ -4097,7 +6199,7 @@ of the ScriptCraft core.
var commando = require('../commando');
commando('hi', function(args,player){
- player.sendMessage('Hi ' + player.name);
+ echo( player, 'Hi ' + player.name);
});
...Displays a greeting to any player who issues the `/hi` command.
@@ -4200,47 +6302,6 @@ code is to demonstrate use of Bukkit's Conversation API.
Once the game begins, guess a number by typing the `/` character
followed by a number between 1 and 10.
-## SnowballFight mini-game
-
-### Description
-
-This is a rough and ready prototype of a simple multi-player
-shoot-em-up. To start a game with all players playing against one another...
-
- /js new Game_SnowballFight(60).start();
-
-... this obviously works best if all of the players are in close
-proximity within the same game world. Alternatively you can have team
-matches...
-
-
- /js var redTeam = ['','',...etc]
- /js var blueTeam = ['',',...etc]
- /js var greenTeam = ['',',...etc]
- /js new Game_SnowballFight(60, {red: redTeam,blue: blueTeam,green: greenTeam}).start();
-
-Or you can just have specific players play against each other...
-
- /js new Game_SnowballFight(60, ['player1','player2','player3']).start();
-
-(where 'player1' etc are the names of actual players)
-
-You specify the teams in the game as an object where each property's
-name is a team name and each property's value is the list of players
-on that team. You specify the duration of the game (in seconds) You
-kick off the game with the start() method. I need to work on a
-better in-game mechanism for players to choose teams and start the
-game but this will do for now.
-
-When the game starts, each player is put in survival mode and given
-snowballs. The aim of the game is to hit players on opposing teams. If
-you hit a player on your own team, you lose a point.
-
-At the end of the game the scores for each team are broadcast and each
-player returns to their previous mode of play (creative or
-survival). Create a small arena with a couple of small buildings for
-cover to make the game more fun.
-
## Cow Clicker Mini-Game
### How to Play
@@ -4281,3 +6342,1117 @@ your own mini-game...
[bukscore]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scoreboard/package-summary.html
+
+## Items module (SpigotMC version)
+The Items module provides a suite of functions - one for each possible item.
+See https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html for a list of possible items
+
+### Usage
+
+ items.book(); // returns org.bukkit.Material.BOOK
+ items.book(2); // returns a new org.bukkit.Material object with an amount 2 (2 books)
+ items.book( itemType ); // compares itemType parameter to org.bukkit.Material.BOOK or an Item of type book
+
+The following functions are provided:
+
+ * acaciaDoor()
+ * acaciaDoorItem()
+ * acaciaFence()
+ * acaciaFenceGate()
+ * acaciaStairs()
+ * activatorRail()
+ * air()
+ * anvil()
+ * apple()
+ * armorStand()
+ * arrow()
+ * bakedPotato()
+ * banner()
+ * barrier()
+ * beacon()
+ * bed()
+ * bedBlock()
+ * bedrock()
+ * beetroot()
+ * beetrootBlock()
+ * beetrootSeeds()
+ * beetrootSoup()
+ * birchDoor()
+ * birchDoorItem()
+ * birchFence()
+ * birchFenceGate()
+ * birchWoodStairs()
+ * blackShulkerBox()
+ * blazePowder()
+ * blazeRod()
+ * blueShulkerBox()
+ * boat()
+ * boatAcacia()
+ * boatBirch()
+ * boatDarkOak()
+ * boatJungle()
+ * boatSpruce()
+ * bone()
+ * boneBlock()
+ * book()
+ * bookAndQuill()
+ * bookshelf()
+ * bow()
+ * bowl()
+ * bread()
+ * brewingStand()
+ * brewingStandItem()
+ * brick()
+ * brickStairs()
+ * brownMushroom()
+ * brownShulkerBox()
+ * bucket()
+ * burningFurnace()
+ * cactus()
+ * cake()
+ * cakeBlock()
+ * carpet()
+ * carrot()
+ * carrotItem()
+ * carrotStick()
+ * cauldron()
+ * cauldronItem()
+ * chainmailBoots()
+ * chainmailChestplate()
+ * chainmailHelmet()
+ * chainmailLeggings()
+ * chest()
+ * chorusFlower()
+ * chorusFruit()
+ * chorusFruitPopped()
+ * chorusPlant()
+ * clay()
+ * clayBall()
+ * clayBrick()
+ * coal()
+ * coalBlock()
+ * coalOre()
+ * cobbleWall()
+ * cobblestone()
+ * cobblestoneStairs()
+ * cocoa()
+ * command()
+ * commandChain()
+ * commandMinecart()
+ * commandRepeating()
+ * compass()
+ * cookedBeef()
+ * cookedChicken()
+ * cookedFish()
+ * cookedMutton()
+ * cookedRabbit()
+ * cookie()
+ * crops()
+ * cyanShulkerBox()
+ * darkOakDoor()
+ * darkOakDoorItem()
+ * darkOakFence()
+ * darkOakFenceGate()
+ * darkOakStairs()
+ * daylightDetector()
+ * daylightDetectorInverted()
+ * deadBush()
+ * detectorRail()
+ * diamond()
+ * diamondAxe()
+ * diamondBarding()
+ * diamondBlock()
+ * diamondBoots()
+ * diamondChestplate()
+ * diamondHelmet()
+ * diamondHoe()
+ * diamondLeggings()
+ * diamondOre()
+ * diamondPickaxe()
+ * diamondSpade()
+ * diamondSword()
+ * diode()
+ * diodeBlockOff()
+ * diodeBlockOn()
+ * dirt()
+ * dispenser()
+ * doublePlant()
+ * doubleStep()
+ * doubleStoneSlab2()
+ * dragonEgg()
+ * dragonsBreath()
+ * dropper()
+ * egg()
+ * elytra()
+ * emerald()
+ * emeraldBlock()
+ * emeraldOre()
+ * emptyMap()
+ * enchantedBook()
+ * enchantmentTable()
+ * endBricks()
+ * endCrystal()
+ * endGateway()
+ * endRod()
+ * enderChest()
+ * enderPearl()
+ * enderPortal()
+ * enderPortalFrame()
+ * enderStone()
+ * expBottle()
+ * explosiveMinecart()
+ * eyeOfEnder()
+ * feather()
+ * fence()
+ * fenceGate()
+ * fermentedSpiderEye()
+ * fire()
+ * fireball()
+ * firework()
+ * fireworkCharge()
+ * fishingRod()
+ * flint()
+ * flintAndSteel()
+ * flowerPot()
+ * flowerPotItem()
+ * frostedIce()
+ * furnace()
+ * ghastTear()
+ * glass()
+ * glassBottle()
+ * glowingRedstoneOre()
+ * glowstone()
+ * glowstoneDust()
+ * goldAxe()
+ * goldBarding()
+ * goldBlock()
+ * goldBoots()
+ * goldChestplate()
+ * goldHelmet()
+ * goldHoe()
+ * goldIngot()
+ * goldLeggings()
+ * goldNugget()
+ * goldOre()
+ * goldPickaxe()
+ * goldPlate()
+ * goldRecord()
+ * goldSpade()
+ * goldSword()
+ * goldenApple()
+ * goldenCarrot()
+ * grass()
+ * grassPath()
+ * gravel()
+ * grayShulkerBox()
+ * greenRecord()
+ * greenShulkerBox()
+ * grilledPork()
+ * hardClay()
+ * hayBlock()
+ * hopper()
+ * hopperMinecart()
+ * hugeMushroom1()
+ * hugeMushroom2()
+ * ice()
+ * inkSack()
+ * ironAxe()
+ * ironBarding()
+ * ironBlock()
+ * ironBoots()
+ * ironChestplate()
+ * ironDoor()
+ * ironDoorBlock()
+ * ironFence()
+ * ironHelmet()
+ * ironHoe()
+ * ironIngot()
+ * ironLeggings()
+ * ironNugget()
+ * ironOre()
+ * ironPickaxe()
+ * ironPlate()
+ * ironSpade()
+ * ironSword()
+ * ironTrapdoor()
+ * itemFrame()
+ * jackOLantern()
+ * jukebox()
+ * jungleDoor()
+ * jungleDoorItem()
+ * jungleFence()
+ * jungleFenceGate()
+ * jungleWoodStairs()
+ * ladder()
+ * lapisBlock()
+ * lapisOre()
+ * lava()
+ * lavaBucket()
+ * leash()
+ * leather()
+ * leatherBoots()
+ * leatherChestplate()
+ * leatherHelmet()
+ * leatherLeggings()
+ * leaves()
+ * leaves2()
+ * lever()
+ * lightBlueShulkerBox()
+ * limeShulkerBox()
+ * lingeringPotion()
+ * log()
+ * log2()
+ * longGrass()
+ * magentaShulkerBox()
+ * magma()
+ * magmaCream()
+ * map()
+ * melon()
+ * melonBlock()
+ * melonSeeds()
+ * melonStem()
+ * milkBucket()
+ * minecart()
+ * mobSpawner()
+ * monsterEgg()
+ * monsterEggs()
+ * mossyCobblestone()
+ * mushroomSoup()
+ * mutton()
+ * mycel()
+ * nameTag()
+ * netherBrick()
+ * netherBrickItem()
+ * netherBrickStairs()
+ * netherFence()
+ * netherStalk()
+ * netherStar()
+ * netherWartBlock()
+ * netherWarts()
+ * netherrack()
+ * noteBlock()
+ * observer()
+ * obsidian()
+ * orangeShulkerBox()
+ * packedIce()
+ * painting()
+ * paper()
+ * pinkShulkerBox()
+ * pistonBase()
+ * pistonExtension()
+ * pistonMovingPiece()
+ * pistonStickyBase()
+ * poisonousPotato()
+ * pork()
+ * portal()
+ * potato()
+ * potatoItem()
+ * potion()
+ * poweredMinecart()
+ * poweredRail()
+ * prismarine()
+ * prismarineCrystals()
+ * prismarineShard()
+ * pumpkin()
+ * pumpkinPie()
+ * pumpkinSeeds()
+ * pumpkinStem()
+ * purpleShulkerBox()
+ * purpurBlock()
+ * purpurDoubleSlab()
+ * purpurPillar()
+ * purpurSlab()
+ * purpurStairs()
+ * quartz()
+ * quartzBlock()
+ * quartzOre()
+ * quartzStairs()
+ * rabbit()
+ * rabbitFoot()
+ * rabbitHide()
+ * rabbitStew()
+ * rails()
+ * rawBeef()
+ * rawChicken()
+ * rawFish()
+ * record10()
+ * record11()
+ * record12()
+ * record3()
+ * record4()
+ * record5()
+ * record6()
+ * record7()
+ * record8()
+ * record9()
+ * redMushroom()
+ * redNetherBrick()
+ * redRose()
+ * redSandstone()
+ * redSandstoneStairs()
+ * redShulkerBox()
+ * redstone()
+ * redstoneBlock()
+ * redstoneComparator()
+ * redstoneComparatorOff()
+ * redstoneComparatorOn()
+ * redstoneLampOff()
+ * redstoneLampOn()
+ * redstoneOre()
+ * redstoneTorchOff()
+ * redstoneTorchOn()
+ * redstoneWire()
+ * rottenFlesh()
+ * saddle()
+ * sand()
+ * sandstone()
+ * sandstoneStairs()
+ * sapling()
+ * seaLantern()
+ * seeds()
+ * shears()
+ * shield()
+ * shulkerShell()
+ * sign()
+ * signPost()
+ * silverShulkerBox()
+ * skull()
+ * skullItem()
+ * slimeBall()
+ * slimeBlock()
+ * smoothBrick()
+ * smoothStairs()
+ * snow()
+ * snowBall()
+ * snowBlock()
+ * soil()
+ * soulSand()
+ * speckledMelon()
+ * spectralArrow()
+ * spiderEye()
+ * splashPotion()
+ * sponge()
+ * spruceDoor()
+ * spruceDoorItem()
+ * spruceFence()
+ * spruceFenceGate()
+ * spruceWoodStairs()
+ * stainedClay()
+ * stainedGlass()
+ * stainedGlassPane()
+ * standingBanner()
+ * stationaryLava()
+ * stationaryWater()
+ * step()
+ * stick()
+ * stone()
+ * stoneAxe()
+ * stoneButton()
+ * stoneHoe()
+ * stonePickaxe()
+ * stonePlate()
+ * stoneSlab2()
+ * stoneSpade()
+ * stoneSword()
+ * storageMinecart()
+ * string()
+ * structureBlock()
+ * structureVoid()
+ * sugar()
+ * sugarCane()
+ * sugarCaneBlock()
+ * sulphur()
+ * thinGlass()
+ * tippedArrow()
+ * tnt()
+ * torch()
+ * totem()
+ * trapDoor()
+ * trappedChest()
+ * tripwire()
+ * tripwireHook()
+ * vine()
+ * wallBanner()
+ * wallSign()
+ * watch()
+ * water()
+ * waterBucket()
+ * waterLily()
+ * web()
+ * wheat()
+ * whiteShulkerBox()
+ * wood()
+ * woodAxe()
+ * woodButton()
+ * woodDoor()
+ * woodDoubleStep()
+ * woodHoe()
+ * woodPickaxe()
+ * woodPlate()
+ * woodSpade()
+ * woodStairs()
+ * woodStep()
+ * woodSword()
+ * woodenDoor()
+ * wool()
+ * workbench()
+ * writtenBook()
+ * yellowFlower()
+ * yellowShulkerBox()
+
+
+## Items module (CanaryMod version)
+The Items module provides a suite of functions - one for each possible item.
+See https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/inventory/ItemType.html for a list of possible items
+
+### Usage
+
+ items.book(); // returns net.canarymod.api.inventory.ItemType.Book
+ items.book(2); // returns a new net.canarymod.api.inventory.Item object with an amount 2 (2 books)
+ items.book( itemType ); // compares itemType parameter to ItemType.Book or an Item of type book
+
+The following functions are provided:
+
+ * acaciaDoor()
+ * acaciaFence()
+ * acaciaFenceGate()
+ * acaciaLeaves()
+ * acaciaLog()
+ * acaciaSapling()
+ * acaciaStairs()
+ * acaciaWood()
+ * acaciaWoodSlab()
+ * activatorRail()
+ * allium()
+ * andesite()
+ * anvil()
+ * apple()
+ * armorStand()
+ * arrow()
+ * azureBluet()
+ * bakedPotato()
+ * banner()
+ * beacon()
+ * bed()
+ * bedrock()
+ * birchDoor()
+ * birchFence()
+ * birchFenceGate()
+ * birchLeaves()
+ * birchLog()
+ * birchSapling()
+ * birchWood()
+ * birchWoodSlab()
+ * birchWoodStairs()
+ * blackCarpet()
+ * blackGlass()
+ * blackGlassPane()
+ * blackStainedClay()
+ * blazePowder()
+ * blazeRod()
+ * blocksRecord()
+ * blueCarpet()
+ * blueGlass()
+ * blueGlassPane()
+ * blueOrchid()
+ * blueStainedClay()
+ * boat()
+ * bone()
+ * bonemeal()
+ * book()
+ * bookAndQuill()
+ * bookshelf()
+ * bottleOEnchanting()
+ * bow()
+ * bowl()
+ * bread()
+ * brewingStand()
+ * brickBlock()
+ * brickSlab()
+ * brickStairs()
+ * brownCarpet()
+ * brownGlass()
+ * brownGlassPane()
+ * brownMushroom()
+ * brownStainedClay()
+ * bucket()
+ * burningFurnace()
+ * cactus()
+ * cactusGreen()
+ * cake()
+ * carrot()
+ * carrotOnAStick()
+ * carrots()
+ * cauldron()
+ * chainmailBoots()
+ * chainmailChestplate()
+ * chainmailHelmet()
+ * chainmailLeggings()
+ * charcoal()
+ * chest()
+ * chirpRecord()
+ * clay()
+ * clayBall()
+ * clayBrick()
+ * clownFish()
+ * coal()
+ * coalBlock()
+ * coalOre()
+ * coarseDirt()
+ * cobble()
+ * cobbleSilverFishBlock()
+ * cobbleSlab()
+ * cobbleStairs()
+ * cobblestoneWall()
+ * cocoaBeans()
+ * cocoaPlant()
+ * commandBlock()
+ * compass()
+ * cookedChicken()
+ * cookedClownFish()
+ * cookedFish()
+ * cookedMutton()
+ * cookedPufferFish()
+ * cookedRabbit()
+ * cookedSalmon()
+ * cookie()
+ * crackedSilverFishBlock()
+ * crackedStoneBrick()
+ * creeperHead()
+ * cyanCarpet()
+ * cyanDye()
+ * cyanGlass()
+ * cyanGlassPane()
+ * cyanStainedClay()
+ * dandelionYellow()
+ * darkOakDoor()
+ * darkOakFence()
+ * darkOakFenceGate()
+ * darkOakLeaves()
+ * darkOakLog()
+ * darkOakSapling()
+ * darkOakStairs()
+ * darkOakWood()
+ * darkOakWoodSlab()
+ * daylightSensor()
+ * deadBush()
+ * detectorRail()
+ * diamond()
+ * diamondAxe()
+ * diamondBlock()
+ * diamondBoots()
+ * diamondChestplate()
+ * diamondHelmet()
+ * diamondHoe()
+ * diamondHorseArmor()
+ * diamondLeggings()
+ * diamondOre()
+ * diamondPickaxe()
+ * diamondSpade()
+ * diamondSword()
+ * diorite()
+ * dirt()
+ * dispenser()
+ * doubleAcaciaWoodSlab()
+ * doubleBirchWoodSlab()
+ * doubleBrickBlockSlab()
+ * doubleCobbleSlab()
+ * doubleDarkOakWoodSlab()
+ * doubleGrass()
+ * doubleJungleWoodSlab()
+ * doubleNetherBrickSlab()
+ * doubleOakWoodSlab()
+ * doubleOrnateStoneSlab()
+ * doubleQuartzSlab()
+ * doubleRedSandstoneSlab()
+ * doubleSandStoneTrimSlab()
+ * doubleSandstoneSlab()
+ * doubleSpruceWoodSlab()
+ * doubleStoneBricksSlab()
+ * doubleStoneSlab()
+ * doubleWoodSlab()
+ * dropper()
+ * egg()
+ * elevenRecord()
+ * emerald()
+ * emeraldBlock()
+ * emeraldOre()
+ * emptyMap()
+ * enchantedBook()
+ * enchantmentTable()
+ * endPortal()
+ * endPortalFrame()
+ * endStone()
+ * enderChest()
+ * enderDragonEgg()
+ * enderPearl()
+ * eyeofEnder()
+ * farRecord()
+ * feather()
+ * fence()
+ * fenceGate()
+ * fermentedSpiderEye()
+ * fireBlock()
+ * fireCharge()
+ * fireworkRocket()
+ * fireworkStar()
+ * fishingRod()
+ * flint()
+ * flintAndSteel()
+ * flowerPot()
+ * furnace()
+ * ghastTear()
+ * glass()
+ * glassBottle()
+ * glassPane()
+ * glisteringMelon()
+ * glowStone()
+ * glowstoneDust()
+ * goldAxe()
+ * goldBlock()
+ * goldBoots()
+ * goldChestplate()
+ * goldHelmet()
+ * goldHoe()
+ * goldHorseArmor()
+ * goldIngot()
+ * goldLeggings()
+ * goldNugget()
+ * goldOre()
+ * goldPickaxe()
+ * goldRecord()
+ * goldSpade()
+ * goldSword()
+ * goldenApple()
+ * goldenCarrot()
+ * granite()
+ * grass()
+ * gravel()
+ * grayCarpet()
+ * grayDye()
+ * grayGlass()
+ * grayGlassPane()
+ * grayStainedClay()
+ * greenCarpet()
+ * greenGlass()
+ * greenGlassPane()
+ * greenRecord()
+ * greenStainedClay()
+ * grilledPork()
+ * gunpowder()
+ * hardenedClay()
+ * hayBale()
+ * heavyWeightedPressurePlate()
+ * hopper()
+ * hugeBrownMushroom()
+ * hugeRedMushroom()
+ * humanHead()
+ * ice()
+ * inkSack()
+ * ironAxe()
+ * ironBars()
+ * ironBlock()
+ * ironBoots()
+ * ironChestplate()
+ * ironDoor()
+ * ironHelmet()
+ * ironHoe()
+ * ironHorseArmor()
+ * ironIngot()
+ * ironLeggings()
+ * ironOre()
+ * ironPickaxe()
+ * ironSpade()
+ * ironSword()
+ * itemFrame()
+ * jackOLantern()
+ * jukebox()
+ * jungleDoor()
+ * jungleFence()
+ * jungleFenceGate()
+ * jungleLeaves()
+ * jungleLog()
+ * jungleSapling()
+ * jungleWood()
+ * jungleWoodSlab()
+ * jungleWoodStairs()
+ * ladder()
+ * lapisBlock()
+ * lapisLazuli()
+ * lapislazuliOre()
+ * largeFern()
+ * lava()
+ * lavaBucket()
+ * lavaFlowing()
+ * lead()
+ * leather()
+ * leatherBoots()
+ * leatherChestplate()
+ * leatherHelmet()
+ * leatherLeggings()
+ * lever()
+ * lightBlueCarpet()
+ * lightBlueDye()
+ * lightBlueGlass()
+ * lightBlueGlassPane()
+ * lightBlueStainedClay()
+ * lightGrayCarpet()
+ * lightGrayDye()
+ * lightGrayGlass()
+ * lightGrayGlassPane()
+ * lightGrayStainedClay()
+ * lightWeightedPressurePlate()
+ * lilac()
+ * lilypad()
+ * limeCarpet()
+ * limeDye()
+ * limeGlass()
+ * limeGlassPane()
+ * limeStainedClay()
+ * magentaCarpet()
+ * magentaDye()
+ * magentaGlass()
+ * magentaGlassPane()
+ * magentaStainedClay()
+ * magmaCream()
+ * mallRecord()
+ * map()
+ * mellohiRecord()
+ * melon()
+ * melonSeeds()
+ * melonSlice()
+ * milkBucket()
+ * minecart()
+ * minecartCommandBlock()
+ * minecartHopper()
+ * minecartTNT()
+ * mobSpawner()
+ * mossyBrickSilverFishBlock()
+ * mossyCobble()
+ * mossyCobbleWall()
+ * mossyStoneBrick()
+ * mushroomSoup()
+ * mycelium()
+ * nameTag()
+ * netherBrick()
+ * netherBrickFence()
+ * netherBrickStairs()
+ * netherBricks()
+ * netherBricksSlab()
+ * netherQuartz()
+ * netherQuartzOre()
+ * netherStar()
+ * netherWart()
+ * netherrack()
+ * noteBlock()
+ * oakLeaves()
+ * oakLog()
+ * oakSapling()
+ * oakWood()
+ * oakWoodSlab()
+ * obsidian()
+ * orangeCarpet()
+ * orangeDye()
+ * orangeGlass()
+ * orangeGlassPane()
+ * orangeStainedClay()
+ * orangeTulip()
+ * ornateQuartzBlock()
+ * ornateSilverFishBlock()
+ * ornateStoneBrick()
+ * ornateStoneSlab()
+ * oxeyeDaisy()
+ * packedIce()
+ * painting()
+ * paper()
+ * peony()
+ * pineLeaves()
+ * pineLog()
+ * pineWoodStairs()
+ * pinkCarpet()
+ * pinkDye()
+ * pinkGlass()
+ * pinkGlassPane()
+ * pinkStainedClay()
+ * pinkTulip()
+ * piston()
+ * podzol()
+ * poisonousPotato()
+ * polishedAndesite()
+ * polishedDiorite()
+ * polishedGranite()
+ * poppy()
+ * pork()
+ * portal()
+ * potato()
+ * potatoes()
+ * potion()
+ * poweredMinecart()
+ * poweredRail()
+ * prismarineCrystals()
+ * prismarineShard()
+ * pufferFish()
+ * pumpkin()
+ * pumpkinPie()
+ * pumpkinSeeds()
+ * purpleCarpet()
+ * purpleDye()
+ * purpleGlass()
+ * purpleGlassPane()
+ * purpleStainedClay()
+ * quartzBlock()
+ * quartzPillarCap()
+ * quartzPillarHorizontal()
+ * quartzPillarVertical()
+ * quartzSlab()
+ * quartzStairs()
+ * rabbitFoot()
+ * rabbitHide()
+ * rabbitStew()
+ * rail()
+ * rawBeef()
+ * rawChicken()
+ * rawFish()
+ * rawMutton()
+ * rawRabbit()
+ * rawSalmon()
+ * redCarpet()
+ * redGlass()
+ * redGlassPane()
+ * redMushroom()
+ * redSandstone()
+ * redSandstoneBlank()
+ * redSandstoneOrnate()
+ * redSandstoneSlab()
+ * redSandstoneStairs()
+ * redStainedClay()
+ * redStone()
+ * redTulip()
+ * redstoneBlock()
+ * redstoneComparator()
+ * redstoneLampOff()
+ * redstoneOre()
+ * redstoneRepeater()
+ * redstoneTorchOn()
+ * reed()
+ * roseBush()
+ * roseRed()
+ * rottenFlesh()
+ * saddle()
+ * sand()
+ * sandStoneTrimSlab()
+ * sandstone()
+ * sandstoneBlank()
+ * sandstoneOrnate()
+ * sandstoneSlab()
+ * sandstoneStairs()
+ * seeds()
+ * shears()
+ * shrub()
+ * sign()
+ * skeletonHead()
+ * slimeBall()
+ * snow()
+ * snowBall()
+ * snowBlock()
+ * soil()
+ * soulSand()
+ * spawnEgg()
+ * spiderEye()
+ * spiderWeb()
+ * sponge()
+ * spruceDoor()
+ * spruceFence()
+ * spruceFenceGate()
+ * spruceSapling()
+ * spruceWood()
+ * spruceWoodSlab()
+ * stalRecord()
+ * steak()
+ * stick()
+ * stickyPiston()
+ * stone()
+ * stoneAxe()
+ * stoneBrick()
+ * stoneBrickSilverFishBlock()
+ * stoneBrickStairs()
+ * stoneBricksSlab()
+ * stoneButton()
+ * stoneHoe()
+ * stonePickaxe()
+ * stonePlate()
+ * stoneSilverFishBlock()
+ * stoneSlab()
+ * stoneSpade()
+ * stoneSword()
+ * storageMinecart()
+ * stradRecord()
+ * string()
+ * sugar()
+ * sunflower()
+ * tallFern()
+ * tallGrass()
+ * tnt()
+ * torch()
+ * trapdoor()
+ * trappedChest()
+ * tripwireHook()
+ * vines()
+ * waitRecord()
+ * wardRecord()
+ * watch()
+ * water()
+ * waterBucket()
+ * waterFlowing()
+ * wheat()
+ * whiteCarpet()
+ * whiteGlass()
+ * whiteGlassPane()
+ * whiteStainedClay()
+ * whiteTulip()
+ * witherSkeletonHead()
+ * woodAxe()
+ * woodDoor()
+ * woodHoe()
+ * woodPickaxe()
+ * woodPlate()
+ * woodSlab()
+ * woodSpade()
+ * woodSword()
+ * woodenButton()
+ * woodenStairs()
+ * woolBlack()
+ * woolBlue()
+ * woolBrown()
+ * woolCyan()
+ * woolDarkGreen()
+ * woolGray()
+ * woolLightBlue()
+ * woolLightGray()
+ * woolLightGreen()
+ * woolMagenta()
+ * woolOrange()
+ * woolPink()
+ * woolPurple()
+ * woolRed()
+ * woolWhite()
+ * woolYellow()
+ * workbench()
+ * writtenBook()
+ * yellowCarpet()
+ * yellowFlower()
+ * yellowGlass()
+ * yellowGlassPane()
+ * yellowStainedClay()
+ * zombieHead()
+
+
+## Entities module
+The Entities module provides a suite of functions - one for each possible entity type.
+It acts as a helper or enumerated module to assist in use with the `spawn` module and command.
+This module is useful for TAB-completion at the in-game prompt.
+
+When each function is called with no parameters, it will return the appropriate EntityType object.
+For example `entities.polar_bear()` will return an `EntityType.POLAR_BEAR` object.
+
+When each function is called with a single parameter - an entity - the entity's type will be compared and return true or false.
+
+### Usage
+
+ entities.zombie(); // returns a SpigotMC/CanaryMod EntityType.ZOMBIE enum value
+ entities.zombie( mob ); // compares the entity's type to a zombie, returns true if mob type is zombie, false otherwise
+ entities.player( self ); // at the in-game prompt this should return true (compares self to a player entity type)
+ entities.rabbit( self ); // at the in-game prompt this should return false (compares self to a rabbit entity type)
+
+The following functions are provided:
+
+ * area_effect_cloud()
+ * armor_stand()
+ * arrow()
+ * bat()
+ * blaze()
+ * boat()
+ * cave_spider()
+ * chicken()
+ * complex_part()
+ * cow()
+ * creeper()
+ * donkey()
+ * dragon_fireball()
+ * dropped_item()
+ * egg()
+ * elder_guardian()
+ * ender_crystal()
+ * ender_dragon()
+ * ender_pearl()
+ * ender_signal()
+ * enderman()
+ * endermite()
+ * evoker()
+ * evoker_fangs()
+ * experience_orb()
+ * falling_block()
+ * fireball()
+ * firework()
+ * fishing_hook()
+ * ghast()
+ * giant()
+ * guardian()
+ * horse()
+ * husk()
+ * iron_golem()
+ * item_frame()
+ * leash_hitch()
+ * lightning()
+ * lingering_potion()
+ * llama()
+ * llama_spit()
+ * magma_cube()
+ * minecart()
+ * minecart_chest()
+ * minecart_command()
+ * minecart_furnace()
+ * minecart_hopper()
+ * minecart_mob_spawner()
+ * minecart_tnt()
+ * mule()
+ * mushroom_cow()
+ * ocelot()
+ * painting()
+ * pig()
+ * pig_zombie()
+ * player()
+ * polar_bear()
+ * primed_tnt()
+ * rabbit()
+ * sheep()
+ * shulker()
+ * shulker_bullet()
+ * silverfish()
+ * skeleton()
+ * skeleton_horse()
+ * slime()
+ * small_fireball()
+ * snowball()
+ * snowman()
+ * spectral_arrow()
+ * spider()
+ * splash_potion()
+ * squid()
+ * stray()
+ * thrown_exp_bottle()
+ * tipped_arrow()
+ * unknown()
+ * vex()
+ * villager()
+ * vindicator()
+ * weather()
+ * witch()
+ * wither()
+ * wither_skeleton()
+ * wither_skull()
+ * wolf()
+ * zombie()
+ * zombie_horse()
+ * zombie_villager()
+
diff --git a/docs/Anatomy-of-a-Plugin.md b/docs/Anatomy-of-a-Plugin.md
index 281bf624b..125b8e84b 100644
--- a/docs/Anatomy-of-a-Plugin.md
+++ b/docs/Anatomy-of-a-Plugin.md
@@ -1,141 +1,173 @@
# Anatomy of a ScriptCraft Plugin
-Anything you can do using a java-based plugin, you can do it
-faster and easier in Javascript with the ScriptCraft plugin. To
+Anything you can do using a Java-based plugin, you can do it
+faster and easier in JavaScript with the ScriptCraft plugin. To
demonstrate this, I've recreated a commonly-used mod (homes) in
-javascript. The `homes` javascript plugin lets players set their current
+javascript. The [homes][homes] JavaScript plugin lets players set their current
location as home and return to that location using in-game commands.
They can also visit other players' homes. It's a simple plugin that
-demonstrates a couple of new features in ScriptCraft ...
+demonstrates a couple of new features in ScriptCraft …
* Persistence
* Adding Player (non-operator) commands
-... First persistence. Persistence is the ability to retain state after
-the server has shutdown and started up again. Persistence is something
-you get for free if you create your javsacript plugin using the new
-`plugin()` function provided with ScriptCraft - just keep any data you
-want to save in a property called `store` and that data will be written
-and read at shutdown and startup. The data is persisted in JSON form so
-it's even somewhat human-readable. Declaring a new plugin is easy, you
-give your plugin a name, specify an interface/object and whether the
-plugin should be persistent. For this I'm going to create a new plugin
-called "chat" that will let players change the default color of their messages
-in the in-game chat window...
-
- var _store = {players: {}};
- exports.chat = plugin('chat', {
- setColor: function(player,chatColor) {
- _store.players[player.name] = chatColor;
- },
- store: _store
- }, true);
-
+[homes]: /src/main/js/plugins/homes/homes.js
+
+Here, I walk you through another useful plugin which lets players modify the color of the in-game chat.
+
+## Persistence
+… First persistence. Persistence is the ability to retain state
+after the server has shutdown and started up again. You can create a
+JavaScript object which will be saved at shutdown and reloaded at
+startup by using the built-in `persist()` function.
+
+```javascript
+// file: scriptcraft/plugins/my-first-plugin.js
+var prefs = persist('myprefs', {});
+...
+prefs.color = 'black';
+```
+In the example above, a new empty object is created and stored in a file called `myprefs-store.json`. The empty object is returned (if data is not already present in that file or the file does not exist) and any changes to the object's contents are written to the file when the server is shutdown.
+
+The data is persisted in JSON form so it's even somewhat
+human-readable. Declaring a new plugin is easy. I'm going to create a
+new plugin called "chat" that will let players change the default
+color of their messages in the in-game chat window …
+
+```javascript
+var store = persist('chat-colors', {players: {}});
+exports.chat = {
+ setColor: function(player,chatColor) {
+ store.players[player.name] = chatColor;
+ }
+}
+```
The above code doesn't do a whole lot other than let operators set a
player's color choice ( `/js chat.setColor(self, 'green')` ). A little
bit more code has to be added so that the player's text color will
change when chatting with other players, but the above code will ensure
the player's color setting is at least saved. The following code just
-ensures that when a player chats , the text will be displayed in their
-chosen color...
-
- var colors = ['black', 'blue', 'darkgreen', 'darkaqua', 'darkred',
- 'purple', 'gold', 'gray', 'darkgray', 'indigo',
- 'brightgreen', 'aqua', 'red', 'pink',
- 'yellow', 'white'];
- var colorCodes = {};
- for (var i =0;i < colors.length;i++) colorCodes[colors[i]] = i.toString(16);
-
- events.on( 'player.AsyncPlayerChatEvent', function( evt ) {
- var player = evt.player;
- var playerChatColor = _store.players[ player.name ];
- if ( playerChatColor ) {
- evt.message = '§' + colorCodes[ playerChatColor ] + e.message;
- }
- });
+ensures that when a player chats, the text will be displayed in their
+chosen color …
+
+```javascript
+var colors = ['black', 'blue', 'darkgreen', 'darkaqua', 'darkred',
+ 'purple', 'gold', 'gray', 'darkgray', 'indigo',
+ 'brightgreen', 'aqua', 'red', 'pink',
+ 'yellow', 'white'];
+var colorCodes = {};
+var COLOR_CHAR = '\u00a7';
+for (var i =0;i < colors.length;i++)
+ colorCodes[colors[i]] = i.toString(16);
+
+var addColor = function( evt ) {
+ var player = evt.player;
+ var playerChatColor = store.players[ player.name ];
+ if ( playerChatColor ) {
+ evt.message = COLOR_CHAR + colorCodes[ playerChatColor ] + evt.message;
+ }
+};
+
+if (__plugin.bukkit) {
+ events.asyncPlayerChat(addColor);
+} else if (__plugin.canary) {
+ events.chat(addColor);
+};
+```
The next step is to declare a lookup table of colors / names and add an event
handler which intercepts and inserts color codes into player's text
messages.
-The other command in ScriptCraft is the `/jsp` command - this lets
+## Adding new Player Commands
+The other command in ScriptCraft is the `/jsp` command – this lets
operators expose plugins for use by regular players. To be clear, `/jsp`
-does not do any javascript evaluation, it just accepts parameters which
-are then passed on to the appropriate javascript plugin. So far in this
-example plugin we haven't provided any way for regular players to - you
-know - actually set their text color of choice - only operators can do
-this for a player using the `js chat.setColor(...)` javascript
-expression. Let's be clear - giving your players access to the whole API
-via javascript isn't a good idea. So how do you safely let players
-choose their text color? If you've written a javascript function and
+does not do any JavaScript evaluation, it just accepts parameters which
+are then passed on to the appropriate JavaScript plugin. So far in this
+example plugin we haven't provided any way for regular players to – you
+know – actually set their text color of choice – only operators can do
+this for a player using the `js chat.setColor(...)` JavaScript
+expression. Let's be clear – giving your players access to the whole API
+via JavaScript isn't a good idea. So how do you safely let players
+choose their text color? If you've written a JavaScript function and
want players to be able to use that function, you expose it using the
-new `command()` function like so...
-
- command( 'chat_color', function( params, sender ) {
- var color = params[0];
- if (colorCodes[color]){
- chat.setColor(sender,color);
- }else{
- sender.sendMessage(color + ' is not a valid color');
- sender.sendMessage('valid colors: ' + colors.join(', '));
- }
- },colors);
-
-... The above code adds a new *subcommand* to the `/jsp` command and
-also specifies autocomplete options (the last parameter - `colors`) for
+new `command()` function like so …
+
+```javascript
+function chat_color( params, sender ){
+ var color = params[0];
+ if (colorCodes[color]){
+ chat.setColor(sender,color);
+ }else{
+ echo(sender, color + ' is not a valid color');
+ echo(sender, 'valid colors: ' + colors.join(', '));
+ }
+}
+command(chat_color, colors);
+```
+
+… The above code adds a new *subcommand* to the `/jsp` command and
+also specifies autocomplete options (the last parameter – `colors`) for
that command when the player presses the `TAB` key. Now the player
-themselves can change their chosen chat color like so...
+themselves can change their chosen chat color like so …
/jsp chat_color yellow
-... What I've done here is create a new plugin which lets players choose
+… What I've done here is create a new plugin which lets players choose
a chat color and saves that preference when the server shuts down and
-starts up. I've also added a new `jsp` sub-command - `chat_color` that
+starts up. I've also added a new `jsp` sub-command – `chat_color` that
players use to change their chat color setting. The full plugin source
-code is just a couple of lines of code but is a fully working plugin...
-
- // declare a new javascript plugin
- var _store = { players: {} } ; // private variable
- exports.chat = plugin('chat', {
- setColor: function(player, color){
- _store.players[player.name] = color;
- },
- store: _store
- },true);
-
- var colors = ['black', 'blue', 'darkgreen', 'darkaqua', 'darkred',
- 'purple', 'gold', 'gray', 'darkgray', 'indigo',
- 'brightgreen', 'aqua', 'red', 'pink',
- 'yellow', 'white'];
- var colorCodes = {};
- for ( var i =0; i < colors.length; i++ ) {
- colorCodes[ colors[i] ] = i.toString(16);
- }
-
- events.on( 'player.AsyncPlayerChatEvent', function( evt ) {
- var player = evt.player;
- var playerChatColor = _store.players[player.name];
- if ( playerChatColor ) {
- evt.message = '§' + colorCodes[playerChatColor] + e.message;
- }
- });
- command( 'chat_color', function( params, sender ) {
- var color = params[0];
- if ( colorCodes[ color ] ) {
- chat.setColor( sender, color );
- }else{
- sender.sendMessage( color + ' is not a valid color' );
- sender.sendMessage( colors.join(',') );
- }
- }, colors );
-
+code is just a couple of lines of code but is a fully working plugin …
+
+```javascript
+var store = persist('chat-colors', {players: {}});
+exports.chat = {
+ setColor: function(player,chatColor) {
+ store.players[player.name] = chatColor;
+ }
+}
+var colors = ['black', 'blue', 'darkgreen', 'darkaqua', 'darkred',
+ 'purple', 'gold', 'gray', 'darkgray', 'indigo',
+ 'brightgreen', 'aqua', 'red', 'pink',
+ 'yellow', 'white'];
+var colorCodes = {};
+var COLOR_CHAR = '\u00a7';
+for (var i =0;i < colors.length;i++)
+ colorCodes[colors[i]] = i.toString(16);
+
+var addColor = function( evt ) {
+ var player = evt.player;
+ var playerChatColor = store.players[ player.name ];
+ if ( playerChatColor ) {
+ evt.message = COLOR_CHAR + colorCodes[ playerChatColor ] + evt.message;
+ }
+};
+
+if (__plugin.bukkit) {
+ events.asyncPlayerChat(addColor);
+} else if (__plugin.canary) {
+ events.chat(addColor);
+};
+
+function chat_color( params, sender ){
+ var color = params[0];
+ if (colorCodes[color]){
+ chat.setColor(sender,color);
+ }else{
+ echo(sender, color + ' is not a valid color');
+ echo(sender, 'valid colors: ' + colors.join(', '));
+ }
+}
+
+command(chat_color, colors);
+```
![Chat Color plugin][1]
-... this is what I would call a minimum viable plugin and it
-demonstrates some of the new features of ScriptCraft - persistence
-(automatic) , event handling, and exposing new functionality to players
-using the `/jsp` command. I hope this will give potential MineCraft
+… this is what I would call a minimum viable plugin and it
+demonstrates some of the new features of ScriptCraft – persistence
+(automatic), event handling, and exposing new functionality to players
+using the `/jsp` command. I hope this will give potential Minecraft
modders a feel for just how easy it can be to change the game to suit
their needs.
diff --git a/docs/Frequently-Asked-Questions.md b/docs/Frequently-Asked-Questions.md
new file mode 100644
index 000000000..792143a14
--- /dev/null
+++ b/docs/Frequently-Asked-Questions.md
@@ -0,0 +1,46 @@
+## Using Other Plugins from ScriptCraft
+The following question gets asked a lot so I'm going to try to answer it here:
+
+> How to use other bukkit plugins API?
+> Like PermissionEX API.
+> I can check permission group by java code:
+> ru.tehkode.permissions.bukkit.PermissionsEx.getUser(player).inGroup("moderator");
+> But I can't run this code in JavaScript.
+> -- [Bukkit forum question][1]
+
+[1]: http://dev.bukkit.org/bukkit-plugins/scriptcraft/?page=2#c48
+
+The above question refers to using ScriptCraft for CraftBukkit so I'll answer that first:
+
+You can get the permissionsEx (or any other Bukkit plugin) like this...
+```javascript
+var pex = server.pluginManager.getPlugin('PermissionsEx');
+if (pex.getUser(player).inGroup('moderator') ) {
+...
+}
+```
+Generally if you want to use another plugin's API, then get the plugin object by name and then call its methods. In the above example the `pex` variable refers to the aforementioned `PermissionsEx` Plugin. Once you have that reference you can call any of the plugin's methods just as you would in Java. The tricky part is getting the reference and that's where `server.pluginManager.getPlugin()` comes in.
+
+To get a reference to and work with another plugin's API using ScriptCraft for CanaryMod the same principle applies. Say you've installed ScriptCraft and the dConomy plugin:
+
+```javascript
+var Canary = Packages.net.canarymod.Canary;
+var pluginMgr = Canary.pluginManager();
+var dConomy = pluginMgr.getPlugin('dConomy');
+var dConomyServer = dConomy.modServer;
+// from here on in you can access all of the dConomyServer object's calls
+// e.g. dConomyServer.newTransaction()
+```
+
+The only difference between CanaryMod and Bukkit is how you get the plugin reference. In Bukkit it's:
+
+```javascript
+var otherPlugin = server.pluginManager.getPlugin('PLUGIN_NAME_GOES_HERE');
+```
+
+whereas in CanaryMod it's:
+
+```javascript
+var Canary = Packages.net.canarymod.Canary;
+var otherPlugin = Canary.pluginManager().getPlugin('PLUGIN_NAME_GOES_HERE');
+```
diff --git a/docs/YoungPersonsGuideToProgrammingMinecraft.md b/docs/YoungPersonsGuideToProgrammingMinecraft.md
index 24c465eda..a41d9738e 100644
--- a/docs/YoungPersonsGuideToProgrammingMinecraft.md
+++ b/docs/YoungPersonsGuideToProgrammingMinecraft.md
@@ -1,7 +1,14 @@
+
# The Young Person's Guide to Programming in Minecraft
## Table of Contents
* [Introduction](#introduction)
- * [Installation](#installation)
+ * [Installing and Running SpigotMC](#installing-and-running-spigotmc)
+ * [Installing ScriptCraft](#installing-scriptcraft)
* [Configuring your Server (optional)](#configuring-your-server-optional)
* [Learning Javascript](#learning-javascript)
* [First Steps](#first-steps)
@@ -21,6 +28,8 @@
* [Parameters](#parameters)
* [true or false](#true-or-false)
* [More fun with `true` or `false`](#more-fun-with-true-or-false)
+ * [Booleans and JavaBeans](#booleans-and-javabeans)
+ * [SIDENOTE](#sidenote)
* [...and Again, and Again, and Again,...](#and-again-and-again-and-again)
* [Counting to 100](#counting-to-100)
* [Saying "Hi!" to every player](#saying-hi-to-every-player)
@@ -34,7 +43,6 @@
* [Keeping Score - Lookup tables in Javascript](#keeping-score---lookup-tables-in-javascript)
* [Counting block break events for each player](#counting-block-break-events-for-each-player)
* [Next Steps](#next-steps)
-
## Introduction
Minecraft is an open-ended 3D game where you can build and craft
@@ -54,55 +62,39 @@ players connect to a Minecraft Server on the internet or locally
![Cottages created using ScriptCraft in MineCraft][img_cr]
-## Installation
-
-CraftBukkit is a version of the Minecraft server software which allows
-easy addition of 'Mods' and extensions to Minecraft. ScriptCraft is a
-'Mod' for use with CraftBukkit. Adding Mods to Minecraft can be
-difficult but CraftBukkit makes it easy. Follow these steps to
-Install ScriptCraft on your computer...
+# Installation
-1. [Download and install CraftBukkit][dlbuk2] (choose either Recommended, Beta or Development) . Then follow the [Bukkit
- Installation Instructions][bii]. (Tip: You can grab the very latest
- version of bukkit from the [alternative versions list][dlbuk2])
+Before installing ScriptCraft you must first install SpigotMC which is
+a special version of Minecraft Server that makes it easy to customize
+the game.
-2. Start the CraftBukkit server, then once it has started up, stop it
- by typing 'stop'. If you go to the craftbukkit folder (see step 1) you
- should see some new files and subfolders.
+## Installing and Running SpigotMC
-3. [Download the latest version of the ScriptCraft Mod][sc-plugin]. Then copy the ScriptCraft.jar file to the
- `craftbukkit/plugins` folder (This folder won't be created until you run Bukkit for the first time (see previous step).
+Follow these steps to download and install SpigotMC.
-4. Start up the craftbukkit server again (see [instructions for starting the server][bii]).
+1. Download Spigot's [BuildTools.jar][spigotdl]
+2. Save the BuildTools.jar file to a new directory called spigotmc.
+3. Open a terminal (Mac and Linux) or command prompt (windows) window and type `java -jar BuildTools.jar`. This will kick off a long series of commands to "build" SpigotMC.
+4. When the build is done, there will be a new file beginning with `spigot` and ending in `.jar` in the spigotmc directory. Run this file by typing `java -jar spigot-1.10.2.jar` (it might not be that exact name - you can list files in the directory by typing `dir` (Windows) or `ls` (Mac and Linux).
+5. The server will start up then shut down very shortly afterwards. You'll need to edit a file called `eula.txt` - change `eula=false` to `eula=true` and save the file.
+6. Run the `java -jar spigot-1.10.2.jar` command again - this time the server will start up. Shut it down by typing `stop` at the server prompt.
-5. In the CraftBukkit command window type `op {your_username}` and hit
- enter, replacing {your_username} with your own minecraft
- username. This will give you `operator` access meaning you can perform
- more commands than are normally available in Minecraft. You should
- make yourself a server operator (Server operators have full privileges
- for the server) permanently by editing the craftbukkit/ops.txt file
- and adding your username (one username per line).
+## Installing ScriptCraft
-6. In the CraftBukkit command window type `js 1 + 1` and hit enter. You should see `> 2` .
+Follow these steps to download and install ScriptCraft.
-... Congratulations! You just installed your own Minecraft Server with
-the ScriptCraft Mod and are now ready to begin programming in Minecraft.
+1. Download the [scriptcraft.jar][dl] plugin and save it to the `plugins` directory and restart the server by typing `java -jar spigot-1.10.2.jar`.
+2. At the server prompt type `js 1 + 1` and hit enter. The result `2` should be displayed.
-Normally, Minecraft Mods are written in Java. This makes writing your
-own extension or game rules difficult because you must first learn Java.
-Java is different enough from Javascript. With the ScriptCraft plug-in
-installed, you don't have to learn Java, you can extend and customize
-Minecraft your way using Javascript. Javascript is easier to learn than
-Java but it's also more flexible and powerful and is used for creating
-interactive web sites and many other applications.
+Congratulations - you've just installed your Custom Minecraft Server and are ready to begin writing your first mod!
## Configuring your Server (optional)
-Once you've installed CraftBukkit, depending on your specific needs,
-you might want to consider setting the following properties in the
-`server.properties` file...
+Once you've installed SpigotMC, depending on your specific needs,
+you might want to consider setting the following properties in the `server.properties` file:
# completely flat worlds are best for building from scratch
+ # bukkit/spigotmc
level-type=FLAT
generate-structures=false
@@ -133,14 +125,14 @@ If you don't already know Javascript, don't worry, you'll learn a little
about Programming and Javascript along the way. You've set up a
Minecraft server and are ready to connect ...
-1. Launch Minecraft (keep the Bukkit Command window open).
+1. Launch Minecraft.
2. Click 'Multi-Player'
3. Click 'Add Server'
4. Type any name you like in the name field then type `localhost` in the
address field. `localhost` is a special internet address that points to
your own computer.
-5. Click 'Join Server' to join the craftbukkit server. If the version
-of Minecraft is incompatible with the version of CraftBukkit you will
+5. Click 'Join Server' to join the server. If the version
+of Minecraft is incompatible with the version of the server you will
not be able to connect to the server. To fix this, you can create a
Minecraft profile in your client. Profiles let you decide which
version of Minecraft client you want to run so that your client and
@@ -171,9 +163,9 @@ called `location`. We can use that name like this...
Blackrock Castle
-...You might be wondering where the `''` (called double-quotes) went.
+...You might be wondering why there's no enclosing `'` single quotes.
When telling the computer to store some text, you have to put `'`
-(that's the double-quote character - press Shift+2) at the start and end
+(that's the single-quote character) at the start and end
of the text. The computer doesn't store these quote characters, only the
text between them. The computer will store the variables while the
Minecraft Server is running. Repeat the last command you entered by
@@ -188,7 +180,7 @@ value like this...
The `var` keyword is only needed when you first create the variable. Now
execute this command...
- /js echo( location )
+ /js echo( self, location )
...and it displays...
@@ -199,11 +191,11 @@ the variables you'll create in your in-game commands and scripts, there
are handy *free* variables created for you by ScriptCraft. One such variable is
`self`, it contains information about the current player (that's you)...
- /js echo ( self )
+ /js echo ( self, self.name )
-... displays the following...
+... displays something like the following...
- CraftPlayer{name=walterh}
+ walterh
... for me but the message displayed will be different for every player.
@@ -217,7 +209,7 @@ looking at the building functions let's look at the `echo()` function.
`echo()` - as its name implies - will echo back at you whatever you
tell it. For example, type ...
- /js echo('Hello')
+ /js echo( self, 'Hello')
... and the game will display...
@@ -225,7 +217,7 @@ tell it. For example, type ...
... type ...
- /js echo( 5 + 7 )
+ /js echo( self, 5 + 7 )
... and the game will display...
@@ -235,7 +227,7 @@ tell it. For example, type ...
don't recommend it. Homework and Minecraft don't mix! The `echo()`
function will display anything you tell it to - Text, Numbers and other types...
- /js echo( new Date() )
+ /js echo( self, new Date() )
... prints today's date. If the statement above looks confusing - don't
worry - `new Date()` creates a new date object - I'll talk about objects
@@ -502,12 +494,12 @@ Once you've installed Notepad++, Launch it, create a new file and type the follo
```javascript
exports.greet = function( player ) {
- player.sendMessage('Hi ' + player.name);
+ echo( player, 'Hi ' + player.name);
}
```
... then save the file in a new directory
-`craftbukkit/plugins/scriptcraft/plugins/{your_name}` (replace
+`scriptcraft/plugins/{your_name}` (replace
{your_name} with your own name) and call the file `greet.js` (be sure
to change the file-type option to '*.* All Files' when saving or
NotePad++ will add a '.txt' extension to the filename. Now switch back
@@ -528,7 +520,7 @@ loaded. Try it out by typing this command...
minecraft username. Congratulations - You've just written your very
first Minecraft Mod! With ScriptCraft installed, writing Minecraft
Mods is as simple as writing a new javascript function and saving it
-in a file in the craftbukkit/plugins/scriptcraft/plugins
+in a file in the scriptcraft/plugins
directory. This function will now be avaible every time you launch
minecraft. This is a deliberately trivial minecraft mod but the
principles are the same when creating more complex mods.
@@ -543,17 +535,17 @@ one or more functions, objects or variables. For example...
#### thrower.js
```javascript
-exports.egg = function(player){
- player.throwEgg();
+exports.boo = function(player){
+ echo( player, 'Boo!');
}
-exports.snowball = function(player){
- player.throwSnowball();
+exports.yo = function(player){
+ echo( player, 'Yo!');
}
```
-... is a plugin which provides 2 javascript functions called `egg()`
-and `snowball()` which can be invoked from the in-game prompt like
-this `/js egg(self)` or `/js snowball(self)`.
+... is a plugin which provides 2 javascript functions called `boo()`
+and `yo()` which can be invoked from the in-game prompt like
+this `/js boo(self)` or `/js yo(self)`.
## Parameters
If you want to change the `greet()` function so that it displays a
@@ -568,7 +560,7 @@ Change the `greet()` function so that it looks like this...
```javascript
exports.greet = function ( greeting , player) {
- player.sendMessage( greeting + player.name );
+ echo( player, greeting + player.name );
}
```
@@ -635,53 +627,78 @@ things...
compare the ages of your friends or siblings to your own age.
## More fun with `true` or `false`
-You can find out if you can Fly in minecraft by typing the following statement...
- /js self.allowFlight
+You can find out if you can Fly in minecraft by typing the following statement ...
+
+ /js self.allowFlight
... the result will be `true` or `false` depending on whether you can
fly or not. You can turn on and off your ability to fly by setting
-your `allowFlight` property to `true` or `false`. Try it...
+your `allowFlight` property to `true` or `false`. Try it ...
- /js self.allowFlight = true
+ /js self.allowFlight = true;
-... Now you can fly! To turn off flight...
+... Now you can fly! Double-press the space bar key to start flying. To turn off flight ...
- /js self.allowFlight = false
+ /js self.allowFlight = false;
... and you come crashing down to earth. This is just one example of
-how `true` and `false` are used throughout ScriptCraft - these are
-called `boolean` values - named after [George Boole][boole], a 19th Century
+how `true` and `false` are used throughout ScriptCraft – these are
+called `boolean` values – named after [George Boole][boole], a 19th Century
Maths Professor at University College Cork. There are plenty more
-examples of boolean values in Minecraft. You can find out if monsters
-are allowed in your minecraft world by typing the following
-statement...
+examples of boolean values in Minecraft. You can find out if it's
+raining in your minecraft world by typing the following statement ...
+
+ /js self.world.hasStorm()
+
+... The result of this statement will be either `false` (if it's not raining) or
+`true` (if it *is* raining). If it's raining, you can make it stop raining typing the following command:
+
+ /js self.world.setStorm(false)
+
+... Similarly, to make it start raining you can issue the following command:
+
+ /js self.world.setStorm( true )
+
+### Booleans and JavaBeans
+
+There are many *boolean* properties you can use to turn on or off
+certain game behaviours. For example, the *thundering* behavior is turned
+on or off using the World's `thundering` property. The World object's
+properties and methods are [documented on the SpigotMC JavaDocs World
+page][spworld]. When browsing the SpigotMC JavaDoc pages, whenever
+you see a method whose name begins with `is` such as `isThundering()` and
+a companion method `setThundering()`, these methods are called *JavaBean*
+methods - the *thundering* property is a *JavaBean* property and there
+are two ways you can use JavaBean properties in Javascript. You can
+*get* and *set* the property using the methods provided by Java. To
+*get* the thundering property you can call the JavaBean Method:
+
+ /js self.world.isThundering()
+
+... or you can get the property like this:
- /js self.location.world.allowMonsters
+ /js self.world.thundering
-... The result of this statement will be either `false` (Phew!) or
-`true` (Yikes!) depending on how your server has been
-configured. However, typing the following statement doesn't work as
-expected...
+To *set* the thundering property, you can call the JavaBean method:
- /js self.location.world.allowMonsters = true
+ /js self.world.setThundering( true )
-... This statement won't work as expected - it will give an Error
-message. This is because sometimes we can read variables but we can't
-change them the same way we read them (this is because of how
-Javascript, Java and the CraftBukkit API work together). To turn on or
-off the spawning of monsters, type the following...
+... or you can set the property like this:
- /js self.location.world.setSpawnFlags(false, true)
+ /js self.world.thundering = true
-... the `setSpawnFlags()` method takes 2 parameters, the first
-parameter says whether or not monsters can spawn, and the 2nd says
-whether or not Animals can spawn. (SIDENOTE: You may be wondering how
-to change other aspects of the Minecraft game - pretty much all
+Whatever approach you use, the result will be the same.
+
+[cmworld]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/World.html
+[spworld]: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/World.html
+
+### SIDENOTE
+You may be wondering how to change other aspects of the Minecraft game - pretty much all
aspects of the game can be changed. Changes are made using what are
called `API` calls - these are calls to functions and methods in
-Minecraft - you can read more about these on the [CraftBukkit API
-Reference][cbapi].)
+Minecraft - you can read more about these on the [SpigotMC API
+Reference][spigotapi].
## ...and Again, and Again, and Again,...
@@ -722,26 +739,24 @@ connnected to a server, the worlds of a server and so on.
At the in-game command prompt type the following then hit Enter...
- /js for (var i = 0;i < server.onlinePlayers.length; i++){ server.onlinePlayers[i].sendMessage('Hi!'); }
+ /js var utils = require('utils');
+ /js var players = utils.players();
+ /js for (var i = 0;i < players.length; i++){ echo(players[i], 'Hi!'); }
... Lets look at these statements in more detail. We had to enter the
statements on a single line at the in-game command prompt but the
statements could be written like this...
```javascript
-var players = server.onlinePlayers;
-var player;
-var i;
-for ( i = 0; i < players.length; i++ ) {
- player = players[i];
- player.sendMessage( 'Hi!' );
+var utils = require('utils');
+var players = utils.players();
+for (var i = 0;i < players.length; i++) {
+ echo(players[i], 'Hi!');
}
```
-... On the first line, a new variable `players` is created from the
-server object's onlinePlayers property. `players` is more concise and
-easier to type than the long-winded `server.onlinePlayers`. On the
-fourth line, the for loop is declared, a counter variable `i` is set
+... On the 2nd line, a new variable `players` is created and assigned a value by calling utils.players().
+On the next line, the for loop is declared, a counter variable `i` is set
to 0 (zero - arrays in javascript start at 0 not 1) and each time
around the loop is tested to see if it's less than the number of
players online. At the end of each run around the loop the `i`
@@ -766,18 +781,19 @@ TextWrangler or your editor of choice) and add the following code at
the bottom of the file...
```javascript
+var utils = require('utils');
exports.hiAll = function () {
- var players = server.onlinePlayers,
- player,
- i;
- for ( i = 0; i < players.length; i++) {
- player = players[i];
- player.sendMessage( 'Hi!' );
- }
+ var players = utils.players();
+ player,
+ i;
+ for ( i = 0; i < players.length; i++) {
+ player = players[i];
+ echo( player, 'Hi!' );
+ }
}
```
-... save the file, at the in-game command prompt type `reload` and
+... save the file, at the in-game command prompt type `/js refresh()` and
then type `/js hiAll()`. This will send the message `Hi!` to all of
the players connected to your server. You've done this using a `for`
loop and arrays. Arrays and `for` loops are used heavily in all types
@@ -818,7 +834,8 @@ through arrays. The following loop prints out all of the players on
the server...
```javascript
-var players = server.onlinePlayers;
+var utils = require('utils');
+var players = utils.players();
var i = 0;
while ( i < players.length ) {
console.log( players[i] );
@@ -847,7 +864,9 @@ loops. utils.foreach() takes two parameters...
Let's see it in action, the following code will `console.log()` (print) the
name of each online player in the server console window...
- utils.foreach( server.onlinePlayers, console.log );
+ var utils = require('utils');
+ var players = utils.players;
+ utils.foreach( players, console.log );
... in the above example, the list of online players is processed one
at a time and each item (player) is passed to the `console.log`
@@ -863,8 +882,10 @@ utils.foreach() function...
give every player the ability to fly.
*/
var utils = require('utils');
-utils.foreach( server.onlinePlayers, function( player ) {
- player.setAllowFlight(true);
+var players = utils.players();
+utils.foreach( players, function( player ) {
+ player.capabilities.flying = true;
+ player.updateCapabilities();
} );
```
@@ -875,19 +896,24 @@ utils.foreach( server.onlinePlayers, function( player ) {
Play a Cat's Meow sound for each player.
*/
var utils = require('utils');
-utils.foreach( server.onlinePlayers, function( player ) {
- player.playSound(player.location,
- org.bukkit.Sound.CAT_MEOW,
- 1,
- 1);
+var players = utils.players();
+var sounds = require('sounds');
+utils.foreach( players, function( player ) {
+ sounds.entityCatAmbient( player ); // spigot 1.9
+ /* canarymod only
+ sounds.catMeow( player );
+ */
} );
```
### Exercise
Try changing the above function so that different sounds are played
-instead of a Cat's Meow. You'll need to lookup the [CraftBukkit API's
-Sound class][soundapi] to see all of the possible sounds that can be
-played.
+instead of a Cat's Meow. To see all of the possible sounds that can be
+played, load the sounds module at the in-game prompt using the following statement:
+
+ /js var sounds = require('sounds');
+
+... then type `/js sounds.` and press the TAB key to see a list of all possible sounds.
Loops are a key part of programming in any language. Javascript
provides `for` and `while` statements for looping and many javascript
@@ -914,28 +940,31 @@ pointing at the block, type the following into the in-game prompt...
so the next step is to repeat this over and over. This is where `for`
loops come in. Open your favorite text editor and create a new file in
your scriptcraft/plugins/{your-name} directory, name the file `myskyscraper.js`, then
-type the following...
+type the following code and save:
```javascript
-var myskyscraper = function(floors) {
- var i ;
- if ( typeof floors == 'undefined' ) {
- floors = 10;
- }
- this.chkpt('myskyscraper'); // saves the drone position so it can return there later
- for ( i = 0; i < floors; i++ ) {
- this.box(blocks.iron,20,1,20)
- .up()
- .box0(blocks.glass_pane,20,3,20)
- .up(3);
- }
- return this.move('myskyscraper'); // return to where we started
+function myskyscraper( floors ) {
+ var i ;
+ if ( typeof floors == 'undefined' ) {
+ floors = 10;
+ }
+ // bookmark the drone's position so it can return there later
+ this.chkpt('myskyscraper');
+ for ( i = 0; i < floors; i++ ) {
+ this
+ .box(blocks.iron,20,1,20)
+ .up()
+ .box0(blocks.glass_pane,20,3,20)
+ .up(3);
+ }
+ // return the drone to where it started
+ this.move('myskyscraper');
};
-var Drone = require('../drone/drone').Drone;
-Drone.extend('myskyscraper',myskyscraper);
+var Drone = require('drone');
+Drone.extend( myskyscraper );
```
-... so this takes a little explaining. First I create a new function
+So this takes a little explaining. First I create a new function
called myskyscraper that will take a single parameter `floors` so that
when you eventually call the `myskyscraper()` function you can tell it
how many floors you want built. The first statement in the function
@@ -949,12 +978,12 @@ floor. When the loop is done I return the drone to where it started.
The last 2 lines load the drone module (it must be loaded before I can
add new features to it) and the last line extends the 'Drone' object
so that now it can build skyscrapers among other things. Once you've
-typed in the above code and saved the file, type `reload` in your
-in-game prompt, then type ...
+typed in the above code and saved the file, type `/js refresh()` in your
+in-game prompt, then type:
/js myskyscraper(2);
-... A two-story skyscraper should appear. If you're feeling
+A two-story skyscraper should appear. If you're feeling
adventurous, try a 10 story skyscraper! Or a 20 story skyscraper!
Minecraft has a height limit (256 blocks from bedrock) beyond which
you can't build. If you try to build higher than this then building
@@ -974,38 +1003,37 @@ All the programs we have seen so far have been fairly predictable - they went
straight through the statements, and then went back to the beginning again. This is
not very useful. In practice the computer would be expected to make decisions and
act accordingly. The javascript statement used for making decisions is `if`.
-While standing on the ground in-game, type the following at the command prompt...
+While standing on the ground in-game, type the following at the command prompt:
+
+ /js if ( self.onGround ) { echo('You are not flying!'); }
+
+the following message should have appeared on your screen:
- /js if ( self.flying ) { echo('Hey, You are flying!'); }
+ You are not flying!
-... No message should appear on screen. That is - `Hey, You are
-flying!` should *not* appear on screen. Now double-tap the `space`
-bar to start flying in-game (tap the space bar twice in rapid
+Now double-tap the `space` bar to start flying in-game (tap the space bar twice in rapid
succession), then press and hold space to rise above the ground. Now
enter the same statement again (If you don't want to type the same
statement again, just press `/` then press the `UP` cursor key on your
keyboard, the statement you entered previously should reappear.
- /js if ( self.flying ) { echo('Hey, You are flying!'); }
+ /js if ( self.onGround ) { echo('You are not flying!'); }
-... this time the following message should have appeared on your screen...
-
- Hey, You are flying!
+This time no message should appear on your screen.
The `if` statement tests to see if something is `true` or `false` and
if `true` then the block of code between the curly braces ( `{` and
`}` ) is executed - but only if the condition is true. The condition
-in the above example is `self.flying` which will be `true` if you are
-currently flying or `false` if you aren't.
+in the above example is `!self.onGround` (self is _not_ on ground) which
+will be `true` if you are currently flying or `false` if you aren't.
What if you wanted to display a message only if a condition is *not*
-true ? For example to only display a message if the player is *not*
-flying...
+true ? For example to only display a message if the player is *not* on the ground:
- /js if ( ! self.flying ) { echo ('You are not flying.'); }
+ /js if ( !self.onGround ) { echo ('You are flying!'); }
-... This code differs in that now there's a `!` (the exclamation mark)
-before `self.flying`. The `!` symbol negates (returns the opposite of)
+This code differs in that now there's a `!` (the exclamation mark)
+before `self.onGround`. The `!` symbol negates (returns the opposite of)
whatever follows it.
What if you want to display a message in both cases - whether you're
@@ -1014,16 +1042,16 @@ Open your favorite editor and type the following code into a new file
in your scriptcraft/plugins directory...
```javascript
-function flightStatus( player ) {
- if ( player.flying ) {
- player.sendMessage( 'Hey, You are flying!' );
- } else {
- player.sendMessage( 'You are not flying.' );
- }
+exports.flightStatus = function( player ) {
+ if ( player.onGround ) {
+ echo(player, 'You are not flying!' );
+ } else {
+ echo(player, 'Hey, You are flying!' );
+ }
}
```
-... now type `/reload` at the in-game prompt then type `/js
+... now type `/js refresh()` at the in-game prompt then type `/js
flightStatus(self)` and an appropriate message will appear based on
whether or not you're currently flying. Type the `/js flightStatus()`
command while on the ground and while flying. The message displayed in
@@ -1052,49 +1080,27 @@ following code sends a message to any player who breaks a block in the
game...
```javascript
-events.on('block.BlockBreakEvent', function ( event ) {
- var breaker = event.player;
- breaker.sendMessage('You broke a block');
-} );
+function myBlockBreakHook( event ){
+ var breaker = event.player;
+ echo( breaker, 'You broke a block');
+}
+events.blockBreak( myBlockBreakHook );
```
-The `events.on()` function is how you *register* a function which you
-want to be called whenever a particular type of event occurs. In the
-above code the first parameter `'block.BlockBreakEvent'` is the type
-of event I want to listen for and the second parameter is the function
+The `events.blockBreak()` function is just one of the many `events` functions which can be used to *register* a function to be called whenever a particular type of event occurs. In the
+above code the blockBreak function takes as a parameter a function
I want to be called when that event occurs. The function I want called
in turn takes 1 parameter. The `event` object has all the information
about the event which just occurred. I can tell who broke the block
and send a message to the player. The important thing to note is that
-the function defined above will not be called until a player breaks a
+the `myBlockBreakHook` function defined above will not be called until a player breaks a
block. Try it - save the above code in a new file in the
`scriptcraft/plugins` directory then type `/js refresh()` to reload
scriptcraft. Then break a block in the game and you should see the
message 'You broke a block'.
There are many types of events you can listen for in Minecraft. You can
-browse [all possible Bukkit events][bkevts] (click the 'Next
-Package' and 'Previous Package' links to browse).
-
-It's important to note that when browsing the Bukkit API's
-[org.bukkit.event][bkevts] package, if you see a class called
-'org.bukkit.events.entity.EntityShootBowEvent', then when calling
-`events.on()` you can listen to such an event using either the fully
-qualified Class name...
-
- events.on(org.bukkit.events.entity.EntityShootBowEvent, function( event ) {
- ...
- });
-
-or an abbreviated name in string form...
-
- events.on('entity.EntityShootBowEvent', function( event ) {
- ...
- });
-
-If the `events.on()` function gets a String (text) as its first
-parameter it automatically converts it to the appropriate Class by
-prepending the 'org.bukkit.events' package.
+browse [all possible event registration functions][spevts2] in the API Reference.
For custom events (events which aren't in the org.bukkit.event tree)
just specify the fully qualified class name instead. E.g. ...
@@ -1108,11 +1114,12 @@ just specify the fully qualified class name instead. E.g. ...
If you want an event handler to only execute once, you can remove the handler like this...
```javascript
-events.on('block.BlockBreakEvent', function( evt ) {
- var breaker = evt.player;
- breaker.sendMessage('You broke a block');
- this.unregister();
-} );
+function myBlockBreakHook( evt ) {
+ var breaker = evt.player;
+ echo( breaker, 'You broke a block');
+ this.unregister();
+}
+events.blockBreak( myBlockBreakHook );
```
The `this.unregister();` statement will remove this function from the
@@ -1124,12 +1131,14 @@ to stop listening for events.
To unregister a listener *outside* of the listener function...
```javascript
-var myBlockBreakListener = events.on( 'block.BlockBreakEvent', function( evt ) { ... } );
+function myBlockBreakHook( evt ){
+ var breaker = evt.player;
+ echo( breaker, 'You broke a block');
+}
+var myBlockBreakListener = events.blockBreak( myBlockBreakHook );
...
myBlockBreakListener.unregister();
```
-
-
## Keeping Score - Lookup tables in Javascript
In the *Event-Driven Programming* section, I defined a function which
@@ -1181,10 +1190,10 @@ like this...
```javascript
var scoreboard = {
- walter: 5,
- tom: 6,
- jane: 8,
- bart: 7
+ walter: 5,
+ tom: 6,
+ jane: 8,
+ bart: 7
};
```
@@ -1193,7 +1202,7 @@ parameter and returned their score, I'd do it like this...
```javascript
function getScore(player){
- return scoreboard[ player ];
+ return scoreboard[ player ];
}
```
@@ -1211,21 +1220,22 @@ var utils = require('utils');
var scores = {};
exports.initialise = function(names){
- scores = {};
- utils.foreach(names, function(name){
- scores[name] = 0;
- });
+ scores = {};
+ utils.foreach(names, function(name){
+ scores[name] = 0;
+ });
};
-/* changes score by diff e.g. to add 6 to the player's current score
- updateScore('walter',6); // walter's new score = 5 + 6 = 11.
+/*
+ changes score by diff e.g. to add 6 to the player's current score
+ updateScore('walter',6); // walter's new score = 5 + 6 = 11.
*/
exports.updateScore = function(name, diff){
- scores[name] += diff;
+ scores[name] += diff;
};
exports.getScore = function(name){
- return scores[name];
+ return scores[name];
};
```
@@ -1238,18 +1248,24 @@ keep a count of how many blocks each player has broken ...
```javascript
var breaks = {};
-// every time a player joins the game reset their block-break-count to 0
-events.on('player.PlayerJoinEvent', function( event ) {
- breaks[event.player] = 0;
-});
-events.on('block.BlockBreakEvent', function( event ) {
- var breaker = event.player;
- var breakCount = breaks[breaker.name];
- breakCount++; // increment the count.
- breaks[breaker.name] = breakCount;
-
- breaker.sendMessage('You broke ' + breakCount + ' blocks');
-});
+
+/*
+ every time a player joins the game reset their block-break-count to 0
+*/
+function initializeBreakCount( event ){
+ breaks[event.player.name] = 0;
+}
+events.playerJoin( initializeBreakCount );
+
+/*
+ every time a player breaks a block increase their block-break-count
+*/
+function incrementBreakCount( event ){
+ breaks[event.player.name] += 1; // add 1
+ var breakCount = breaks[event.player.name];
+ echo( event.player, 'You broke ' + breakCount + ' blocks');
+}
+events.blockBreak( incrementBreakCount );
```
With a little more work, you could turn this into a game where players
@@ -1266,27 +1282,32 @@ covered here. If you want to dive deeper into programming and modding
minecraft, I recommend reading the accompanying [ScriptCraft API
reference][api] which covers all of the ScriptCraft functions, objects
and methods. I also recommend reading the source code to some of the
-existing scriptcraft add-ons, the *chat* module (
-`scriptcraft/plugins/chat/color.js` ) is a good place to start, followed by
-[Anatomy of a ScriptCraft Plug-in][ap]. The online [Craftbukkit API
-Reference][cbapi] provides lots of valuable information about the
+existing scriptcraft plugins, followed by
+[Anatomy of a ScriptCraft Plug-in][ap]. The online [SpigotMC API
+Reference][spigotapi] provides lots of valuable information about the
different objects and methods available for use by ScriptCraft.
-[buk]: http://wiki.bukkit.org/Setting_up_a_server
+[cmadmin]: https://github.com/walterhiggins/canarymod-admin-guide/
[dlbuk2]: http://dl.bukkit.org/downloads/craftbukkit/
+[dlcm]: http://canarymod.net/releases
[bii]: http://wiki.bukkit.org/Setting_up_a_server
[sc-plugin]: http://scriptcraftjs.org/download/
[ce]: http://www.codecademy.com/
[mcdv]: http://www.minecraftwiki.net/wiki/Data_values
[np]: http://notepad-plus-plus.org/
[cbapi]: http://jd.bukkit.org/beta/apidocs/
+[cmapi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/
+[spigotapi]: https://hub.spigotmc.org/javadocs/spigot/
[boole]: http://en.wikipedia.org/wiki/George_Boole
-[soundapi]: http://jd.bukkit.org/beta/apidocs/org/bukkit/Sound.html
+[soundapi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/effects/SoundEffect.Type.html
[ap]: Anatomy-of-a-Plugin.md
[api]: API-Reference.md
[twl]: http://www.barebones.com/products/textwrangler/
[bkevts]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/package-summary.html
+[cmevts]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/package-summary.html
+[cmevts2]: API-Reference.md#events-helper-module-canary-version
+[spevts2]: API-Reference.md#events-helper-module-spigotmc-version
[img_echo_date]: img/ypgpm_echo_date.png
[img_3d_shapes]: img/ypgpm_3dshapes.jpg
[img_whd]: img/ypgpm_whd.jpg
diff --git a/docs/img/castleex1.png b/docs/img/castleex1.png
new file mode 100644
index 000000000..d1efaf747
Binary files /dev/null and b/docs/img/castleex1.png differ
diff --git a/docs/img/chessboardex1.png b/docs/img/chessboardex1.png
new file mode 100644
index 000000000..24bc8c965
Binary files /dev/null and b/docs/img/chessboardex1.png differ
diff --git a/docs/img/cottageex1.png b/docs/img/cottageex1.png
new file mode 100644
index 000000000..bc6a0779b
Binary files /dev/null and b/docs/img/cottageex1.png differ
diff --git a/docs/img/cottageroadex1.png b/docs/img/cottageroadex1.png
new file mode 100644
index 000000000..8e7f2add2
Binary files /dev/null and b/docs/img/cottageroadex1.png differ
diff --git a/docs/img/dancefloorex1.png b/docs/img/dancefloorex1.png
new file mode 100644
index 000000000..dd8c449eb
Binary files /dev/null and b/docs/img/dancefloorex1.png differ
diff --git a/docs/img/fortex1.png b/docs/img/fortex1.png
new file mode 100644
index 000000000..e204ebcf8
Binary files /dev/null and b/docs/img/fortex1.png differ
diff --git a/docs/img/lcdclockex1.png b/docs/img/lcdclockex1.png
new file mode 100644
index 000000000..df17c4819
Binary files /dev/null and b/docs/img/lcdclockex1.png differ
diff --git a/docs/img/mazeex1.png b/docs/img/mazeex1.png
new file mode 100644
index 000000000..1db678e82
Binary files /dev/null and b/docs/img/mazeex1.png differ
diff --git a/docs/img/templeex1.png b/docs/img/templeex1.png
new file mode 100644
index 000000000..059ca4c41
Binary files /dev/null and b/docs/img/templeex1.png differ
diff --git a/docs/readme.md b/docs/readme.md
deleted file mode 100644
index b79fc5666..000000000
--- a/docs/readme.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Let's begin...
-
-I created ScriptCraft to make it easier for children (and anyone
-curious about programming) to create their own Minecraft
-Mods. ScriptCraft makes it easier for new programmers to create
-Minecraft mods. Mods are written using the Javascript programming
-language and once the ScriptCraft mod is installed, you can add your
-own new Mods by adding Javascript (.js) files in a directory.
-
- * If you're new to programming and want to start modding Minecraft, then [Start Here][ypgpm].
- * If you've already used [Scratch][scr], have attended a few [CoderDojo][cd] sessions, or have already dabbled with Javascript, then [Start Here][cda].
-
-# Additional Resources
-
-CoderDojo Athenry have some [excellent tutorials][cda] for younger
-programmers who have used [Scratch][scr] and are interested in Modding
-Minecraft using Javascript. In particular, they have an excellent
-[Scratch - to - Javascript][sj] tutorial which explains Scratch
-programs and how to do the same thing in Javascript.
-
-I highly recommend the series of [tutorials provided by CoderDojo Athenry][cda].
-
-Developer Chris Cacciatore has created some interesting tools using Scriptcraft...
-
- * [A wolf-bot][wb]
- * [L-Systems (Large-scale fractal structures in Minecraft)][ls]
-
-[wb]: https://github.com/cacciatc/wolfbot
-[ls]: https://github.com/cacciatc/scriptcraft-lsystems
-[ypgpm]: YoungPersonsGuideToProgrammingMinecraft.md
-[cd]: http://coderdojo.com/
-[scr]: http://scratch.mit.edu/
-[cda]: http://cdathenry.wordpress.com/category/modderdojo/
-[sj]: http://cdathenry.wordpress.com/2013/10/12/modderdojo-week-2-moving-from-scratch-to-javascript/
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 000000000..a2e0b10aa
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1 @@
+/canarymod.jar
diff --git a/lib/canarymod-1.8.0.jar b/lib/canarymod-1.8.0.jar
new file mode 100644
index 000000000..c3b025fac
Binary files /dev/null and b/lib/canarymod-1.8.0.jar differ
diff --git a/lib/spigot-1.11.2.jar b/lib/spigot-1.11.2.jar
new file mode 100644
index 000000000..6a055256d
Binary files /dev/null and b/lib/spigot-1.11.2.jar differ
diff --git a/license.txt b/license.txt
index 3f3b29081..e0e06da85 100644
--- a/license.txt
+++ b/license.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2014 Walter Higgins
+Copyright (c) 2016 Walter Higgins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/nbproject/ide-targets.xml b/nbproject/ide-targets.xml
deleted file mode 100644
index efb4aa50b..000000000
--- a/nbproject/ide-targets.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- Starting Bukkit with ScriptCraft
-
-
-
-
-
-
-
diff --git a/docs/release-notes.md b/release-notes.md
similarity index 67%
rename from docs/release-notes.md
rename to release-notes.md
index 2bd662c87..5d19238ed 100644
--- a/docs/release-notes.md
+++ b/release-notes.md
@@ -1,7 +1,206 @@
-# 2014 05 10
+RELEASE NOTES
+=============
-Further simplification of events handling. The events.on() function can still be used but additional functions are now provided for each type of event.
-For example, to register a custom player-join event handler...
+3.2.1 Release (2016 12 23)
+--------------------------
+
+Bug fixes and updated from Spigot 1.9 to Spigot 1.11.2
+
+3.2.0 Release (2016 03 20)
+--------------------------
+
+Bug fixes and updated from Spigot 1.8.8 to Spigot 1.9
+
+Fixed issues #256 and #287
+
+3.1.12 Release (2015 12 30)
+---------------------------
+
+Added new modules
+
+* lightning
+
+The entities module and lightning module are now documented.
+
+To make lightning strikes when and where any arrow lands:
+
+ var lightning = require('lightning');
+ events.projectileHit( function( event ) {
+ if (entities.arrow( event.projectile )
+ lightning( event.projectile );
+ });
+
+3.1.11 Release (2015 11 21)
+---------------------------
+
+Added new modules
+
+* entities
+* spawn
+
+And new Drone function `spawn()`
+
+To use:
+Point at a block then type...
+```
+/js spawn('ZOMBIE').fwd().times(5).right().back(5).times(6)
+```
+
+... unleash a horde of zombies (in 5x6 grid formation).
+
+3.1.10 Release (2015 08 16)
+---------------------------
+Bug fix: modules/bukkit/sounds.js now works (fixed for Bukkit/SpigotMC/Glowstone)
+
+3.1.9 Release (2015 08 01)
+--------------------------
+Bug fix: minigames/scoreboard.js module's updatePlayerScore() function did not work with latest version of CanaryMod.
+Using /scoreboard command instead. See https://github.com/walterhiggins/ScriptCraft/issues/261
+
+3.1.8 Release (2015 06 07)
+--------------------------
+Bug fix: Fixes drone on Spigot 1.8.7 with JDK 7 see
+https://github.com/walterhiggins/ScriptCraft/issues/254
+
+
+3.1.7 Release (2015 06 07)
+--------------------------
+Added workaround for https://bugs.openjdk.java.net/browse/JDK-8072596 to recipes module.
+
+3.1.6 Release (2015 05 31)
+--------------------------
+Provide more helpful error messages when trying to require modules which don't exist e.g.
+require('greetings') should fail but indicate if there's a 'greeting' module present instead.
+
+Fixes problem with Fireworks module on Mac OS.
+
+3.1.5 Release (2015 05 31)
+--------------------------
+CanaryMod version : Add events.connect as synonym for events.connnection for backward-compatibility
+with 1.7.9 and book listings.
+
+3.1.4 Release (2015 05 09)
+--------------------------
+Various bug fixes and new 'inventory' module.
+
+3.1.3 Release (2015 03 02)
+--------------------------
+Various bug fixes.
+
+3.1.2 Release (2015 02 16)
+--------------------------
+Bug fix release. Fixes bug #213 (http.request fixed for CanaryMod)
+New blocks.slime and other block types.
+Various other fixes. See https://github.com/walterhiggins/ScriptCraft/compare/3.1.1...3.1.2
+
+3.1.1 Release (2015 01 24)
+--------------------------
+This is a bug fix release.
+See https://github.com/walterhiggins/ScriptCraft/compare/3.1.0...master for bug fix details.
+
+3.1.0 Release (2015 01 11)
+--------------------------
+Fixes issue #197
+
+Extending Drone has been made easier. Drone is now a module so it can be required like this:
+
+ var Drone = require('drone');
+ Drone.extend(function myExtension(){ } );
+
+There have been a number of documentation updates.
+The Drone.copy() and Drone.paste() methods are deprecated.
+
+New utils methods:
+
+* utils.time(world) returns the time of day (in minecraft ticks) for a world
+* utils.time24(world) returns the time of day (in minutes) for a world
+
+The Arrows and Signs plugins have now been updated to support CanaryMod.
+
+3.0.3 Release (2015 01 03)
+--------------------------
+Additional support for Drone methods in 1.7.10 and 1.8.
+Fixes issues:
+
+* 177: https://github.com/walterhiggins/ScriptCraft/issues/177
+* 185: https://github.com/walterhiggins/ScriptCraft/issues/185
+* 188: https://github.com/walterhiggins/ScriptCraft/issues/188
+
+3.0.2 Release (2014 12 28)
+--------------------------
+This version includes fixes for CraftBukkit (1.7.X) and also fixes
+problems on Mac OS X and includes some support for Drone methods in
+Minecraft 1.8 (CanaryMod 1.2)
+
+3.0.0 Release (2014 11 09)
+----------------------------------
+In September 2014, development of CraftBukkit was discontinued due to
+a DMCA takedown notice. ScriptCraft has since switched from
+CraftBukkit to CanaryMod as the underlying framework. ScriptCraft
+continues to run on CraftBukkit but - for the immediate future -
+future development efforts will be to ensure it works primarily on
+CanaryMod. When other frameworks like SpongePowered become available,
+
+I hope to eventually support those too.
+
+The biggest change in 3.0.0 is the removal of the short name event
+registration function.
+
+This will no longer work:
+
+ events.on('block.BlockBreakEvent', function( event ) { ... });
+
+Instead use this:
+
+ events.blockDestroy( function( event ) { ... });
+
+or
+
+ events.on(net.canarymod.hook.block.BlockDestroyHook, function( event ) { ... });
+
+
+2014 08 23
+----------
+Chessboard was broken, is now fixed.
+Rainbows are now made from stained glass. Full range of stained_glass
+colors is available in blocks variable.
+SnowballFight mini-game has been removed. An improved version will be
+available soon online.
+chat plugin has been removed - will be available in another github repo soon.
+
+2014 06 14
+----------
+Fix issue #140 - fails to build for JRE7
+Changed command() documentation to conform with new way of using (passing a named function)
+
+2014 05 31
+----------
+Fix bug in persistence module. Private load function wasn't returning result of scload.
+
+2014 05 29
+----------
+Fix tab completion for /jsp command so that it conforms with tab completion norms in minecraft.
+/jsp ice completes to /jsp icecream
+Hitting TAB again has no effect. Player must type space then hit TAB to get list of flavors.
+This is consistent with how MC treats other commands for tab completion.
+
+2014 05 19
+----------
+Improved Tab Completion to work with Java Enums too.
+
+2014 05 12
+----------
+Turn off modality for conversations which are started via the 'input' module.
+(with modality on, player.sendMessage() is suppressed but player.sendRawMessage() isn't.
+ turning modality off as devs would expect player.sendMessage() to work - I did anyway)
+
+
+2014 05 10
+----------
+Further simplification of events handling. The events.on() function
+can still be used but additional functions are now provided for each
+type of event. For example, to register a custom player-join event
+handler...
events.playerJoin(function(event){
event.player.sendMessage('welcome!');
@@ -10,14 +209,12 @@ For example, to register a custom player-join event handler...
Added new sounds module for simpler sounds playback and in-game tab completion.
All of the org.bukkit.Sound enum values are attached to the sounds module.
-# 2014 04 13
-
+2014 04 13
+----------
Added asynchronous `input()` function module.
-# 2014 03 15
-
-## Version 2.0.6
-
+Version 2.0.6 (2014 03 15)
+--------------------------
## Simplified Event handling code.
The callback function for event handling now only takes 1 single
@@ -44,8 +241,9 @@ the event.
# 2014 03 12
-Added Drone.MAX_VOLUME and Drone.MAX_SIDE properties to specify limits on size of Drone ops.
-This is to stop individual players from hogging the CPU in a classrom environment.
+Added Drone.MAX_VOLUME and Drone.MAX_SIDE properties to specify limits
+on size of Drone ops. This is to stop individual players from hogging
+the CPU in a classrom environment.
# 2014 03 08
diff --git a/src/docs/java/jscript.java b/src/docs/java/jscript.java
index 777fe7d48..53334a134 100644
--- a/src/docs/java/jscript.java
+++ b/src/docs/java/jscript.java
@@ -1,5 +1,6 @@
import javax.script.*;
import java.io.FileReader;
+import net.canarymod.api.inventory.ItemType;
public class jscript
{
@@ -10,6 +11,7 @@ public static void main(String[] args) throws Exception
java.io.File file = new java.io.File(args[0]);
engine.put("engine",engine);
engine.put("args",args);
+ engine.put("cmItemTypeClass",ItemType.class);
FileReader fr = new java.io.FileReader(file);
engine.eval(fr);
fr.close();
diff --git a/src/docs/js/generateApiDocs.js b/src/docs/js/generateApiDocs.js
index b1570cec3..467dbf712 100644
--- a/src/docs/js/generateApiDocs.js
+++ b/src/docs/js/generateApiDocs.js
@@ -1,135 +1,118 @@
+/*global load, args, Packages*/
/*
- This script is run at build time to generate api.md - a single Markdown document containing documentation for ScriptCraft's API
-*/
-var err = java.lang.System.err;
-
-args = Array.prototype.slice.call(args,1);
-
-if (typeof importPackage == 'undefined'){
- // load compatibility script
- load('nashorn:mozilla_compat.js');
+ This script is run at build time to generate api.md - a single Markdown document containing documentation for ScriptCraft's API
+ */
+function foreach(array, func){
+ for (var i =0; i < array.length; i++){
+ func(array[i],i,array);
+ }
}
-var dir = args[0];
-var foreach = function(array, func){
- for (var i =0; i < array.length; i++){
- func(array[i],i,array);
- }
-};
-
-importPackage(java.io);
/*
- find - a (very) basic implementation of the unix command line tool.
-*/
-var find = function(dir,store,re)
-{
- var files = dir.listFiles();
- foreach (files, function(filename){
- filename = "" + filename;
- var file = new File(filename);
- if (file.isDirectory()) {
- find(file,store,re);
- } else {
- if (typeof re == "undefined")
- store.push(filename);
- else if (filename.match(re))
- store.push(filename);
- }
- });
-};
+ find - a (very) basic implementation of the unix command line tool.
+ */
+function find(dir,store,re) {
+ var files = dir.listFiles();
+ foreach (files, function(filename){
+ filename = "" + filename;
+ var file = new File(filename);
+ if (file.isDirectory()) {
+ find(file,store,re);
+ } else {
+ if (typeof re == "undefined")
+ store.push(filename);
+ else if (filename.match(re))
+ store.push(filename);
+ }
+ });
+}
/*
- the main module file for a given directory
- (assuming the main module is in a file with the same name as the parent
- directory) - e.g. drone/drone.js
-*/
-var sorter = function( precedence ){
- return function(a,b)
- {
- // convert from Java string to JS string
- a = '' + a;
- b = '' + b;
- var aparts = a.split(/\//);
- var bparts = b.split(/\//);
- var adir = aparts.slice(3,aparts.length-1).join('/');
- var afile = aparts[aparts.length-1];
- var bdir = bparts.slice(3,bparts.length-1).join('/');
- var bfile = bparts[bparts.length-1];
-
- for (var i = 0;i < precedence.length; i++){
- var re = precedence[i];
- if (a.match(re) && b.match(re)){
- if (afile < bfile)
- return -1;
- if (afile > bfile)
- return 1;
- }
- if (a.match(re))
- return -1;
- if (b.match(re))
- return 1;
- }
- if(adirbdir) return 1;
- afile = afile.replace(/\.js$/,'');
- if (afile == adir){
- return -1;
- }
- else {
- var result = 0;
- if (afile < bfile){
- result = -1;
- }
- if (afile > bfile){
- result = 1;
- }
- //err.println("afile: " + afile + ", bfile:" + bfile + ",result=" + result);
-
- return result;
- }
- };
-};
-var sortByModule = function(a,b)
-{
- var aparts = (''+a).split(/\//);
- var bparts = (''+b).split(/\//);
- var adir = aparts[aparts.length-2];
+ the main module file for a given directory
+ (assuming the main module is in a file with the same name as the parent
+ directory) - e.g. drone/drone.js
+ */
+function sorter( precedence ){
+ return function(a,b)
+ {
+ // convert from Java string to JS string
+ a = '' + a;
+ b = '' + b;
+ var aparts = a.split(/\//);
+ var bparts = b.split(/\//);
+ var adir = aparts.slice(3,aparts.length-1).join('/');
var afile = aparts[aparts.length-1];
- var bdir = bparts[bparts.length-2];
+ var bdir = bparts.slice(3,bparts.length-1).join('/');
var bfile = bparts[bparts.length-1];
- if (afile == '_scriptcraft.js')
+
+ for (var i = 0;i < precedence.length; i++){
+ var re = precedence[i];
+ if (a.match(re) && b.match(re)){
+ if (afile < bfile)
+ return -1;
+ if (afile > bfile)
+ return 1;
+ }
+ if (a.match(re))
return -1;
- if (bfile == '_scriptcraft.js')
+ if (b.match(re))
return 1;
+ }
if(adirbdir) return 1;
- if (afile.indexOf(adir) == 0)
- return -1;
- else
- return 1;
-};
+ afile = afile.replace(/\.js$/,'');
+ if (afile == adir){
+ return -1;
+ }
+ else {
+ var result = 0;
+ if (afile < bfile){
+ result = -1;
+ }
+ if (afile > bfile){
+ result = 1;
+ }
+ //err.println("afile: " + afile + ", bfile:" + bfile + ",result=" + result);
+
+ return result;
+ }
+ };
+}
+var err = java.lang.System.err;
+args = Array.prototype.slice.call(args,1);
+
+if (typeof importPackage == 'undefined'){
+ // load compatibility script
+ load('nashorn:mozilla_compat.js');
+}
+var dir = args[0];
+
+var io = Packages.java.io;
+var File = io.File;
var store = [];
-find(new File(dir),store,/\/[a-zA-Z0-9_\-]+\.js$/);
+find(new io.File(dir),store,/\/[a-zA-Z0-9_\-]+\.js$/);
store.sort(sorter([
- /lib\/scriptcraft\.js$/,
- /lib\/require\.js$/,
- /lib\/plugin\.js$/,
- /lib\/events\.js$/,
- /lib\//,
- /modules\//,
- /drone\.js/,
- /drone\//,
- /examples\//
+ /lib\/scriptcraft\.js$/,
+ /lib\/require\.js$/,
+ /lib\/plugin\.js$/,
+ /lib\/events\.js$/,
+ /lib\/events\-helper\-canary/,
+ /lib\/events\-helper\-bukkit/,
+ /lib\//,
+ /modules\/drone\/index\.js/,
+ /modules\/drone\//,
+ /plugins\/drone\//,
+ /modules\//,
+ /examples\//
]));
-//err.println("store=" + JSON.stringify(store));
var contents = [];
foreach(store, function(filename){
- var br = new BufferedReader(new FileReader(filename));
- var line ;
- while ( (line = br.readLine()) != null){
- contents.push(line);
- }
- br.close();
+ var br = new io.BufferedReader(new io.FileReader(filename));
+ var line ;
+ while ( (line = br.readLine()) != null){
+ contents.push(line);
+ }
+ br.close();
});
var len = contents.length;
@@ -138,16 +121,16 @@ var startComment = /^\/\*{10}/;
var endComment = /^\*{3}\//;
for (var i = 0;i < contents.length; i++){
- var line = contents[i];
- if (line.match(startComment)){
- writeComment = true;
- i++;
- }
- if (line.match(endComment)){
- writeComment = false;
- }
- if (writeComment){
- java.lang.System.out.println(contents[i]);
- }
+ var line = contents[i];
+ if (line.match(startComment)){
+ writeComment = true;
+ i++;
+ }
+ if (line.match(endComment)){
+ writeComment = false;
+ }
+ if (writeComment){
+ java.lang.System.out.println(contents[i]);
+ }
}
diff --git a/src/docs/js/generateEntitiesDoc.js b/src/docs/js/generateEntitiesDoc.js
new file mode 100644
index 000000000..f24e368c2
--- /dev/null
+++ b/src/docs/js/generateEntitiesDoc.js
@@ -0,0 +1,46 @@
+args = Array.prototype.slice.call(args,1);
+// [0] = type, [1] = lib.jar [2] = blockX, [3] = classX
+var out = java.lang.System.out,
+ err = java.lang.System.err,
+ entry = null;
+var content = [
+ '',
+ '## Entities module',
+ 'The Entities module provides a suite of functions - one for each possible entity type. ',
+ 'It acts as a helper or enumerated module to assist in use with the `spawn` module and command. ',
+ 'This module is useful for TAB-completion at the in-game prompt. ',
+ '',
+ 'When each function is called with no parameters, it will return the appropriate EntityType object. ',
+ 'For example `entities.polar_bear()` will return an `EntityType.POLAR_BEAR` object. ',
+ '',
+ 'When each function is called with a single parameter - an entity - the entity\'s type will be compared and return true or false. ',
+ '',
+ '### Usage',
+ '',
+ ' entities.zombie(); // returns a SpigotMC/CanaryMod EntityType.ZOMBIE enum value',
+ ' entities.zombie( mob ); // compares the entity\'s type to a zombie, returns true if mob type is zombie, false otherwise',
+ ' entities.player( self ); // at the in-game prompt this should return true (compares self to a player entity type)',
+ ' entities.rabbit( self ); // at the in-game prompt this should return false (compares self to a rabbit entity type)',
+ '',
+ 'The following functions are provided:',
+ ''
+];
+
+var enumVals = [], t, i, name;
+var entitytypes = org.bukkit.entity.EntityType.values();
+for (t in entitytypes) {
+ if (entitytypes[t] && entitytypes[t].ordinal) {
+ name = entitytypes[t].name();
+ name = ('' + name).replace(/^(.*)/,function(a){ return a.toLowerCase(); });
+ enumVals.push(' * ' + name + '()');
+ }
+}
+enumVals.sort();
+content = content.concat(enumVals);
+content.push('');
+for (i = 0; i< content.length; i++){
+ out.println(content[i]);
+}
+
+
+
diff --git a/src/docs/js/generateEventsHelper.js b/src/docs/js/generateEventsHelper.js
new file mode 100644
index 000000000..d32fcb864
--- /dev/null
+++ b/src/docs/js/generateEventsHelper.js
@@ -0,0 +1,103 @@
+args = Array.prototype.slice.call(args,1);
+// [0] = type, [1] = lib.jar [2] = blockX, [3] = classX
+var File = java.io.File,
+ FileReader = java.io.FileReader,
+ FileInputStream = java.io.FileInputStream,
+ FRAMEWORK = args[0],
+ out = java.lang.System.out,
+ err = java.lang.System.err,
+ Modifier = java.lang.reflect.Modifier,
+ clz,
+ ZipInputStream = java.util.zip.ZipInputStream,
+ zis = new ZipInputStream(new FileInputStream(args[1])),
+ entry = null;
+var content = [
+ '/*********************',
+ '## Events Helper Module (' + FRAMEWORK + ' version)',
+ 'The Events helper module provides a suite of functions - one for each possible event.',
+ 'For example, the events.' + args[2] + '() function is just a wrapper function which calls events.on(' + args[3] + ', callback, priority)',
+ 'This module is a convenience wrapper for easily adding new event handling functions in Javascript. ',
+ 'At the in-game or server-console prompt, players/admins can type `events.` and use TAB completion ',
+ 'to choose from any of the approx. 160 different event types to listen to.',
+ '',
+ '### Usage',
+ '',
+ ' events.' + args[2] + '( function( event ) { ',
+ ' echo( event.player, \'You broke a block!\'); ',
+ ' });',
+ '',
+ 'The crucial difference is that the events module now has functions for each of the built-in events. The functions are accessible via TAB-completion so will help beginning programmers to explore the events at the server console window.',
+ '',
+ '***/'
+];
+var canary = false;
+if (FRAMEWORK == 'CanaryMod'){
+ canary = true;
+}
+
+for (var i = 0; i< content.length; i++){
+ out.println(content[i]);
+}
+while ( ( entry = zis.nextEntry) != null) {
+ var name = new String( entry.name );
+ var re1 = /org\/bukkit\/event\/.+Event\.class$/;
+ if (canary){
+ re1 = /net\/canarymod\/hook\/.+Hook\.class$/;
+ }
+ if ( re1.test(name) ) {
+ name = name.replace(/\//g,'.').replace('.class','');
+ try {
+ clz = java.lang.Class.forName(name);
+ }catch ( e) {
+ err.println('Warning: could not Class.forName("' + name + '")');
+ clz = engine.eval(name);
+ }
+ var isAbstract = Modifier.isAbstract(clz.getModifiers());
+ if ( isAbstract ) {
+ continue;
+ }
+ var parts = name.split('.');
+ var shortName = null;
+ if (canary){
+ shortName = name.replace('net.canarymod.hook.','');
+ }
+ if (!canary){
+ shortName = name.replace('org.bukkit.event.','');
+ }
+ var fname = parts.reverse().shift().replace(/^(.)/,function(a){
+ return a.toLowerCase();});
+ if (!canary){
+ fname = fname.replace(/Event$/,'');
+ }
+ if (canary){
+ fname = fname.replace(/Hook$/,'');
+ }
+ var javaDoc = canary ? 'https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/' : 'https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/';
+ var comment = [
+ '/*********************',
+ '### events.' + fname + '()',
+ '',
+ '#### Parameters ',
+ '',
+ ' * callback - A function which is called whenever the ['+ shortName + ' event](' + javaDoc + shortName.replace('.','/') + '.html) is fired',
+ '',
+ ' * priority - optional - see events.on() for more information.',
+ '',
+ '***/'
+//http://jd.bukkit.org/rb/apidocs/org/bukkit/event/player/PlayerJoinEvent.html
+ ];
+ for (var i = 0; i < comment.length; i++){
+ out.println(comment[i]);
+ }
+ out.println('exports.' + fname + ' = function(callback,priority){ ');
+ if (canary){
+ out.println(' return events.on(Packages.' + name + ',callback,priority);');
+ } else {
+ out.println(' return events.on(' + name + ',callback,priority);');
+ }
+ out.println('};');
+ }
+}
+
+
+
diff --git a/src/docs/js/generateItemsDoc.js b/src/docs/js/generateItemsDoc.js
new file mode 100644
index 000000000..fe5715fe6
--- /dev/null
+++ b/src/docs/js/generateItemsDoc.js
@@ -0,0 +1,77 @@
+args = Array.prototype.slice.call(args,1);
+// [0] = type, [1] = lib.jar [2] = blockX, [3] = classX
+var out = java.lang.System.out,
+ err = java.lang.System.err,
+ entry = null;
+var content = [
+ '',
+ '## Items module (SpigotMC version)',
+ 'The Items module provides a suite of functions - one for each possible item.',
+ 'See https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html for a list of possible items',
+ '',
+ '### Usage',
+ '',
+ ' items.book(); // returns org.bukkit.Material.BOOK',
+ ' items.book(2); // returns a new org.bukkit.Material object with an amount 2 (2 books)',
+ ' items.book( itemType ); // compares itemType parameter to org.bukkit.Material.BOOK or an Item of type book',
+ '',
+ 'The following functions are provided:',
+ ''
+];
+
+var enumVals = [], t, i, name;
+var types = org.bukkit.Material.values();
+for (t in types) {
+ if (types[t] && types[t].ordinal) {
+ name = ('' + types[t].name()).toLowerCase();
+ name = name.replace(/(_.)/g,function(a){ return a.replace(/_/,'').toUpperCase(); });
+ enumVals.push(' * ' + name + '()');
+ }
+}
+enumVals.sort();
+content = content.concat(enumVals);
+content.push('');
+for (i = 0; i< content.length; i++){
+ out.println(content[i]);
+}
+
+content = [
+ '',
+ '## Items module (CanaryMod version)',
+ 'The Items module provides a suite of functions - one for each possible item.',
+ 'See https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/inventory/ItemType.html for a list of possible items',
+ '',
+ '### Usage',
+ '',
+ ' items.book(); // returns net.canarymod.api.inventory.ItemType.Book',
+ ' items.book(2); // returns a new net.canarymod.api.inventory.Item object with an amount 2 (2 books)',
+ ' items.book( itemType ); // compares itemType parameter to ItemType.Book or an Item of type book',
+ '',
+ 'The following functions are provided:',
+ ''
+];
+
+//var ItemType = java.lang.Class.forName('net.canarymod.api.inventory.ItemType');
+var materials = cmItemTypeClass.getDeclaredFields();
+
+enumVals = [];
+for ( i = 0;i < materials.length; i++ ){
+
+ if (materials[i].type != cmItemTypeClass) {
+ continue;
+ }
+ var materialField = materials[i];
+ name = (''+materialField.name).replace(/^(.)/,function(a){
+ return a.toLowerCase() ;
+ });
+ enumVals.push(' * ' + name + '()');
+}
+enumVals.sort();
+content = content.concat(enumVals);
+content.push('');
+for (var i = 0; i< content.length; i++){
+ out.println(content[i]);
+}
+
+
+
diff --git a/src/docs/js/generateTOC.js b/src/docs/js/generateTOC.js
index b53605184..22e35df68 100644
--- a/src/docs/js/generateTOC.js
+++ b/src/docs/js/generateTOC.js
@@ -40,11 +40,11 @@ for (var i = 0; i < contents.length; i++){
if (line.match(/^##\s+/)){
var h2 = line.match(/^##\s+(.*)/)[1].trim();
var link = createLink(h2);
- java.lang.System.out.println (' * [' + h2 + '](#' + link + ')');
+ java.lang.System.out.println(' * [' + h2 + '](#' + link + ')');
}
if (line.match(/^###\s+/)){
var h3 = line.match(/^###\s+(.*)/)[1].trim();
var link = createLink(h3);
- java.lang.System.out.println (' * [' + h3 + '](#' + link + ')');
+ java.lang.System.out.println(' * [' + h3 + '](#' + link + ')');
}
}
diff --git a/src/docs/templates/ypgpm.md b/src/docs/templates/ypgpm.md
index 39b30a215..445166c48 100644
--- a/src/docs/templates/ypgpm.md
+++ b/src/docs/templates/ypgpm.md
@@ -1,4 +1,3 @@
-
## Introduction
Minecraft is an open-ended 3D game where you can build and craft
@@ -18,55 +17,39 @@ players connect to a Minecraft Server on the internet or locally
![Cottages created using ScriptCraft in MineCraft][img_cr]
-## Installation
-
-CraftBukkit is a version of the Minecraft server software which allows
-easy addition of 'Mods' and extensions to Minecraft. ScriptCraft is a
-'Mod' for use with CraftBukkit. Adding Mods to Minecraft can be
-difficult but CraftBukkit makes it easy. Follow these steps to
-Install ScriptCraft on your computer...
+# Installation
-1. [Download and install CraftBukkit][dlbuk2] (choose either Recommended, Beta or Development) . Then follow the [Bukkit
- Installation Instructions][bii]. (Tip: You can grab the very latest
- version of bukkit from the [alternative versions list][dlbuk2])
+Before installing ScriptCraft you must first install SpigotMC which is
+a special version of Minecraft Server that makes it easy to customize
+the game.
-2. Start the CraftBukkit server, then once it has started up, stop it
- by typing 'stop'. If you go to the craftbukkit folder (see step 1) you
- should see some new files and subfolders.
+## Installing and Running SpigotMC
-3. [Download the latest version of the ScriptCraft Mod][sc-plugin]. Then copy the ScriptCraft.jar file to the
- `craftbukkit/plugins` folder (This folder won't be created until you run Bukkit for the first time (see previous step).
+Follow these steps to download and install SpigotMC.
-4. Start up the craftbukkit server again (see [instructions for starting the server][bii]).
+1. Download Spigot's [BuildTools.jar][spigotdl]
+2. Save the BuildTools.jar file to a new directory called spigotmc.
+3. Open a terminal (Mac and Linux) or command prompt (windows) window and type `java -jar BuildTools.jar`. This will kick off a long series of commands to "build" SpigotMC.
+4. When the build is done, there will be a new file beginning with `spigot` and ending in `.jar` in the spigotmc directory. Run this file by typing `java -jar spigot-1.10.2.jar` (it might not be that exact name - you can list files in the directory by typing `dir` (Windows) or `ls` (Mac and Linux).
+5. The server will start up then shut down very shortly afterwards. You'll need to edit a file called `eula.txt` - change `eula=false` to `eula=true` and save the file.
+6. Run the `java -jar spigot-1.10.2.jar` command again - this time the server will start up. Shut it down by typing `stop` at the server prompt.
-5. In the CraftBukkit command window type `op {your_username}` and hit
- enter, replacing {your_username} with your own minecraft
- username. This will give you `operator` access meaning you can perform
- more commands than are normally available in Minecraft. You should
- make yourself a server operator (Server operators have full privileges
- for the server) permanently by editing the craftbukkit/ops.txt file
- and adding your username (one username per line).
+## Installing ScriptCraft
-6. In the CraftBukkit command window type `js 1 + 1` and hit enter. You should see `> 2` .
+Follow these steps to download and install ScriptCraft.
-... Congratulations! You just installed your own Minecraft Server with
-the ScriptCraft Mod and are now ready to begin programming in Minecraft.
+1. Download the [scriptcraft.jar][dl] plugin and save it to the `plugins` directory and restart the server by typing `java -jar spigot-1.10.2.jar`.
+2. At the server prompt type `js 1 + 1` and hit enter. The result `2` should be displayed.
-Normally, Minecraft Mods are written in Java. This makes writing your
-own extension or game rules difficult because you must first learn Java.
-Java is different enough from Javascript. With the ScriptCraft plug-in
-installed, you don't have to learn Java, you can extend and customize
-Minecraft your way using Javascript. Javascript is easier to learn than
-Java but it's also more flexible and powerful and is used for creating
-interactive web sites and many other applications.
+Congratulations - you've just installed your Custom Minecraft Server and are ready to begin writing your first mod!
## Configuring your Server (optional)
-Once you've installed CraftBukkit, depending on your specific needs,
-you might want to consider setting the following properties in the
-`server.properties` file...
+Once you've installed SpigotMC, depending on your specific needs,
+you might want to consider setting the following properties in the `server.properties` file:
# completely flat worlds are best for building from scratch
+ # bukkit/spigotmc
level-type=FLAT
generate-structures=false
@@ -97,14 +80,14 @@ If you don't already know Javascript, don't worry, you'll learn a little
about Programming and Javascript along the way. You've set up a
Minecraft server and are ready to connect ...
-1. Launch Minecraft (keep the Bukkit Command window open).
+1. Launch Minecraft.
2. Click 'Multi-Player'
3. Click 'Add Server'
4. Type any name you like in the name field then type `localhost` in the
address field. `localhost` is a special internet address that points to
your own computer.
-5. Click 'Join Server' to join the craftbukkit server. If the version
-of Minecraft is incompatible with the version of CraftBukkit you will
+5. Click 'Join Server' to join the server. If the version
+of Minecraft is incompatible with the version of the server you will
not be able to connect to the server. To fix this, you can create a
Minecraft profile in your client. Profiles let you decide which
version of Minecraft client you want to run so that your client and
@@ -135,9 +118,9 @@ called `location`. We can use that name like this...
Blackrock Castle
-...You might be wondering where the `''` (called double-quotes) went.
+...You might be wondering why there's no enclosing `'` single quotes.
When telling the computer to store some text, you have to put `'`
-(that's the double-quote character - press Shift+2) at the start and end
+(that's the single-quote character) at the start and end
of the text. The computer doesn't store these quote characters, only the
text between them. The computer will store the variables while the
Minecraft Server is running. Repeat the last command you entered by
@@ -152,7 +135,7 @@ value like this...
The `var` keyword is only needed when you first create the variable. Now
execute this command...
- /js echo( location )
+ /js echo( self, location )
...and it displays...
@@ -163,11 +146,11 @@ the variables you'll create in your in-game commands and scripts, there
are handy *free* variables created for you by ScriptCraft. One such variable is
`self`, it contains information about the current player (that's you)...
- /js echo ( self )
+ /js echo ( self, self.name )
-... displays the following...
+... displays something like the following...
- CraftPlayer{name=walterh}
+ walterh
... for me but the message displayed will be different for every player.
@@ -181,7 +164,7 @@ looking at the building functions let's look at the `echo()` function.
`echo()` - as its name implies - will echo back at you whatever you
tell it. For example, type ...
- /js echo('Hello')
+ /js echo( self, 'Hello')
... and the game will display...
@@ -189,7 +172,7 @@ tell it. For example, type ...
... type ...
- /js echo( 5 + 7 )
+ /js echo( self, 5 + 7 )
... and the game will display...
@@ -199,7 +182,7 @@ tell it. For example, type ...
don't recommend it. Homework and Minecraft don't mix! The `echo()`
function will display anything you tell it to - Text, Numbers and other types...
- /js echo( new Date() )
+ /js echo( self, new Date() )
... prints today's date. If the statement above looks confusing - don't
worry - `new Date()` creates a new date object - I'll talk about objects
@@ -466,12 +449,12 @@ Once you've installed Notepad++, Launch it, create a new file and type the follo
```javascript
exports.greet = function( player ) {
- player.sendMessage('Hi ' + player.name);
+ echo( player, 'Hi ' + player.name);
}
```
... then save the file in a new directory
-`craftbukkit/plugins/scriptcraft/plugins/{your_name}` (replace
+`scriptcraft/plugins/{your_name}` (replace
{your_name} with your own name) and call the file `greet.js` (be sure
to change the file-type option to '*.* All Files' when saving or
NotePad++ will add a '.txt' extension to the filename. Now switch back
@@ -492,7 +475,7 @@ loaded. Try it out by typing this command...
minecraft username. Congratulations - You've just written your very
first Minecraft Mod! With ScriptCraft installed, writing Minecraft
Mods is as simple as writing a new javascript function and saving it
-in a file in the craftbukkit/plugins/scriptcraft/plugins
+in a file in the scriptcraft/plugins
directory. This function will now be avaible every time you launch
minecraft. This is a deliberately trivial minecraft mod but the
principles are the same when creating more complex mods.
@@ -507,17 +490,17 @@ one or more functions, objects or variables. For example...
#### thrower.js
```javascript
-exports.egg = function(player){
- player.throwEgg();
+exports.boo = function(player){
+ echo( player, 'Boo!');
}
-exports.snowball = function(player){
- player.throwSnowball();
+exports.yo = function(player){
+ echo( player, 'Yo!');
}
```
-... is a plugin which provides 2 javascript functions called `egg()`
-and `snowball()` which can be invoked from the in-game prompt like
-this `/js egg(self)` or `/js snowball(self)`.
+... is a plugin which provides 2 javascript functions called `boo()`
+and `yo()` which can be invoked from the in-game prompt like
+this `/js boo(self)` or `/js yo(self)`.
## Parameters
If you want to change the `greet()` function so that it displays a
@@ -532,7 +515,7 @@ Change the `greet()` function so that it looks like this...
```javascript
exports.greet = function ( greeting , player) {
- player.sendMessage( greeting + player.name );
+ echo( player, greeting + player.name );
}
```
@@ -599,53 +582,78 @@ things...
compare the ages of your friends or siblings to your own age.
## More fun with `true` or `false`
-You can find out if you can Fly in minecraft by typing the following statement...
- /js self.allowFlight
+You can find out if you can Fly in minecraft by typing the following statement ...
+
+ /js self.allowFlight
... the result will be `true` or `false` depending on whether you can
fly or not. You can turn on and off your ability to fly by setting
-your `allowFlight` property to `true` or `false`. Try it...
+your `allowFlight` property to `true` or `false`. Try it ...
- /js self.allowFlight = true
+ /js self.allowFlight = true;
-... Now you can fly! To turn off flight...
+... Now you can fly! Double-press the space bar key to start flying. To turn off flight ...
- /js self.allowFlight = false
+ /js self.allowFlight = false;
... and you come crashing down to earth. This is just one example of
-how `true` and `false` are used throughout ScriptCraft - these are
-called `boolean` values - named after [George Boole][boole], a 19th Century
+how `true` and `false` are used throughout ScriptCraft – these are
+called `boolean` values – named after [George Boole][boole], a 19th Century
Maths Professor at University College Cork. There are plenty more
-examples of boolean values in Minecraft. You can find out if monsters
-are allowed in your minecraft world by typing the following
-statement...
+examples of boolean values in Minecraft. You can find out if it's
+raining in your minecraft world by typing the following statement ...
+
+ /js self.world.hasStorm()
+
+... The result of this statement will be either `false` (if it's not raining) or
+`true` (if it *is* raining). If it's raining, you can make it stop raining typing the following command:
+
+ /js self.world.setStorm(false)
+
+... Similarly, to make it start raining you can issue the following command:
+
+ /js self.world.setStorm( true )
+
+### Booleans and JavaBeans
+
+There are many *boolean* properties you can use to turn on or off
+certain game behaviours. For example, the *thundering* behavior is turned
+on or off using the World's `thundering` property. The World object's
+properties and methods are [documented on the SpigotMC JavaDocs World
+page][spworld]. When browsing the SpigotMC JavaDoc pages, whenever
+you see a method whose name begins with `is` such as `isThundering()` and
+a companion method `setThundering()`, these methods are called *JavaBean*
+methods - the *thundering* property is a *JavaBean* property and there
+are two ways you can use JavaBean properties in Javascript. You can
+*get* and *set* the property using the methods provided by Java. To
+*get* the thundering property you can call the JavaBean Method:
+
+ /js self.world.isThundering()
+
+... or you can get the property like this:
- /js self.location.world.allowMonsters
+ /js self.world.thundering
-... The result of this statement will be either `false` (Phew!) or
-`true` (Yikes!) depending on how your server has been
-configured. However, typing the following statement doesn't work as
-expected...
+To *set* the thundering property, you can call the JavaBean method:
- /js self.location.world.allowMonsters = true
+ /js self.world.setThundering( true )
-... This statement won't work as expected - it will give an Error
-message. This is because sometimes we can read variables but we can't
-change them the same way we read them (this is because of how
-Javascript, Java and the CraftBukkit API work together). To turn on or
-off the spawning of monsters, type the following...
+... or you can set the property like this:
- /js self.location.world.setSpawnFlags(false, true)
+ /js self.world.thundering = true
-... the `setSpawnFlags()` method takes 2 parameters, the first
-parameter says whether or not monsters can spawn, and the 2nd says
-whether or not Animals can spawn. (SIDENOTE: You may be wondering how
-to change other aspects of the Minecraft game - pretty much all
+Whatever approach you use, the result will be the same.
+
+[cmworld]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/World.html
+[spworld]: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/World.html
+
+### SIDENOTE
+You may be wondering how to change other aspects of the Minecraft game - pretty much all
aspects of the game can be changed. Changes are made using what are
called `API` calls - these are calls to functions and methods in
-Minecraft - you can read more about these on the [CraftBukkit API
-Reference][cbapi].)
+Minecraft - you can read more about these on the [SpigotMC API
+Reference][spigotapi].
## ...and Again, and Again, and Again,...
@@ -686,26 +694,24 @@ connnected to a server, the worlds of a server and so on.
At the in-game command prompt type the following then hit Enter...
- /js for (var i = 0;i < server.onlinePlayers.length; i++){ server.onlinePlayers[i].sendMessage('Hi!'); }
+ /js var utils = require('utils');
+ /js var players = utils.players();
+ /js for (var i = 0;i < players.length; i++){ echo(players[i], 'Hi!'); }
... Lets look at these statements in more detail. We had to enter the
statements on a single line at the in-game command prompt but the
statements could be written like this...
```javascript
-var players = server.onlinePlayers;
-var player;
-var i;
-for ( i = 0; i < players.length; i++ ) {
- player = players[i];
- player.sendMessage( 'Hi!' );
+var utils = require('utils');
+var players = utils.players();
+for (var i = 0;i < players.length; i++) {
+ echo(players[i], 'Hi!');
}
```
-... On the first line, a new variable `players` is created from the
-server object's onlinePlayers property. `players` is more concise and
-easier to type than the long-winded `server.onlinePlayers`. On the
-fourth line, the for loop is declared, a counter variable `i` is set
+... On the 2nd line, a new variable `players` is created and assigned a value by calling utils.players().
+On the next line, the for loop is declared, a counter variable `i` is set
to 0 (zero - arrays in javascript start at 0 not 1) and each time
around the loop is tested to see if it's less than the number of
players online. At the end of each run around the loop the `i`
@@ -730,18 +736,19 @@ TextWrangler or your editor of choice) and add the following code at
the bottom of the file...
```javascript
+var utils = require('utils');
exports.hiAll = function () {
- var players = server.onlinePlayers,
- player,
- i;
- for ( i = 0; i < players.length; i++) {
- player = players[i];
- player.sendMessage( 'Hi!' );
- }
+ var players = utils.players();
+ player,
+ i;
+ for ( i = 0; i < players.length; i++) {
+ player = players[i];
+ echo( player, 'Hi!' );
+ }
}
```
-... save the file, at the in-game command prompt type `reload` and
+... save the file, at the in-game command prompt type `/js refresh()` and
then type `/js hiAll()`. This will send the message `Hi!` to all of
the players connected to your server. You've done this using a `for`
loop and arrays. Arrays and `for` loops are used heavily in all types
@@ -782,7 +789,8 @@ through arrays. The following loop prints out all of the players on
the server...
```javascript
-var players = server.onlinePlayers;
+var utils = require('utils');
+var players = utils.players();
var i = 0;
while ( i < players.length ) {
console.log( players[i] );
@@ -811,7 +819,9 @@ loops. utils.foreach() takes two parameters...
Let's see it in action, the following code will `console.log()` (print) the
name of each online player in the server console window...
- utils.foreach( server.onlinePlayers, console.log );
+ var utils = require('utils');
+ var players = utils.players;
+ utils.foreach( players, console.log );
... in the above example, the list of online players is processed one
at a time and each item (player) is passed to the `console.log`
@@ -827,8 +837,10 @@ utils.foreach() function...
give every player the ability to fly.
*/
var utils = require('utils');
-utils.foreach( server.onlinePlayers, function( player ) {
- player.setAllowFlight(true);
+var players = utils.players();
+utils.foreach( players, function( player ) {
+ player.capabilities.flying = true;
+ player.updateCapabilities();
} );
```
@@ -839,19 +851,24 @@ utils.foreach( server.onlinePlayers, function( player ) {
Play a Cat's Meow sound for each player.
*/
var utils = require('utils');
-utils.foreach( server.onlinePlayers, function( player ) {
- player.playSound(player.location,
- org.bukkit.Sound.CAT_MEOW,
- 1,
- 1);
+var players = utils.players();
+var sounds = require('sounds');
+utils.foreach( players, function( player ) {
+ sounds.entityCatAmbient( player ); // spigot 1.9
+ /* canarymod only
+ sounds.catMeow( player );
+ */
} );
```
### Exercise
Try changing the above function so that different sounds are played
-instead of a Cat's Meow. You'll need to lookup the [CraftBukkit API's
-Sound class][soundapi] to see all of the possible sounds that can be
-played.
+instead of a Cat's Meow. To see all of the possible sounds that can be
+played, load the sounds module at the in-game prompt using the following statement:
+
+ /js var sounds = require('sounds');
+
+... then type `/js sounds.` and press the TAB key to see a list of all possible sounds.
Loops are a key part of programming in any language. Javascript
provides `for` and `while` statements for looping and many javascript
@@ -878,28 +895,31 @@ pointing at the block, type the following into the in-game prompt...
so the next step is to repeat this over and over. This is where `for`
loops come in. Open your favorite text editor and create a new file in
your scriptcraft/plugins/{your-name} directory, name the file `myskyscraper.js`, then
-type the following...
+type the following code and save:
```javascript
-var myskyscraper = function(floors) {
- var i ;
- if ( typeof floors == 'undefined' ) {
- floors = 10;
- }
- this.chkpt('myskyscraper'); // saves the drone position so it can return there later
- for ( i = 0; i < floors; i++ ) {
- this.box(blocks.iron,20,1,20)
- .up()
- .box0(blocks.glass_pane,20,3,20)
- .up(3);
- }
- return this.move('myskyscraper'); // return to where we started
+function myskyscraper( floors ) {
+ var i ;
+ if ( typeof floors == 'undefined' ) {
+ floors = 10;
+ }
+ // bookmark the drone's position so it can return there later
+ this.chkpt('myskyscraper');
+ for ( i = 0; i < floors; i++ ) {
+ this
+ .box(blocks.iron,20,1,20)
+ .up()
+ .box0(blocks.glass_pane,20,3,20)
+ .up(3);
+ }
+ // return the drone to where it started
+ this.move('myskyscraper');
};
-var Drone = require('../drone/drone').Drone;
-Drone.extend('myskyscraper',myskyscraper);
+var Drone = require('drone');
+Drone.extend( myskyscraper );
```
-... so this takes a little explaining. First I create a new function
+So this takes a little explaining. First I create a new function
called myskyscraper that will take a single parameter `floors` so that
when you eventually call the `myskyscraper()` function you can tell it
how many floors you want built. The first statement in the function
@@ -913,12 +933,12 @@ floor. When the loop is done I return the drone to where it started.
The last 2 lines load the drone module (it must be loaded before I can
add new features to it) and the last line extends the 'Drone' object
so that now it can build skyscrapers among other things. Once you've
-typed in the above code and saved the file, type `reload` in your
-in-game prompt, then type ...
+typed in the above code and saved the file, type `/js refresh()` in your
+in-game prompt, then type:
/js myskyscraper(2);
-... A two-story skyscraper should appear. If you're feeling
+A two-story skyscraper should appear. If you're feeling
adventurous, try a 10 story skyscraper! Or a 20 story skyscraper!
Minecraft has a height limit (256 blocks from bedrock) beyond which
you can't build. If you try to build higher than this then building
@@ -938,38 +958,37 @@ All the programs we have seen so far have been fairly predictable - they went
straight through the statements, and then went back to the beginning again. This is
not very useful. In practice the computer would be expected to make decisions and
act accordingly. The javascript statement used for making decisions is `if`.
-While standing on the ground in-game, type the following at the command prompt...
+While standing on the ground in-game, type the following at the command prompt:
+
+ /js if ( self.onGround ) { echo('You are not flying!'); }
+
+the following message should have appeared on your screen:
- /js if ( self.flying ) { echo('Hey, You are flying!'); }
+ You are not flying!
-... No message should appear on screen. That is - `Hey, You are
-flying!` should *not* appear on screen. Now double-tap the `space`
-bar to start flying in-game (tap the space bar twice in rapid
+Now double-tap the `space` bar to start flying in-game (tap the space bar twice in rapid
succession), then press and hold space to rise above the ground. Now
enter the same statement again (If you don't want to type the same
statement again, just press `/` then press the `UP` cursor key on your
keyboard, the statement you entered previously should reappear.
- /js if ( self.flying ) { echo('Hey, You are flying!'); }
+ /js if ( self.onGround ) { echo('You are not flying!'); }
-... this time the following message should have appeared on your screen...
-
- Hey, You are flying!
+This time no message should appear on your screen.
The `if` statement tests to see if something is `true` or `false` and
if `true` then the block of code between the curly braces ( `{` and
`}` ) is executed - but only if the condition is true. The condition
-in the above example is `self.flying` which will be `true` if you are
-currently flying or `false` if you aren't.
+in the above example is `!self.onGround` (self is _not_ on ground) which
+will be `true` if you are currently flying or `false` if you aren't.
What if you wanted to display a message only if a condition is *not*
-true ? For example to only display a message if the player is *not*
-flying...
+true ? For example to only display a message if the player is *not* on the ground:
- /js if ( ! self.flying ) { echo ('You are not flying.'); }
+ /js if ( !self.onGround ) { echo ('You are flying!'); }
-... This code differs in that now there's a `!` (the exclamation mark)
-before `self.flying`. The `!` symbol negates (returns the opposite of)
+This code differs in that now there's a `!` (the exclamation mark)
+before `self.onGround`. The `!` symbol negates (returns the opposite of)
whatever follows it.
What if you want to display a message in both cases - whether you're
@@ -978,16 +997,16 @@ Open your favorite editor and type the following code into a new file
in your scriptcraft/plugins directory...
```javascript
-function flightStatus( player ) {
- if ( player.flying ) {
- player.sendMessage( 'Hey, You are flying!' );
- } else {
- player.sendMessage( 'You are not flying.' );
- }
+exports.flightStatus = function( player ) {
+ if ( player.onGround ) {
+ echo(player, 'You are not flying!' );
+ } else {
+ echo(player, 'Hey, You are flying!' );
+ }
}
```
-... now type `/reload` at the in-game prompt then type `/js
+... now type `/js refresh()` at the in-game prompt then type `/js
flightStatus(self)` and an appropriate message will appear based on
whether or not you're currently flying. Type the `/js flightStatus()`
command while on the ground and while flying. The message displayed in
@@ -1016,49 +1035,27 @@ following code sends a message to any player who breaks a block in the
game...
```javascript
-events.on('block.BlockBreakEvent', function ( event ) {
- var breaker = event.player;
- breaker.sendMessage('You broke a block');
-} );
+function myBlockBreakHook( event ){
+ var breaker = event.player;
+ echo( breaker, 'You broke a block');
+}
+events.blockBreak( myBlockBreakHook );
```
-The `events.on()` function is how you *register* a function which you
-want to be called whenever a particular type of event occurs. In the
-above code the first parameter `'block.BlockBreakEvent'` is the type
-of event I want to listen for and the second parameter is the function
+The `events.blockBreak()` function is just one of the many `events` functions which can be used to *register* a function to be called whenever a particular type of event occurs. In the
+above code the blockBreak function takes as a parameter a function
I want to be called when that event occurs. The function I want called
in turn takes 1 parameter. The `event` object has all the information
about the event which just occurred. I can tell who broke the block
and send a message to the player. The important thing to note is that
-the function defined above will not be called until a player breaks a
+the `myBlockBreakHook` function defined above will not be called until a player breaks a
block. Try it - save the above code in a new file in the
`scriptcraft/plugins` directory then type `/js refresh()` to reload
scriptcraft. Then break a block in the game and you should see the
message 'You broke a block'.
There are many types of events you can listen for in Minecraft. You can
-browse [all possible Bukkit events][bkevts] (click the 'Next
-Package' and 'Previous Package' links to browse).
-
-It's important to note that when browsing the Bukkit API's
-[org.bukkit.event][bkevts] package, if you see a class called
-'org.bukkit.events.entity.EntityShootBowEvent', then when calling
-`events.on()` you can listen to such an event using either the fully
-qualified Class name...
-
- events.on(org.bukkit.events.entity.EntityShootBowEvent, function( event ) {
- ...
- });
-
-or an abbreviated name in string form...
-
- events.on('entity.EntityShootBowEvent', function( event ) {
- ...
- });
-
-If the `events.on()` function gets a String (text) as its first
-parameter it automatically converts it to the appropriate Class by
-prepending the 'org.bukkit.events' package.
+browse [all possible event registration functions][spevts2] in the API Reference.
For custom events (events which aren't in the org.bukkit.event tree)
just specify the fully qualified class name instead. E.g. ...
@@ -1072,11 +1069,12 @@ just specify the fully qualified class name instead. E.g. ...
If you want an event handler to only execute once, you can remove the handler like this...
```javascript
-events.on('block.BlockBreakEvent', function( evt ) {
- var breaker = evt.player;
- breaker.sendMessage('You broke a block');
- this.unregister();
-} );
+function myBlockBreakHook( evt ) {
+ var breaker = evt.player;
+ echo( breaker, 'You broke a block');
+ this.unregister();
+}
+events.blockBreak( myBlockBreakHook );
```
The `this.unregister();` statement will remove this function from the
@@ -1088,12 +1086,14 @@ to stop listening for events.
To unregister a listener *outside* of the listener function...
```javascript
-var myBlockBreakListener = events.on( 'block.BlockBreakEvent', function( evt ) { ... } );
+function myBlockBreakHook( evt ){
+ var breaker = evt.player;
+ echo( breaker, 'You broke a block');
+}
+var myBlockBreakListener = events.blockBreak( myBlockBreakHook );
...
myBlockBreakListener.unregister();
```
-
-
## Keeping Score - Lookup tables in Javascript
In the *Event-Driven Programming* section, I defined a function which
@@ -1145,10 +1145,10 @@ like this...
```javascript
var scoreboard = {
- walter: 5,
- tom: 6,
- jane: 8,
- bart: 7
+ walter: 5,
+ tom: 6,
+ jane: 8,
+ bart: 7
};
```
@@ -1157,7 +1157,7 @@ parameter and returned their score, I'd do it like this...
```javascript
function getScore(player){
- return scoreboard[ player ];
+ return scoreboard[ player ];
}
```
@@ -1175,21 +1175,22 @@ var utils = require('utils');
var scores = {};
exports.initialise = function(names){
- scores = {};
- utils.foreach(names, function(name){
- scores[name] = 0;
- });
+ scores = {};
+ utils.foreach(names, function(name){
+ scores[name] = 0;
+ });
};
-/* changes score by diff e.g. to add 6 to the player's current score
- updateScore('walter',6); // walter's new score = 5 + 6 = 11.
+/*
+ changes score by diff e.g. to add 6 to the player's current score
+ updateScore('walter',6); // walter's new score = 5 + 6 = 11.
*/
exports.updateScore = function(name, diff){
- scores[name] += diff;
+ scores[name] += diff;
};
exports.getScore = function(name){
- return scores[name];
+ return scores[name];
};
```
@@ -1202,18 +1203,24 @@ keep a count of how many blocks each player has broken ...
```javascript
var breaks = {};
-// every time a player joins the game reset their block-break-count to 0
-events.on('player.PlayerJoinEvent', function( event ) {
- breaks[event.player] = 0;
-});
-events.on('block.BlockBreakEvent', function( event ) {
- var breaker = event.player;
- var breakCount = breaks[breaker.name];
- breakCount++; // increment the count.
- breaks[breaker.name] = breakCount;
-
- breaker.sendMessage('You broke ' + breakCount + ' blocks');
-});
+
+/*
+ every time a player joins the game reset their block-break-count to 0
+*/
+function initializeBreakCount( event ){
+ breaks[event.player.name] = 0;
+}
+events.playerJoin( initializeBreakCount );
+
+/*
+ every time a player breaks a block increase their block-break-count
+*/
+function incrementBreakCount( event ){
+ breaks[event.player.name] += 1; // add 1
+ var breakCount = breaks[event.player.name];
+ echo( event.player, 'You broke ' + breakCount + ' blocks');
+}
+events.blockBreak( incrementBreakCount );
```
With a little more work, you could turn this into a game where players
@@ -1230,27 +1237,32 @@ covered here. If you want to dive deeper into programming and modding
minecraft, I recommend reading the accompanying [ScriptCraft API
reference][api] which covers all of the ScriptCraft functions, objects
and methods. I also recommend reading the source code to some of the
-existing scriptcraft add-ons, the *chat* module (
-`scriptcraft/plugins/chat/color.js` ) is a good place to start, followed by
-[Anatomy of a ScriptCraft Plug-in][ap]. The online [Craftbukkit API
-Reference][cbapi] provides lots of valuable information about the
+existing scriptcraft plugins, followed by
+[Anatomy of a ScriptCraft Plug-in][ap]. The online [SpigotMC API
+Reference][spigotapi] provides lots of valuable information about the
different objects and methods available for use by ScriptCraft.
-[buk]: http://wiki.bukkit.org/Setting_up_a_server
+[cmadmin]: https://github.com/walterhiggins/canarymod-admin-guide/
[dlbuk2]: http://dl.bukkit.org/downloads/craftbukkit/
+[dlcm]: http://canarymod.net/releases
[bii]: http://wiki.bukkit.org/Setting_up_a_server
[sc-plugin]: http://scriptcraftjs.org/download/
[ce]: http://www.codecademy.com/
[mcdv]: http://www.minecraftwiki.net/wiki/Data_values
[np]: http://notepad-plus-plus.org/
[cbapi]: http://jd.bukkit.org/beta/apidocs/
+[cmapi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/
+[spigotapi]: https://hub.spigotmc.org/javadocs/spigot/
[boole]: http://en.wikipedia.org/wiki/George_Boole
-[soundapi]: http://jd.bukkit.org/beta/apidocs/org/bukkit/Sound.html
+[soundapi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/effects/SoundEffect.Type.html
[ap]: Anatomy-of-a-Plugin.md
[api]: API-Reference.md
[twl]: http://www.barebones.com/products/textwrangler/
[bkevts]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/package-summary.html
+[cmevts]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/package-summary.html
+[cmevts2]: API-Reference.md#events-helper-module-canary-version
+[spevts2]: API-Reference.md#events-helper-module-spigotmc-version
[img_echo_date]: img/ypgpm_echo_date.png
[img_3d_shapes]: img/ypgpm_3dshapes.jpg
[img_whd]: img/ypgpm_whd.jpg
diff --git a/src/generateEventsHelper.js b/src/generateEventsHelper.js
deleted file mode 100644
index 26ed1de8b..000000000
--- a/src/generateEventsHelper.js
+++ /dev/null
@@ -1,81 +0,0 @@
-var File = java.io.File,
- FileReader = java.io.FileReader,
- FileInputStream = java.io.FileInputStream,
- out = java.lang.System.out,
- ZipInputStream = java.util.zip.ZipInputStream,
- zis = new ZipInputStream(new FileInputStream('./target/minecraft/craftbukkit.jar')),
- entry = null;
-var content = [
- '/*********************',
- '## Events Helper Module',
- 'The Events helper module provides a suite of functions - one for each possible event.',
- 'For example, the events.blockBreak() function is just a wrapper function which calls events.on(org.bukkit.event.block.BlockBreakEvent, callback, priority)',
- 'This module is a convenience wrapper for easily adding new event handling functions in Javascript. ',
- 'At the in-game or server-console prompt, players/admins can type `events.` and use TAB completion ',
- 'to choose from any of the approx. 160 different event types to listen to.',
- '',
- '### Usage',
- '',
- ' events.blockBreak(function(evt){ ',
- ' evt.player.sendMessage("You broke a block!"); ',
- ' });',
- '',
- '... which is just a shorter and less error-prone way of writing ...',
- '',
- ' events.on("block.BlockBreakEvent",function(evt){ ',
- ' evt.player.sendMessage("You broke a block!");',
- ' });',
- '',
- 'The crucial difference is that the events module now has functions for each ',
- 'of the built-in events. The functions are accessible via tab-completion so will help ',
- 'beginning programmers to explore the events at the server console window.',
- '',
- '***/'
-];
-for (var i = 0; i< content.length; i++){
- out.println(content[i]);
-}
-while ( ( entry = zis.nextEntry) != null) {
- var name = '' + entry.name;
- if (name.match(/org\/bukkit\/event\/.+Event\.class$/)){
- name = name.replace(/\//g,'.').replace('.class','');
-
- // abstract events don't have a static getHandlerList method so
- // shouldn't be added to this module
- var hasHandlerList = false;
- try {
- hasHandlerList = engine.eval(name + '.getHandlerList');
- } catch ( ex ) {
- // exception is thrown for JRE7
- continue;
- }
- if ( !hasHandlerList ) {
- continue;
- }
- var parts = name.split('.');
- var shortName = name.replace('org.bukkit.event.','');
- var fname = parts.reverse().shift().replace(/^(.)/,function(a){ return a.toLowerCase()}).replace(/Event$/,'');
-
- var comment = [
- '/*********************',
- '### events.' + fname + '()',
- '',
- '#### Parameters ',
- '',
- ' * callback - A function which is called whenever the ' + shortName + ' event is fired',
- '',
- ' * priority - optional - see events.on() for more information.',
- '',
- '***/'
- ];
- for (var i = 0; i < comment.length; i++){
- out.println(comment[i]);
- }
- out.println('exports.' + fname + ' = function(callback,priority){ ');
- out.println(' return events.on(' + name + ',callback,priority);');
- out.println('};');
- }
-}
-
-
-
diff --git a/src/main/java/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java b/src/main/java/bukkit/org/scriptcraftjs/bukkit/ScriptCraftPlugin.java
similarity index 58%
rename from src/main/java/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java
rename to src/main/java/bukkit/org/scriptcraftjs/bukkit/ScriptCraftPlugin.java
index 7a0681ce7..9713500dc 100644
--- a/src/main/java/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java
+++ b/src/main/java/bukkit/org/scriptcraftjs/bukkit/ScriptCraftPlugin.java
@@ -1,52 +1,63 @@
-package net.walterhiggins.scriptcraft;
+package org.scriptcraftjs.bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
import java.io.InputStreamReader;
-import javax.script.*;
-import java.util.List;
import java.util.ArrayList;
-
-import org.bukkit.plugin.java.JavaPlugin;
-import org.bukkit.command.*;
-import org.bukkit.event.Listener;
+import java.util.List;
public class ScriptCraftPlugin extends JavaPlugin implements Listener
{
+ public boolean canary = false;
+ public boolean bukkit = true;
// right now all ops share the same JS context/scope
// need to look at possibly having context/scope per operator
//protected Map playerContexts = new HashMap();
private String NO_JAVASCRIPT_MESSAGE = "No JavaScript Engine available. ScriptCraft will not work without Javascript.";
protected ScriptEngine engine = null;
- @Override
- public void onEnable()
+
+ @Override public void onEnable()
{
- try{
+ Thread currentThread = Thread.currentThread();
+ ClassLoader previousClassLoader = currentThread.getContextClassLoader();
+ currentThread.setContextClassLoader(getClassLoader());
+ try {
ScriptEngineManager factory = new ScriptEngineManager();
this.engine = factory.getEngineByName("JavaScript");
- if (this.engine == null){
- this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
- } else {
- Invocable inv = (Invocable)this.engine;
- this.engine.eval(new InputStreamReader(this.getResource("boot.js")));
- inv.invokeFunction("__scboot", this, engine);
- }
- }catch(Exception e){
- e.printStackTrace();
- this.getLogger().severe(e.getMessage());
- }
+ if (this.engine == null) {
+ this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
+ } else {
+ Invocable inv = (Invocable) this.engine;
+ this.engine.eval(new InputStreamReader(this.getResource("boot.js")));
+ inv.invokeFunction("__scboot", this, engine);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ this.getLogger().severe(e.getMessage());
+ } finally {
+ currentThread.setContextClassLoader(previousClassLoader);
+ }
}
+
public List onTabComplete(CommandSender sender, Command cmd,
String alias,
String[] args)
{
List result = new ArrayList();
- if (this.engine == null){
- this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
- return null;
- }
+ if (this.engine == null) {
+ this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
+ return null;
+ }
try {
Invocable inv = (Invocable)this.engine;
inv.invokeFunction("__onTabComplete", result, sender, cmd, alias, args);
- }catch (Exception e){
+ } catch (Exception e) {
sender.sendMessage(e.getMessage());
e.printStackTrace();
}
@@ -56,15 +67,14 @@ public List onTabComplete(CommandSender sender, Command cmd,
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
{
boolean result = false;
- String javascriptCode = "";
Object jsResult = null;
- if (this.engine == null){
- this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
- return false;
- }
- try {
+ if (this.engine == null) {
+ this.getLogger().severe(NO_JAVASCRIPT_MESSAGE);
+ return false;
+ }
+ try {
jsResult = ((Invocable)this.engine).invokeFunction("__onCommand", sender, cmd, label, args);
- }catch (Exception se){
+ } catch (Exception se) {
this.getLogger().severe(se.toString());
se.printStackTrace();
sender.sendMessage(se.getMessage());
diff --git a/src/main/java/canary/org/scriptcraftjs/canarymod/ScriptCraftPlugin.java b/src/main/java/canary/org/scriptcraftjs/canarymod/ScriptCraftPlugin.java
new file mode 100644
index 000000000..6c16cc171
--- /dev/null
+++ b/src/main/java/canary/org/scriptcraftjs/canarymod/ScriptCraftPlugin.java
@@ -0,0 +1,164 @@
+package org.scriptcraftjs.canarymod;
+
+import java.io.InputStreamReader;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptEngine;
+import javax.script.Invocable;
+import java.util.List;
+import java.util.ArrayList;
+
+import net.canarymod.plugin.Plugin;
+import net.canarymod.plugin.PluginListener;
+import net.canarymod.tasks.ServerTask;
+import net.canarymod.tasks.TaskOwner;
+import net.canarymod.commandsys.CommandListener;
+import net.canarymod.commandsys.Command;
+import net.canarymod.commandsys.TabComplete;
+import net.canarymod.chat.MessageReceiver;
+import net.canarymod.Canary;
+// event help stuff
+import net.canarymod.hook.Dispatcher;
+import net.canarymod.hook.Hook;
+
+public class ScriptCraftPlugin extends Plugin implements PluginListener, CommandListener
+{
+ public boolean canary = true;
+ public boolean bukkit = false;
+ private String NO_JAVASCRIPT_MESSAGE = "No JavaScript Engine available. " +
+ "ScriptCraft will not work without Javascript.";
+ protected ScriptEngine engine = null;
+
+ @Override
+ public void disable(){
+ try {
+ ((Invocable)this.engine).invokeFunction("__onDisable", this.engine, this);
+ }catch ( Exception e) {
+ this.getLogman().error(e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean enable()
+ {
+ try{
+ ScriptEngineManager factory = new ScriptEngineManager();
+ this.engine = factory.getEngineByName("JavaScript");
+ if (this.engine == null){
+ this.getLogman().error(NO_JAVASCRIPT_MESSAGE);
+ } else {
+ Invocable inv = (Invocable)this.engine;
+ //File f = new File(this.getJarPath());
+ InputStreamReader reader = new InputStreamReader(getClass()
+ .getClassLoader()
+ .getResourceAsStream("boot.js"));
+ this.engine.eval(reader);
+ inv.invokeFunction("__scboot", this, engine, getClass().getClassLoader());
+ }
+
+ Canary.commands().registerCommands(this, this, false);
+ }catch(Exception e){
+ e.printStackTrace();
+ this.getLogman().error(e.getMessage());
+ }
+
+
+ return true;
+ }
+
+ public static interface IDispatcher {
+ public void execute(PluginListener listener, Hook hook);
+ }
+
+ public Dispatcher getDispatcher(final IDispatcher impl){
+ return new Dispatcher(){
+ public void execute(PluginListener listener, Hook hook){
+ impl.execute(listener, hook);
+ }
+ };
+ }
+
+ static class ScriptCraftTask extends ServerTask {
+ private Runnable runnable = null;
+ public ScriptCraftTask(Runnable runnable, TaskOwner owner, long delay, boolean continuous){
+ super(owner, delay, continuous);
+ this.runnable = runnable;
+ }
+ @Override
+ public void run(){
+ this.runnable.run();
+ }
+ }
+
+ public ServerTask createServerTask(Runnable runnable, long delay, boolean continuous){
+ return new ScriptCraftTask(runnable, this, delay, continuous);
+ }
+
+ private void executeCommand( MessageReceiver sender, String[] args) {
+ Object jsResult = null;
+ if (this.engine == null){
+ this.getLogman().error(NO_JAVASCRIPT_MESSAGE);
+ return;
+ }
+ try {
+ jsResult = ((Invocable)this.engine).invokeFunction("__onCommand", sender, args);
+ }catch (Exception se){
+ this.getLogman().error(se.toString());
+ se.printStackTrace();
+ sender.message(se.getMessage());
+ }
+ if (jsResult != null){
+ return ;
+ }
+ return;
+ }
+
+ @Command(
+ aliases = { "js" },
+ description = "Execute Javascript code",
+ permissions = { "scriptcraft.evaluate" },
+ toolTip = "/js javascript expression")
+ public void jsCommand(MessageReceiver sender, String[] args) {
+
+ executeCommand(sender, args);
+ }
+
+ /*
+ groupmod permission add visitors canary.jsp
+ groupmod permission add visitors canary.command.jsp
+ */
+ @Command(
+ aliases = { "jsp" },
+ description = "Run javascript-provided command",
+ permissions = { "" },
+ toolTip = "/jsp command")
+ public void jspCommand(MessageReceiver sender, String[] args) {
+
+ executeCommand(sender, args);
+ }
+
+ private List complete(MessageReceiver sender, String[] args, String cmd){
+ List result = new ArrayList();
+ if (this.engine == null){
+ this.getLogman().error(NO_JAVASCRIPT_MESSAGE);
+ return null;
+ }
+ try {
+ Invocable inv = (Invocable)this.engine;
+ inv.invokeFunction("__onTabComplete", result, sender, args, cmd);
+ }catch (Exception e){
+ sender.message(e.getMessage());
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ @TabComplete (commands = { "js" })
+ public List jsComplete(MessageReceiver sender, String[] args){
+ return complete(sender, args, "js");
+ }
+
+ @TabComplete (commands = { "jsp" })
+ public List jspComplete(MessageReceiver sender, String[] args){
+ return complete(sender, args, "jsp");
+ }
+}
diff --git a/src/main/js/lib/.gitignore b/src/main/js/lib/.gitignore
new file mode 100644
index 000000000..455bdfa69
--- /dev/null
+++ b/src/main/js/lib/.gitignore
@@ -0,0 +1,2 @@
+/events-helper-bukkit.js
+/events-helper-canary.js
diff --git a/src/main/js/lib/command.js b/src/main/js/lib/command.js
index 6f2ae12ec..2ba6a4d29 100644
--- a/src/main/js/lib/command.js
+++ b/src/main/js/lib/command.js
@@ -42,6 +42,14 @@ var executeCmd = function( args, player ) {
define a new JSP command.
*/
var defineCmd = function( name, func, options, intercepts ) {
+
+ if ( typeof name == 'function'){
+ intercepts = options;
+ options = func;
+ func = name;
+ name = func.name;
+ }
+
if ( typeof options == 'undefined' ) {
options = [];
}
diff --git a/src/main/js/lib/console.js b/src/main/js/lib/console.js
index 75a22871e..854aa3402 100644
--- a/src/main/js/lib/console.js
+++ b/src/main/js/lib/console.js
@@ -16,7 +16,7 @@ The ScriptCraft console methods work like the [Web API implementation][webcons].
console.log('Hello %s', 'world');
Basic variable substitution is supported (ScriptCraft's implementation
-of console uses the Bukkit Plugin [Logger][lgr] under the hood and
+of console uses the Bukkit Plugin [Logger][lgr] or Canary Plugin [Logman][cmlgr] under the hood and
uses [java.lang.String.format()][strfmt] for variable
substitution. All output will be sent to the server console (not
in-game).
@@ -31,43 +31,49 @@ ScriptCraft uses Java's [String.format()][strfmt] so any string substitution ide
}
[lgr]: http://jd.bukkit.org/beta/apidocs/org/bukkit/plugin/PluginLogger.html
+[cmlgr]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/logger/Logman.html
[strfmt]: http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)
[webcons]: https://developer.mozilla.org/en-US/docs/Web/API/console
***/
-var logger = __plugin.logger,
- logMethodName = 'log(java.util.logging.Level,java.lang.String)';
-var argsToArray = function( args ) {
+function argsToArray( args ) {
var result = [];
for ( var i =0; i < args.length; i++ ) {
result.push(args[i]);
}
return result;
}
-var log = function( level, restOfArgs ) {
- var args = argsToArray( restOfArgs );
+function consMsg(params){
+ var args = argsToArray(params);
if ( args.length > 1 ) {
- var msg = java.lang.String.format( args[0], args.slice(1) );
- logger[logMethodName]( level, msg );
+ return java.lang.String.format( args[0], args.slice(1) );
} else {
- logger[logMethodName]( level, args[0] );
+ return args[0];
}
-};
-
-var Level = java.util.logging.Level;
-
-exports.log = function( ) {
- log( Level.INFO, arguments );
-};
+}
-exports.info = function( ) {
- log( Level.INFO, arguments );
-};
+module.exports = function(logger){
-exports.warn = function( ) {
- log( Level.WARNING, arguments );
-};
+ function bukkitLog( level, restOfArgs ) {
+ logger['log(java.util.logging.Level,java.lang.String)'](
+ java.util.logging.Level[level],
+ consMsg(restOfArgs)
+ );
+ }
-exports.error = function( ) {
- log( Level.SEVERE, arguments );
+ if (__plugin.canary){
+ return {
+ log: function( ) { logger.info( consMsg(arguments) ); },
+ info: function( ) { logger.info( consMsg(arguments) ); },
+ warn: function( ) { logger.warn( consMsg(arguments) ); },
+ error: function( ) { logger.error( consMsg(arguments) ); }
+ };
+ } else {
+ return {
+ log: function() { bukkitLog('INFO', arguments ); },
+ info: function() { bukkitLog('INFO', arguments ); },
+ warn: function( ) { bukkitLog('WARNING', arguments ); },
+ error: function( ) { bukkitLog('SEVERE', arguments ); }
+ };
+ }
};
diff --git a/src/main/js/lib/events-bukkit.js b/src/main/js/lib/events-bukkit.js
new file mode 100644
index 000000000..875d91c34
--- /dev/null
+++ b/src/main/js/lib/events-bukkit.js
@@ -0,0 +1,78 @@
+/*global Java, exports, org, __plugin */
+var bkEventPriority = org.bukkit.event.EventPriority,
+ bkEventExecutor = org.bukkit.plugin.EventExecutor,
+ bkRegisteredListener = org.bukkit.plugin.RegisteredListener,
+ bkEventPackage = 'org.bukkit.event.';
+
+var nashorn = (typeof Java != 'undefined');
+
+function getHandlerListForEventType( eventType ){
+ var result = null;
+ var clazz = null;
+ if (nashorn) {
+
+ //Nashorn doesn't like when getHandlerList is in a superclass of your event
+ //so to avoid this problem, call getHandlerList using java.lang.reflect
+ //methods
+ clazz = eventType['class'];
+ result = clazz.getMethod("getHandlerList").invoke(null);
+
+ } else {
+ result = eventType.getHandlerList();
+ }
+
+ return result;
+}
+exports.on = function(
+ /* Java Class */
+ eventType,
+ /* function( registeredListener, event) */
+ handler,
+ /* (optional) String (HIGH, HIGHEST, LOW, LOWEST, NORMAL, MONITOR), */
+ priority ) {
+ var handlerList,
+ regd,
+ eventExecutor;
+
+ if ( typeof priority == 'undefined' ) {
+ priority = bkEventPriority.HIGHEST;
+ } else {
+ priority = bkEventPriority[priority.toUpperCase().trim()];
+ }
+ handlerList = getHandlerListForEventType (eventType);
+
+ var result = { };
+ eventExecutor = new bkEventExecutor( {
+ execute: function( l, evt ) {
+ function cancel(){
+ if (evt instanceof org.bukkit.event.Cancellable){
+ evt.setCancelled(true);
+ }
+ }
+ /*
+ let handlers use this.cancel() to cancel the current event
+ or this.unregister() to unregister from future events.
+ */
+ var bound = {};
+ for (var i in result){
+ bound[i] = result[i];
+ }
+ bound.cancel = cancel;
+ handler.call( bound, evt, cancel );
+ }
+ } );
+ /*
+ wph 20130222 issue #64 bad interaction with Essentials plugin
+ if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
+ then BOOM! the other plugin will throw an error because Rhino can't coerce an
+ equals() method from an Interface.
+ The workaround is to make the ScriptCraftPlugin java class a Listener.
+ Should only unregister() registered plugins in ScriptCraft js code.
+ */
+ regd = new bkRegisteredListener( __plugin, eventExecutor, priority, __plugin, false );
+ handlerList.register( regd );
+ result.unregister = function(){
+ handlerList.unregister( regd );
+ };
+ return result;
+};
diff --git a/src/main/js/lib/events-canary.js b/src/main/js/lib/events-canary.js
new file mode 100644
index 000000000..59e983b72
--- /dev/null
+++ b/src/main/js/lib/events-canary.js
@@ -0,0 +1,68 @@
+/*global nashorn, exports, require, Packages, __plugin*/
+var cmPriority = Packages.net.canarymod.plugin.Priority,
+ cmCanary = Packages.net.canarymod.Canary,
+ cmDispatcher = Packages.net.canarymod.hook.Dispatcher,
+ cmRegisteredPluginListener = Packages.net.canarymod.plugin.RegisteredPluginListener,
+ cmPluginListener = Packages.net.canarymod.plugin.PluginListener;
+var cmHookExecutor = cmCanary.hooks();
+
+exports.on = function(
+ /* Java Class */
+ eventType,
+ /* function( registeredListener, event) */
+ handler,
+ /* (optional) String (CRITICAL, HIGH, NORMAL, LOW, PASSIVE), */
+ priority ) {
+ var handlerList,
+ regd,
+ eventExecutor;
+
+ if ( typeof priority == 'undefined' ) {
+ priority = cmPriority.NORMAL;
+ } else {
+ priority = cmPriority[priority.toUpperCase().trim()];
+ }
+
+ var result = { };
+ eventExecutor = __plugin.getDispatcher( function(l,e){
+ function cancel(){
+ if (e.setCanceled){
+ e.setCanceled();
+ }
+ }
+ /*
+ let handlers use this.cancel() to cancel the current event
+ or this.unregister() to unregister from future events.
+ */
+ var bound = {};
+ for (var i in result){
+ bound[i] = result[i];
+ }
+ bound.cancel = cancel;
+ try {
+ handler.call(bound, e, cancel);
+ } catch ( error ){
+ console.log('Error while executing handler:' + handler +
+ ' for event type:' + eventType +
+ ' error: ' + error);
+ }
+ });
+ /*
+ wph 20130222 issue #64 bad interaction with Essentials plugin
+ if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
+ then BOOM! the other plugin will throw an error because Rhino can't coerce an
+ equals() method from an Interface.
+ The workaround is to make the ScriptCraftPlugin java class a Listener.
+ Should only unregister() registered plugins in ScriptCraft js code.
+ */
+ if (nashorn){
+ // nashorn
+ eventType = require('nashorn-type')(eventType);
+ }
+ regd = new cmPluginListener({});
+ cmHookExecutor.registerHook(regd, __plugin, eventType, eventExecutor, priority);
+ result.unregister = function(){
+ cmHookExecutor.unregisterPluginListener(regd);
+ };
+ return result;
+};
diff --git a/src/main/js/lib/events.js b/src/main/js/lib/events.js
index 427826988..f66fd1150 100644
--- a/src/main/js/lib/events.js
+++ b/src/main/js/lib/events.js
@@ -2,37 +2,53 @@
/************************************************************************
## events Module
-The Events module provides a thin wrapper around Bukkit's
-Event-handling API. Bukkit's Events API makes use of Java Annotations
-which are not available in Javascript, so this module provides a
-simple way to listen to minecraft events in javascript.
+The Events module provides a thin wrapper around CanaryMod's or
+Bukkit's Event-handling API. The Java-based CanaryMod and Bukkit
+Events APIs make use of Java Annotations which are not available in
+Javascript, so this module provides a simple way to listen to
+minecraft events in javascript.
### events.on() static method
-This method is used to register event listeners.
+This method is used to register event listeners. This method is called by all of the Event Helper methods.
+The `events` object has functions for registering listeners for each type of event. For example, you can register a block-break listener using events.on:
+
+```javascript
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
+} );
+```
+
+or you can (and probably should) use the more succinct:
+
+```javascript
+events.blockDestroy( function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
+} );
+```
+
+The events.on method can be used to register standard CanaryMod/Bukkit
+events and can also be used to register non-standard events - that is
+- events provided by plugins.
#### Parameters
- * eventName - A string or java class. If a string is supplied it must
- be part of the Bukkit event class name. See [Bukkit API][buk] for
- details of the many bukkit event types. When a string is supplied
- there is no need to provide the full class name - you should omit
- the 'org.bukkit.event' prefix. e.g. if the string
- "block.BlockBreakEvent" is supplied then it's converted to the
- org.bukkit.event.block.BlockBreakEvent class .
-
- If a java class is provided (say in the case where you've defined
- your own custom event) then provide the full class name (without
- enclosing quotes).
+ * eventType - A Java class. See the [CanaryMod Hook API][cmEvtApi] or [Bukkit Event API][buk] for details of the many event types.
* callback - A function which will be called whenever the event
- fires. The callback should take a single parameter, event (the event fired).
-
- * priority (optional - default: "HIGHEST") - The priority the
- listener/callback takes over other listeners to the same
- event. Possible values are "HIGH", "HIGHEST", "LOW", "LOWEST",
- "NORMAL", "MONITOR". For an explanation of what the different
- priorities mean refer to bukkit's [Event API Reference][buk2].
+ fires. The callback in turn takes 2 parameters:
+
+ - event : the event fired
+ - cancel : a function which if invoked will cancel the event - not all event types are cancelable; this function only cancels cancelable events).
+
+ * priority (optional - default: "CRITICAL" for CanaryMod or "HIGHEST" for Bukkit) -
+ The priority the listener/callback takes over other listeners to the same event.
+ Possible values for CanaryMod are "CRITICAL", "HIGH", "LOW", "NORMAL" and "PASSIVE".
+ For an explanation of what the different CanaryMod Hook priorities
+ mean, refer to CanaryMod's [Hook Priority class][cmPriority].
+ Possible values for Bukkit are "HIGH", "HIGHEST", "LOW", "LOWEST", "NORMAL", "MONITOR".
+ For an explanation of what the different Bukkit Event priorities
+ mean, refer to bukkit's [Event API Reference][buk2].
#### Returns
@@ -43,105 +59,54 @@ An object which can be used to unregister the listener.
The following code will print a message on screen every time a block is broken in the game
```javascript
-events.on( 'block.BlockBreakEvent', function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo(evt.player, evt.player.name + ' broke a block!');
} );
```
To handle an event only once and unregister from further events...
```javascript
-events.on( 'block.BlockBreakEvent', function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
- this.unregister();
+events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt, cancel ) {
+ echo( evt.player, evt.player.name + ' broke a block!');
+ this.unregister();
} );
```
The `this` keyword when used inside the callback function refers to
-the Listener object created by ScriptCraft. It has a single method
-`unregister()` which can be used to stop listening. This is the same
-object which is returned by the `events.on()` function.
+the Listener object created by ScriptCraft. It has 2 methods
+`unregister()` which can be used to stop listening and `cancel()`
+which can be used to cancel the current event. The object returned by
+`events.on()` only has the `unregister()` method, the `cancel()`
+method is only available from within the event handling function.
To unregister a listener *outside* of the listener function...
```javascript
-var myBlockBreakListener = events.on( 'block.BlockBreakEvent', function( evt ) { ... } );
+var myBlockBreakListener = events.on( Packages.net.canarymod.hook.player.BlockDestroyHook, function( evt ) { ... } );
...
myBlockBreakListener.unregister();
```
-To listen for events using a full class name as the `eventName` parameter...
-
-```javascript
-events.on( org.bukkit.event.block.BlockBreakEvent, function( evt ) {
- evt.player.sendMessage( evt.player.name + ' broke a block!');
-} );
-```
-
[buk2]: http://wiki.bukkit.org/Event_API_Reference
[buk]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/event/Event.html
+[cmEvtApi]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/hook/Hook.html
+[cmPriority]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/plugin/Priority.html
***/
-var helper = require('events-helper');
-for ( var func in helper ) {
- exports[func] = helper[func];
-};
-
-var bkEventPriority = org.bukkit.event.EventPriority,
- bkEventExecutor = org.bukkit.plugin.EventExecutor,
- bkRegisteredListener = org.bukkit.plugin.RegisteredListener,
- bkEventPackage = 'org.bukkit.event.';
-
-exports.on = function(
- /* String or java Class */
- eventType,
- /* function( registeredListener, event) */
- handler,
- /* (optional) String (HIGH, HIGHEST, LOW, LOWEST, NORMAL, MONITOR), */
- priority ) {
- var handlerList,
- listener = {},
- eventExecutor;
-
- if ( typeof priority == 'undefined' ) {
- priority = bkEventPriority.HIGHEST;
- } else {
- priority = bkEventPriority[priority.toUpperCase()];
+var helper;
+/*global __plugin, module, require*/
+if (__plugin.canary){
+ module.exports = require('events-canary');
+ helper = require('events-helper-canary');
+ // backwards-compatibility with canarymod 1.7.9 for book listings
+ if (helper.connection && !helper.connect){
+ helper.connect = helper.connection;
}
- if ( typeof eventType == 'string' ) {
- /*
- Nashorn doesn't support bracket notation for accessing packages.
- E.g. java.net will work but java['net'] won't.
-
- https://bugs.openjdk.java.net/browse/JDK-8031715
- */
- if ( typeof Java != 'undefined' ) {
- // nashorn environment
- eventType = Java.type( bkEventPackage + eventType );
- } else {
- eventType = eval( bkEventPackage + eventType );
- }
- }
- handlerList = eventType.getHandlerList( );
-
- var result = { };
- eventExecutor = new bkEventExecutor( {
- execute: function( l, evt ) {
- handler.call( result, evt );
- }
- } );
- /*
- wph 20130222 issue #64 bad interaction with Essentials plugin
- if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
- then BOOM! the other plugin will throw an error because Rhino can't coerce an
- equals() method from an Interface.
- The workaround is to make the ScriptCraftPlugin java class a Listener.
- Should only unregister() registered plugins in ScriptCraft js code.
- */
- listener.reg = new bkRegisteredListener( __plugin, eventExecutor, priority, __plugin, true );
- handlerList.register( listener.reg );
- result.unregister = function(){
- handlerList.unregister( listener.reg );
- };
- return result;
+} else {
+ module.exports = require('events-bukkit');
+ helper = require('events-helper-bukkit');
+}
+for ( var func in helper ) {
+ module.exports[func] = helper[func];
};
diff --git a/src/main/js/lib/find.js b/src/main/js/lib/find.js
new file mode 100644
index 000000000..2f686fff5
--- /dev/null
+++ b/src/main/js/lib/find.js
@@ -0,0 +1,29 @@
+'use strict';
+var File = java.io.File;
+module.exports = function find(dir, filter) {
+ var result = [];
+ function recurse( dir, store ) {
+ var files,
+ len,
+ i,
+ file,
+ dirfile = new File( dir );
+
+ if ( typeof filter == 'undefined' ) {
+ files = dirfile.list();
+ } else {
+ files = dirfile.list(filter);
+ }
+ len = files.length; i = 0;
+ for (; i < len; i++){
+ file = new File( dir + '/' + files[i] );
+ if ( file.isDirectory() ) {
+ recurse( file.canonicalPath, store );
+ } else {
+ store.push( ('' + file.canonicalPath).replace(/\\\\/g,'/') );
+ }
+ }
+ }
+ recurse( dir, result );
+ return result;
+};
diff --git a/src/main/js/lib/java-utils.js b/src/main/js/lib/java-utils.js
index e9405e14f..aaa065ecf 100644
--- a/src/main/js/lib/java-utils.js
+++ b/src/main/js/lib/java-utils.js
@@ -3,7 +3,27 @@ exports.isJavaObject = function( o ) {
return false;
}
if (o !== undefined && o !== null){
- return o.getClass ? true : false;
+ try {
+ // this throws error for java objects in jre7
+ if (typeof o.constructor === 'function'){
+ return false;
+ }
+ } catch (e){
+ return true;
+ }
+ try {
+ var result = o.getClass ? true : false; // throws error for Enums/Class in jre7
+ if (result == true){
+ return result;
+ }
+ }catch (e2){
+ // fail silently and move on to next test
+ }
+ // java classes don't have a getClass so just because .getClass isn't present
+ // doesn't mean it's not a Java Enum or Class (.getClass only works for object instances?)
+ if (o instanceof java.lang.Object){
+ return true;
+ }
}
return o instanceof java.lang.Object;
};
diff --git a/src/main/js/lib/js-patch.js b/src/main/js/lib/js-patch.js
index ea8b7c4bd..20cd7fa1c 100644
--- a/src/main/js/lib/js-patch.js
+++ b/src/main/js/lib/js-patch.js
@@ -1,4 +1,3 @@
-
module.exports = function( $ ) {
// wph 20140105 trim not availabe in String on Mac OS.
@@ -44,29 +43,35 @@ module.exports = function( $ ) {
return bind;
}(Array.prototype.slice));
}
- $.setTimeout = function( callback, delayInMillis ) {
+
+ if (__plugin.canary){
+ require('task-canary')($);
+ } else {
+ require('task-bukkit')($);
+ }
+
+ return function unitTest( console ) {
/*
- javascript programmers familiar with setTimeout know that it expects
- a delay in milliseconds. However, bukkit's scheduler expects a delay in ticks
- (where 1 tick = 1/20th second)
+ sanity tests
*/
- var bukkitTask = server.scheduler.runTaskLater( __plugin, callback, Math.ceil( delayInMillis / 50 ) );
- return bukkitTask;
- };
+ $.setTimeout(function(){
+ console.log('js-patch setTimeout() test complete');
+ },100);
+ var clearMe = $.setTimeout(function(){
+ console.error('js-patch clearTimeout() test failed');
+ },100);
+ $.clearTimeout( clearMe );
- $.clearTimeout = function( bukkitTask ) {
- bukkitTask.cancel();
- };
-
- $.setInterval = function( callback, intervalInMillis ) {
- var delay = Math.ceil( intervalInMillis / 50);
- var bukkitTask = server.scheduler.runTaskTimer( __plugin, callback, delay, delay );
- return bukkitTask;
- };
-
- $.clearInterval = function( bukkitTask ) {
- bukkitTask.cancel();
+ var runs = 3;
+ var clearAfterRuns = $.setInterval(function(){
+ runs --;
+ if (runs == 0){
+ $.clearInterval(clearAfterRuns);
+ }
+ if (runs < 0){
+ console.error('js-patch clearInterval test failed.');
+ }
+ },100);
};
-
};
diff --git a/src/main/js/lib/legacy-check.js b/src/main/js/lib/legacy-check.js
new file mode 100644
index 000000000..21bf89c45
--- /dev/null
+++ b/src/main/js/lib/legacy-check.js
@@ -0,0 +1,32 @@
+var File = java.io.File;
+/*
+ wph 20140102 - warn if legacy 'mcserver/js-plugins' or
+ 'mcserver/plugins/scriptcraft' directories are present
+ */
+module.exports = function( jsPluginsRootDir ) {
+ var mcServerDir = new File(jsPluginsRootDir.canonicalPath).parentFile;
+ if (mcServerDir == null){
+ console.warn('Could not find parent directory for ' + jsPluginsRootDir.canonicalPath);
+ return;
+ }
+ var legacyExists = false,
+ legacyDirs = [
+ new File( mcServerDir, 'js-plugins' )
+ ];
+
+ for ( var i = 0; i < legacyDirs.length; i++ ) {
+ if ( legacyDirs[i].exists()
+ && legacyDirs[i].isDirectory() ) {
+
+ legacyExists = true;
+
+ console.warn('Legacy ScriptCraft directory %s was found. This directory is no longer used.',
+ legacyDirs[i].canonicalPath);
+ console.warn('Please put plugins in the ' + jsPluginsRootDir.canonicalPath + '/plugins directory');
+ }
+ }
+ if ( legacyExists ) {
+ console.info( 'The working directory for %s is %s',
+ __plugin, jsPluginsRootDir.canonicalPath );
+ }
+};
diff --git a/src/main/js/lib/nashorn-type.js b/src/main/js/lib/nashorn-type.js
new file mode 100644
index 000000000..ad3f73174
--- /dev/null
+++ b/src/main/js/lib/nashorn-type.js
@@ -0,0 +1,8 @@
+/*
+ The .class operator causes problems for non-nashorn Java on Mac OS X and some other
+ environments. So need to have it in a separate module which should only be loaded in
+ nashorn environment.
+*/
+module.exports = function(t){
+ return t.class;
+};
diff --git a/src/main/js/lib/persistence.js b/src/main/js/lib/persistence.js
index 1b82231d1..36eabb406 100644
--- a/src/main/js/lib/persistence.js
+++ b/src/main/js/lib/persistence.js
@@ -4,10 +4,12 @@ var _dataDir = null,
module.exports = function( rootDir, $ ) {
var _load = function( name ) {
- $.scload( _dataDir.canonicalPath + '/' + name + '-store.json' );
+ var result = $.scloadJSON( _dataDir.canonicalPath + '/' + name + '-store.json' );
+ return result;
};
- var _save = function( name, data ) {
- $.scsave( data, _dataDir.canonicalPath + '/' + name + '-store.json' );
+
+ var _save = function( name, objToSave ) {
+ $.scsave( objToSave, _dataDir.canonicalPath + '/' + name + '-store.json' );
};
_dataDir = new java.io.File( rootDir, 'data' );
@@ -23,7 +25,7 @@ module.exports = function( rootDir, $ ) {
}
if ( !write ) {
dataFromFile = _load( name );
- if ( dataFromFile ) {
+ if ( typeof dataFromFile != 'undefined') {
for ( i in dataFromFile ) {
data[i] = dataFromFile[i];
}
diff --git a/src/main/js/lib/plugin.js b/src/main/js/lib/plugin.js
index 5b7bec7b7..ac64be9a2 100644
--- a/src/main/js/lib/plugin.js
+++ b/src/main/js/lib/plugin.js
@@ -1,15 +1,15 @@
'use strict';
-
-var console = require('./console'),
- File = java.io.File,
- FileWriter = java.io.FileWriter,
- PrintWriter = java.io.PrintWriter;
+/*global persist,exports,config,__plugin,require*/
+var File = java.io.File,
+ FileWriter = java.io.FileWriter,
+ PrintWriter = java.io.PrintWriter,
+ find = require('./find');
/*
plugin management
*/
var _plugins = {};
-var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPersistent ) {
+function _plugin(/* String */ moduleName, /* Object */ moduleObject, isPersistent ) {
//
// don't load plugin more than once
//
@@ -27,69 +27,46 @@ var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPer
moduleObject.store = persist( moduleName, moduleObject.store );
}
return moduleObject;
-};
-
-exports.plugin = _plugin;
-
-exports.autoload = function( context, pluginDir, logger, options ) {
+}
- var _canonize = function( file ) {
- return '' + file.canonicalPath.replaceAll('\\\\','/');
- };
- /*
- recursively walk the given directory and return a list of all .js files
- */
- var _listSourceFiles = function( store, dir ) {
- var files = dir.listFiles(),
- file;
- if ( !files ) {
- return;
- }
- for ( var i = 0; i < files.length; i++ ) {
- file = files[i];
- if ( file.isDirectory( ) ) {
- _listSourceFiles( store, file );
- }else{
- if ( file.canonicalPath.endsWith( '.js' ) ) {
- store.push( file );
- }
- }
- }
- };
+function _autoload( context, pluginDir, options ) {
/*
Reload all of the .js files in the given directory
*/
- (function( pluginDir ) {
- var sourceFiles = [],
+ var sourceFiles = [],
property,
module,
pluginPath;
- _listSourceFiles( sourceFiles, pluginDir );
+ sourceFiles = find(pluginDir);
+
+ var len = sourceFiles.length;
+ if ( config && config.verbose ) {
+ console.info( len + ' scriptcraft plugins found in ' + pluginDir );
+ }
+
+ for ( var i = 0; i < len; i++ ) {
- var len = sourceFiles.length;
- if ( config.verbose ) {
- console.info( len + ' scriptcraft plugins found in ' + pluginDir );
+ pluginPath = sourceFiles[i];
+ if (!pluginPath.match(/\.js$/)){
+ continue;
}
- for ( var i = 0; i < len; i++ ) {
- pluginPath = _canonize( sourceFiles[i] );
- module = {};
+ module = {};
- try {
- module = require( pluginPath , options);
- for ( property in module ) {
- /*
- all exports in plugins become members of context object
- */
- context[property] = module[property];
- }
- } catch ( e ) {
- if ( typeof logger != 'undefined' ) {
- logger.severe( 'Plugin ' + pluginPath + ' ' + e );
- } else {
- java.lang.System.out.println( 'Error: Plugin ' + pluginPath + ' ' + e );
- }
+ try {
+ module = require( pluginPath , options);
+ for ( property in module ) {
+ /*
+ all exports in plugins become members of context object
+ */
+ context[property] = module[property];
}
+ } catch ( e ) {
+ var msg = 'Plugin ' + pluginPath + ' ' + e ;
+ console.error( msg );
}
- }(pluginDir));
-};
+ }
+
+}
+exports.plugin = _plugin;
+exports.autoload = _autoload;
diff --git a/src/main/js/lib/require.js b/src/main/js/lib/require.js
index 4d7398f15..c75fba2ae 100644
--- a/src/main/js/lib/require.js
+++ b/src/main/js/lib/require.js
@@ -65,8 +65,20 @@ module specification, the '.js' suffix is optional.
var File = java.io.File,
FileReader = java.io.FileReader,
BufferedReader = java.io.BufferedReader;
+
+ function fileExists( file ) {
+ if ( file.isDirectory() ) {
+ return readModuleFromDirectory( file );
+ } else {
+ return file;
+ }
+ }
+
+ function _canonize(file){
+ return "" + file.canonicalPath.replaceAll("\\\\","/");
+ }
- var readModuleFromDirectory = function( dir ) {
+ function readModuleFromDirectory( dir ) {
// look for a package.json file
var pkgJsonFile = new File( dir, './package.json' );
@@ -87,19 +99,8 @@ module specification, the '.js' suffix is optional.
return null;
}
}
- };
+ }
- var fileExists = function( file ) {
- if ( file.isDirectory() ) {
- return readModuleFromDirectory( file );
- } else {
- return file;
- }
- };
-
- var _canonize = function(file){
- return "" + file.canonicalPath.replaceAll("\\\\","/");
- };
/**********************************************************************
### module name resolution
@@ -135,7 +136,7 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
3.2 if no package.json file exists then look for an index.js file in the directory
***/
- var resolveModuleToFile = function ( moduleName, parentDir ) {
+ function resolveModuleToFile( moduleName, parentDir ) {
var file = new File(moduleName),
i = 0,
pathWithJSExt,
@@ -159,33 +160,20 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
}
}
} else {
- // it's of the form ./path
- file = new File(parentDir, moduleName);
- if ( file.exists() ) {
+ if ((file = new File(parentDir, moduleName)).exists()) {
return fileExists(file);
- } else {
- // try appending a .js to the end
- pathWithJSExt = file.canonicalPath + '.js';
- file = new File( parentDir, pathWithJSExt );
- if (file.exists()) {
- return file;
- } else {
- file = new File(pathWithJSExt);
- if ( file.exists() ) {
- return file;
- }
- }
-
+ } else if ((file = new File(parentDir, moduleName + ".js")).exists()) { // try .js extension
+ return file;
+ } else if ((file = new File(parentDir, moduleName + ".json")).exists()) { // try .json extension
+ return file;
}
}
return null;
- };
- var _loadedModules = {};
- var _format = java.lang.String.format;
+ }
/*
require() function implementation
*/
- var _require = function( parentFile, path, options ) {
+ function _require( parentFile, path, options ) {
var file,
canonizedFilename,
moduleInfo,
@@ -209,7 +197,30 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
if (! ( (''+path).match( /^\./ ) ) ) {
errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths);
}
- throw errMsg;
+ var find = _require(parentFile, 'find').exports;
+ var allJS = [];
+ for (var i = 0;i < modulePaths.length; i++){
+ var js = find( modulePaths[i] );
+ for (var j = 0;j < js.length; j++){
+ if (js[j].match(/\.js$/)){
+ allJS.push( js[j].replace(modulePaths[i],'') );
+ }
+ }
+ }
+ var pathL = path.toLowerCase();
+ var candidates = [];
+ for (i = 0;i < allJS.length;i++){
+ var filenameparts = allJS[i];
+ var candidate = filenameparts.replace(/\.js/,'') ;
+ var lastpart = candidate.toLowerCase();
+ if (pathL.indexOf(lastpart) > -1 || lastpart.indexOf(pathL) > -1){
+ candidates.push(candidate);
+ }
+ }
+ if (candidates.length > 0){
+ errMsg += '\nBut found module/s named: ' + candidates.join(',') + ' - is this what you meant?';
+ }
+ throw new Error(errMsg);
}
canonizedFilename = _canonize(file);
@@ -228,6 +239,9 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
}
buffered.close(); // close the stream so there's no file locks
+ if(canonizedFilename.toLowerCase().substring(canonizedFilename.length - 5) === ".json") // patch code when it is json
+ code = "module.exports = (" + code + ");";
+
moduleInfo = {
loaded: false,
id: canonizedFilename,
@@ -249,8 +263,8 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
This can be fixed by instead using __engine.eval
*/
throw new Error( "Error evaluating module " + path
- + " line #" + e.lineNumber
- + " : " + e.message, canonizedFilename, e.lineNumber );
+ + " line #" + e.lineNumber
+ + " : " + e.message, canonizedFilename, e.lineNumber );
}
var __dirname = '' + file.parentFile.canonicalPath;
var parameters = [
@@ -265,23 +279,43 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
.apply(moduleInfo.exports, /* this */
parameters);
} catch (e) {
+ var snippet = '';
+ if ((''+e.lineNumber).match(/[0-9]/)){
+ var lines = code.split(/\n/);
+ if (e.lineNumber > 1){
+ snippet = ' ' + lines[e.lineNumber-2] + '\n';
+ }
+ snippet += '> ' + lines[e.lineNumber-1] + '\n';
+ if (e.lineNumber < lines.length){
+ snippet += ' ' + lines[e.lineNumber] + '\n';
+ }
+ }
throw new Error( "Error executing module " + path
+ " line #" + e.lineNumber
- + " : " + e.message, canonizedFilename, e.lineNumber );
+ + " : " + e.message + (snippet?('\n' + snippet):''), canonizedFilename, e.lineNumber );
}
if ( hooks ) {
hooks.loaded( canonizedFilename );
}
moduleInfo.loaded = true;
return moduleInfo;
- };
+ }
- var _requireClosure = function( parent ) {
- return function( path, options ) {
- var module = _require( parent, path , options);
+ function _requireClosure( parentFile ) {
+ var _boundRequire = function requireBoundToParent( path, options ) {
+ var module = _require( parentFile, path , options);
return module.exports;
};
- };
+
+ _boundRequire.resolve = function resolveBoundToParent ( path ) {
+ return resolveModuleToFile(path, parentFile);
+ };
+ _boundRequire.cache = _loadedModules;
+
+ return _boundRequire;
+ }
+ var _loadedModules = {};
+ var _format = java.lang.String.format;
return _requireClosure( new java.io.File(rootDir) );
// last line deliberately has no semicolon!
})
diff --git a/src/main/js/lib/scriptcraft.js b/src/main/js/lib/scriptcraft.js
index bdb9848ad..369f7c09c 100644
--- a/src/main/js/lib/scriptcraft.js
+++ b/src/main/js/lib/scriptcraft.js
@@ -1,4 +1,5 @@
'use strict';
+/*global require*/
/************************************************************************
## Modules in Scriptcraft
@@ -46,14 +47,13 @@ module.exports instead of exports.
## Module Loading
When the ScriptCraft Java plugin is first installed, a new
-subdirectory is created in the craftbukkit/plugins directory. If your
-craftbukkit directory is called 'craftbukkit' then the new
-subdirectories will be ...
+`scriptcraft` subdirectory is created. If your minecraft server
+directory is called 'mcserver' then the new subdirectories will be ...
- * craftbukkit/plugins/scriptcraft/
- * craftbukkit/plugins/scriptcraft/plugins
- * craftbukkit/plugins/scriptcraft/modules
- * craftbukkit/plugins/scriptcraft/lib
+ * mcserver/scriptcraft/
+ * mcserver/scriptcraft/plugins
+ * mcserver/scriptcraft/modules
+ * mcserver/scriptcraft/lib
... The `plugins`, `modules` and `lib` directories each serve a different purpose.
@@ -70,7 +70,7 @@ directory....
```javascript
exports.greet = function(player) {
- player.sendMessage('Hello ' + player.name);
+ echo(player, 'Hello ' + player.name);
};
```
@@ -123,7 +123,7 @@ As of December 24 2013, the `scriptcraft/plugins` directory has the following su
* arrows - The arrows module - Changes the behaviour of Arrows: Explosive, Fireworks, Teleportation etc.
* signs - The signs module (includes example signs) - create interactive signs.
* chat - The chat plugin/module
- * alias - The alias plugin/module - for creating custom aliases for commonly used commands.
+ * alias - The alias plugin/module - for creating custom aliases for commonly-used commands.
* home - The home module - for setting homes and visiting other homes.
## Global variables
@@ -137,10 +137,7 @@ The ScriptCraft JavaPlugin object.
The Minecraft Server object
### self variable
-The current player. (Note - this value should not be used in
-multi-threaded scripts or event-handling code - it's not
-thread-safe). This variable is only safe to use at the in-game prompt
-and should *never* be used in modules. For example you can use it here...
+The current player. (Note - this value should not be used in multi-threaded scripts or event-handling code - it's not thread-safe). This variable is only safe to use at the in-game prompt and should *never* be used in modules. For example you can use it here...
/js console.log(self.name)
@@ -169,33 +166,19 @@ ScripCraft provides some global functions which can be used by all plugins/modul
### echo function
-The `echo()` function displays a message on the in-game screen. The
-message is displayed to the `self` player (this is usually the player
-who issued the `/js` or `/jsp` command).
+The `echo()` function displays a message on the in-game screen.
#### Example
- /js echo('Hello World')
+ /js echo( self, 'Hello World')
For programmers familiar with Javascript web programming, an `alert`
function is also provided. `alert` works exactly the same as `echo`
-e.g. `alert('Hello World')`.
-
-#### Notes
-
-The `echo` and `alert` functions are provided as convenience functions
-for beginning programmers. The use of these 2 functions is not
-recommended in event-handling code or multi-threaded code. In such
-cases, if you want to send a message to a given player then use the
-Bukkit API's [Player.sendMessage()][plsm] function instead.
-
-[plsm]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/CommandSender.html#sendMessage(java.lang.String)
+e.g. `alert( self, 'Hello World')`.
### require() function
-ScriptCraft's `require()` function is used to load modules. The
-`require()` function takes a module name as a parameter and will try
-to load the named module.
+ScriptCraft's `require()` function is used to load modules. The `require()` function takes a module name as a parameter and will try to load the named module.
#### Parameters
@@ -247,11 +230,12 @@ scload() will return the result of the last statement evaluated in the file.
The scsave() function saves an in-memory javascript object to a
specified file. Under the hood, scsave() uses JSON (specifically
-json2.js) to save the object. Again, there will usually be no need to
-call this function directly as all javascript plugins' state are saved
-automatically if they are declared using the `plugin()` function. Any
-in-memory object saved using the `scsave()` function can later be
-restored using the `scload()` function.
+json2.js) to save the object. There will usually be no need to call
+this function directly - If you want to have a javascript object
+automatically loaded at startup and saved on shutdown then use the
+`persist()` module. The `persist()` module uses scsave and scload
+under the hood. Any in-memory object saved using the `scsave()`
+function can later be restored using the `scload()` function.
#### Parameters
@@ -276,6 +260,12 @@ scsave(myObject, 'johndoe.json');
### plugin() function
+#### Update April 2015
+The `plugin()` function is deprecated. Please refer to [Anatomy of a
+ScriptCraft Plugin][anatomy] for an up-to-date step-by-step guide to
+creating a plugin which uses persistence (loading and saving data).
+
+#### Deprecated
The `plugin()` function should be used to declare a javascript module
whose state you want to have managed by ScriptCraft - that is - a
Module whose state will be loaded at start up and saved at shut down.
@@ -303,53 +293,49 @@ ScriptCraft Plugin][anatomy].
### command() function
-The `command()` function is used to expose javascript functions for
-use by non-operators (regular players). Only operators should be
-allowed use raw javascript using the `/js ` command because it is too
-powerful for use by regular players and can be easily abused. However,
-the `/jsp ` command lets you (the operator / server administrator /
-plugin author) safely expose javascript functions for use by players.
+The `command()` function is used to expose javascript functions for use by non-operators (regular players). Only operators should be allowed use raw javascript using the `/js ` command because it is too powerful for use by regular players and can be easily abused. However, the `/jsp ` command lets you (the operator / server administrator / plugin author) safely expose javascript functions for use by players.
#### Parameters
- * commandName : The name to give your command - the command will
- be invoked like this by players `/jsp commandName`
- * commandFunction: The javascript function which will be invoked when
- the command is invoked by a player. The callback function in turn
- takes 2 parameters...
-
- * params : An Array of type String - the list of parameters
- passed to the command.
- * sender : The [CommandSender][bukcs] object that invoked the
- command (this is usually a Player object but can be a Block
- ([BlockCommandSender][bukbcs]).
-
- * options (Array - optional) : An array of command options/parameters
- which the player can supply (It's useful to supply an array so that
- Tab-Completion works for the `/jsp ` commands.
- * intercepts (boolean - optional) : Indicates whether this command
- can intercept Tab-Completion of the `/jsp ` command - advanced
- usage - see alias/alias.js for example.
+ * commandFunction: The named javascript function which will be invoked when the command is invoked by a player. The name of the function will be used as the command name so name this function accordingly. The callback function in turn takes 2 parameters...
+
+ * params : An Array of type String - the list of parameters passed to the command.
+ * sender : The [CommandSender][bukcs] object that invoked the command (this is usually a Player object but can be a Block ([BlockCommandSender][bukbcs]).
+
+ * options (Array|Function - optional) : An array of command options/parameters which the player can supply (It's useful to supply an array so that Tab-Completion works for the `/jsp ` commands. If a function is supplied instead of an array then the function will be invoked at TAB-completion time and should return an array of strings.
+ * intercepts (boolean - optional) : Indicates whether this command can intercept Tab-Completion of the `/jsp ` command - advanced usage - see alias/alias.js for example.
#### Example
-See chat/colors.js or alias/alias.js or homes/homes.js for examples of
-how to use the `command()` function.
+ // javascript code
+ function boo( params, sender) {
+ echo( sender, params[0] );
+ }
+ command( boo );
+
+ # in-game execution
+ /jsp boo Hi!
+ > Hi!
+
+To use a callback for options (TAB-Completion) ...
+
+ var utils = require('utils');
+ function boo( params, sender ) {
+ var receiver = server.getPlayer( params[0] );
+ if ( receiver ){
+ echo( receiver, sender.name + ' says boo!');
+ }
+ }
+ command( boo, utils.playerNames );
+
+See chat/colors.js or alias/alias.js or homes/homes.js for more examples of how to use the `command()` function.
### setTimeout() function
-This function mimics the setTimeout() function used in browser-based
-javascript. However, the function will only accept a function
-reference, not a string of javascript code. Where setTimeout() in the
-browser returns a numeric value which can be subsequently passed to
-clearTimeout(), This implementation returns a [BukkitTask][btdoc]
-object which can be subsequently passed to ScriptCraft's own
-clearTimeout() implementation.
+This function mimics the setTimeout() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setTimeout() in the browser returns a numeric value which can be subsequently passed to clearTimeout(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearTimeout() implementation.
If Node.js supports setTimeout() then it's probably good for ScriptCraft to support it too.
-[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
-
#### Example
```javascript
@@ -368,18 +354,7 @@ A scriptcraft implementation of clearTimeout().
### setInterval() function
-This function mimics the setInterval() function used in browser-based
-javascript. However, the function will only accept a function
-reference, not a string of javascript code. Where setInterval() in
-the browser returns a numeric value which can be subsequently passed
-to clearInterval(), This implementation returns a [BukkitTask][btdoc]
-object which can be subsequently passed to ScriptCraft's own
-clearInterval() implementation.
-
-If Node.js supports setInterval() then it's probably good for
-ScriptCraft to support it too.
-
-[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
+This function mimics the setInterval() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setInterval() in the browser returns a numeric value which can be subsequently passed to clearInterval(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearInterval() implementation.
### clearInterval() function
@@ -387,29 +362,33 @@ A scriptcraft implementation of clearInterval().
### refresh() function
-The refresh() function can be used to only reload the ScriptCraft
-plugin (it's like the `reload` command except it only reloads
-ScriptCraft). The refresh() function will ...
+The refresh() function can be used to only reload the ScriptCraft plugin (it's like the `reload` command except it only reloads ScriptCraft). The refresh() function will ...
1. Disable the ScriptCraft plugin.
2. Unload all event listeners associated with the ScriptCraft plugin.
+3. Cancel all timed tasks (created by `setInterval` & `setTimeout`)
3. Enable the ScriptCraft plugin.
... refresh() can be used during development to reload only scriptcraft javascript files.
See [issue #69][issue69] for more information.
+By default, if `self` is defined at runtime, it checks, whether `self` is server operator, otherwise fails with message. This behavivor can be modified using `skipOpCheck` parameter (useful, if you are doing some custom premission checks before calling this function).
+
+#### Parameters
+
+ * skipOpCheck (boolean - optional) : If true, the function won't check if `self` is server operator.
+
[issue69]: https://github.com/walterhiggins/ScriptCraft/issues/69
### addUnloadHandler() function
-The addUnloadHandler() function takes a callback function as a
-parameter. The callback will be called when the ScriptCraft plugin is
-unloaded (usually as a result of a a `reload` command or server
-shutdown).
+The addUnloadHandler() function takes a callback function as a parameter. The callback will be called when the ScriptCraft plugin is unloaded (usually as a result of a a `reload` command or server shutdown).
-This function provides a way for ScriptCraft modules to do any
-required cleanup/housekeeping just prior to the ScriptCraft Plugin
-unloading.
+This function provides a way for ScriptCraft modules to do any required cleanup/housekeeping just prior to the ScriptCraft Plugin unloading.
+
+### isOp() function
+
+This function takes a single parameter and returns true if it's an operator or has operator-level privileges.
***/
@@ -417,59 +396,95 @@ unloading.
wph 20130124 - make self, plugin and server public - these are far more useful now that tab-complete works.
*/
var global = this;
-var server = org.bukkit.Bukkit.server;
+var server;
+global.nashorn = typeof Java !== 'undefined';
/*
private implementation
*/
-function __onEnable ( __engine, __plugin, __script )
-{
- var File = java.io.File,
- FileReader = java.io.FileReader,
- BufferedReader = java.io.BufferedReader,
- PrintWriter = java.io.PrintWriter,
- FileWriter = java.io.FileWriter;
- var debug = function(msg){
- java.lang.System.out.println('DEBUG:' + msg);
- };
- var _canonize = function( file ) {
+var __onDisableImpl;
+function __onDisable ( __engine, __plugin ) {
+ __onDisableImpl( __engine, __plugin);
+}
+function __onEnable ( __engine, __plugin, __script ) {
+ function _echo( ) {
+ var sender, msg;
+ if (arguments.length == 2){
+ sender = arguments[0];
+ msg = arguments[1];
+ } else {
+ if ( typeof self == 'undefined' ) {
+ return;
+ }
+ sender = self;
+ msg = arguments[0];
+ }
+ if (__plugin.canary){
+ sender.message( msg );
+ } else {
+ sender.sendMessage( msg );
+ }
+ } // end echo()
+ function _canonize( file ) {
return '' + file.getCanonicalPath().replaceAll( '\\\\', '/' );
- };
- // lib (assumes scriptcraft.js is in craftbukkit/plugins/scriptcraft/lib directory
- var libDir = __script.parentFile,
- jsPluginsRootDir = libDir.parentFile, // scriptcraft
- jsPluginsRootDirName = _canonize(jsPluginsRootDir),
- logger = __plugin.logger;
-
+ }
/*
Save a javascript object to a file (saves using JSON notation)
*/
- var _save = function( object, filename ) {
+ function _save( objToSave, filename ) {
var objectToStr = null,
f,
out;
try {
- objectToStr = JSON.stringify( object, null, 2 );
+ objectToStr = JSON.stringify( objToSave, null, 2 );
+
} catch( e ) {
- print( 'ERROR: ' + e.getMessage() + ' while saving ' + filename );
+ console.error( 'ERROR: ' + e.getMessage() + ' while saving ' + filename );
return;
}
f = (filename instanceof File) ? filename : new File(filename);
out = new PrintWriter(new FileWriter(f));
out.println( objectToStr );
out.close();
- };
- /*
- make sure eval is present: it's present on JRE 6, 7, and 8 on Linux
- */
- if ( typeof eval == 'undefined' ) {
- global.eval = function( str ) {
- return __engine.eval( str );
- };
- }
+ }
+ function _loadJSON( filename ){
+ var result = null,
+ file = filename,
+ r,
+ reader,
+ br,
+ contents;
+
+ if ( !( filename instanceof File ) ) {
+ file = new File(filename);
+ }
+ var canonizedFilename = _canonize( file );
+
+ if ( file.exists() ) {
+ reader = new FileReader( file );
+ br = new BufferedReader( reader );
+ contents = '';
+ try {
+ while ( (r = br.readLine()) !== null ) {
+ contents += r + '\n';
+ }
+ result = JSON.parse(contents);
+ } catch ( e ) {
+ logError('Error evaluating ' + canonizedFilename + ', ' + e );
+ }
+ finally {
+ try {
+ reader.close();
+ } catch ( re ) {
+ // fail silently on reader close error
+ }
+ }
+ }
+ return result;
+ }
/*
Load the contents of the file and evaluate as javascript
*/
- var _load = function( filename, warnOnFileNotFound )
+ function _load( filename, warnOnFileNotFound )
{
var result = null,
file = filename,
@@ -497,7 +512,7 @@ function __onEnable ( __engine, __plugin, __script )
result = __engine.eval( wrappedCode );
// issue #103 avoid side-effects of || operator on Mac Rhino
} catch ( e ) {
- logger.severe( 'Error evaluating ' + canonizedFilename + ', ' + e );
+ logError('Error evaluating ' + canonizedFilename + ', ' + e );
}
finally {
try {
@@ -508,11 +523,174 @@ function __onEnable ( __engine, __plugin, __script )
}
} else {
if ( warnOnFileNotFound ) {
- logger.warning( canonizedFilename + ' not found' );
+ logWarn(canonizedFilename + ' not found' );
}
}
return result;
- };
+ } // end _load()
+
+ function _isOp( sender ){
+ if (__plugin.canary){
+ return sender.receiverType.name() == 'SERVER' || Canary.ops().isOpped(sender);
+ } else {
+ return sender.op;
+ }
+ }
+ function _refresh( skipOpCheck ) {
+ if (!skipOpCheck && typeof self !== 'undefined') {
+ if (!_isOp(self))
+ return echo(self, 'Only operators can refresh()');
+ }
+
+ if (__plugin.canary){
+ var pluginName = __plugin.name;
+ Canary.manager().disablePlugin( pluginName );
+ Canary.manager().enablePlugin( pluginName );
+ } else {
+ __plugin.pluginLoader.disablePlugin( __plugin );
+ org.bukkit.event.HandlerList["unregisterAll(org.bukkit.plugin.Plugin)"]( __plugin );
+ server.scheduler.cancelTasks( __plugin );
+ __plugin.pluginLoader.enablePlugin( __plugin );
+ }
+ } // end _refresh()
+ function _onDisable( evt ) {
+ // save config
+ _save( global.config, new File( jsPluginsRootDir, 'data/global-config.json' ) );
+ _runUnloadHandlers();
+ }
+ function _addUnloadHandler( f ) {
+ unloadHandlers.push( f );
+ }
+ function _runUnloadHandlers() {
+ for ( var i = 0; i < unloadHandlers.length; i++ ) {
+ unloadHandlers[i]( );
+ }
+ }
+ function __onCommand() {
+ var jsArgs = [],
+ i = 0,
+ jsResult,
+ result,
+ cmdName,
+ sender,
+ args,
+ cmd,
+ label,
+ fnBody;
+
+ if ( __plugin.canary ) {
+ sender = arguments[0];
+ args = arguments[1];
+ cmdName = (''+args[0]).toLowerCase().replace(/^\//,'');
+ for ( i = 1; i < args.length ; i++ ) {
+ jsArgs.push( '' + args[i] );
+ }
+ } else {
+ sender = arguments[0];
+ cmd = arguments[1];
+ label = arguments[2];
+ args = arguments[3];
+ cmdName = ( '' + cmd.name ).toLowerCase();
+ for ( ; i < args.length ; i++ ) {
+ jsArgs.push( '' + args[i] );
+ }
+ }
+ result = false;
+
+ if (cmdName == 'js')
+ {
+ result = true;
+ fnBody = jsArgs.join(' ');
+ global.self = sender;
+ global.__engine = __engine;
+ try {
+ // cannot rely on native eval in jre7 and jre8
+ // because ...
+ // js var hearts
+ // js hearts
+ // ... throws an execption ('hearts' is not defined). vars are not sticky in native eval .
+ //
+ jsResult = __engine.eval( fnBody );
+
+ if ( typeof jsResult != 'undefined' ) {
+ if ( jsResult == null) {
+ // engine eval will return null even if the result should be undefined
+ // this can be confusing so I think it's better to omit output for this case
+ // sender.sendMessage('(null)');
+ } else {
+ try {
+ if ( isJavaObject(jsResult) || typeof jsResult === 'function') {
+ echo(sender, jsResult);
+ } else {
+ var replacer = function replacer(key, value){
+ return this[key] instanceof java.lang.Object ? '' + this[key] : value;
+ };
+ echo(sender, JSON.stringify( jsResult, replacer, 2) );
+ }
+ } catch ( displayError ) {
+ logError('Error while trying to display result: ' + jsResult + ', Error: '+ displayError) ;
+ }
+ }
+ }
+ } catch ( e ) {
+ logError( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
+ echo( sender, 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
+ throw e;
+ } finally {
+ /*
+ wph 20140312 don't delete self on nashorn until https://bugs.openjdk.java.net/browse/JDK-8034055 is fixed
+ */
+ if ( !nashorn ) {
+ delete global.self;
+ delete global.__engine;
+ }
+ }
+ }
+ if ( cmdName == 'jsp' ) {
+ cmdModule.exec( jsArgs, sender );
+ result = true;
+ }
+ return result;
+ } // end __onCommand() function
+
+ var Bukkit = null;
+ var Canary = null;
+ var logger = null;
+
+ if (__plugin.canary){
+ Canary = Packages.net.canarymod.Canary;
+ server = Canary.server;
+ logger = __plugin.logman;
+ } else {
+ Bukkit = Packages.org.bukkit.Bukkit;
+ server = Bukkit.server;
+ logger = __plugin.logger;
+ }
+ function logError(msg){
+ __plugin.canary ? logger.error( msg ) : logger.severe( msg );
+ }
+ function logWarn(msg){
+ __plugin.canary ? logger.warn( msg ) : logger.warning( msg );
+ }
+ var File = java.io.File,
+ FileReader = java.io.FileReader,
+ BufferedReader = java.io.BufferedReader,
+ PrintWriter = java.io.PrintWriter,
+ FileWriter = java.io.FileWriter,
+ // assumes scriptcraft.js is in mcserver/plugins/scriptcraft/lib directory
+ jsPluginsRootDir = __script.parentFile.parentFile,
+ jsPluginsRootDirName = _canonize(jsPluginsRootDir),
+ unloadHandlers = [];
+
+ /*
+ make sure eval is present: it's present on JRE 6, 7, and 8 on Linux
+ */
+ if ( typeof eval == 'undefined' ) {
+ global.eval = function( str ) {
+ return __engine.eval( str );
+ };
+ }
+
/*
now that load is defined, use it to load a global config object
*/
@@ -533,44 +711,14 @@ function __onEnable ( __engine, __plugin, __script )
var jsonLoaded = __engine['eval(java.io.Reader)']( jsonFileReader );
}());
- /*
- Unload Handlers
- */
- var unloadHandlers = [];
- var _addUnloadHandler = function( f ) {
- unloadHandlers.push( f );
- };
- var _runUnloadHandlers = function() {
- for ( var i = 0; i < unloadHandlers.length; i++ ) {
- unloadHandlers[i]( );
- }
- };
global.addUnloadHandler = _addUnloadHandler;
-
-
- global.refresh = function( ) {
- if ( typeof self !== 'undefined' ) {
- if ( !self.op ) {
- self.sendMessage('Only operators can refresh()');
- return;
- }
- }
- __plugin.pluginLoader.disablePlugin( __plugin );
- __plugin.pluginLoader.enablePlugin( __plugin );
- };
-
- var _echo = function ( msg ) {
- if ( typeof self == 'undefined' ) {
- return;
- }
- self.sendMessage( msg );
- };
-
+ global.refresh = _refresh;
global.echo = _echo;
global.alert = _echo;
global.scload = _load;
global.scsave = _save;
-
+ global.scloadJSON = _loadJSON;
+ global.isOp = _isOp;
var configRequire = _load( jsPluginsRootDirName + '/lib/require.js', true );
/*
setup paths to search for modules
@@ -603,8 +751,11 @@ function __onEnable ( __engine, __plugin, __script )
}
);
- require('js-patch')( global );
- global.console = require('console');
+ var testJSPatch = require('js-patch')( global );
+ var console = require('console')(logger);
+ global.console = console;
+ testJSPatch(console);
+
/*
setup persistence
*/
@@ -622,109 +773,13 @@ function __onEnable ( __engine, __plugin, __script )
// wph 20131226 - make events global as it is used by many plugins/modules
global.events = events;
- events.pluginDisable(function( evt ) {
- // save config
- _save( global.config, new File( jsPluginsRootDir, 'data/global-config.json' ) );
-
- _runUnloadHandlers();
- org.bukkit.event.HandlerList['unregisterAll(org.bukkit.plugin.Plugin)'](__plugin);
- });
-
-
- global.__onCommand = function( sender, cmd, label, args) {
- var jsArgs = [],
- i = 0,
- jsResult,
- result,
- cmdName,
- fnBody;
- for ( ; i < args.length ; i++ ) {
- jsArgs.push( '' + args[i] );
- }
-
- result = false;
- cmdName = ( '' + cmd.name ).toLowerCase();
- if (cmdName == 'js')
- {
- result = true;
- fnBody = jsArgs.join(' ');
- global.self = sender;
- global.__engine = __engine;
- try {
- // cannot rely on native eval in jre7 and jre8
- // because ...
- // js var hearts
- // js hearts
- // ... throws an execption ('hearts' is not defined). vars are not sticky in native eval .
- //
- jsResult = __engine.eval( fnBody );
-
- if ( typeof jsResult != 'undefined' ) {
- if ( jsResult == null) {
- // engine eval will return null even if the result should be undefined
- // this can be confusing so I think it's better to omit output for this case
- // sender.sendMessage('(null)');
- } else {
- try {
- if ( isJavaObject(jsResult) || typeof jsResult === 'function') {
- sender.sendMessage(jsResult);
- } else {
- var replacer = function replacer(key, value){
- return this[key] instanceof java.lang.Object ? '' + this[key] : value;
- };
- sender.sendMessage( JSON.stringify( jsResult, replacer, 2) );
- }
- } catch ( displayError ) {
- logger.severe( 'Error while trying to display result: ' + jsResult + ', Error: '+ displayError );
- }
- }
- }
- } catch ( e ) {
- logger.severe( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
- sender.sendMessage( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
- throw e;
- } finally {
- /*
- wph 20140312 don't delete self on nashorn until https://bugs.openjdk.java.net/browse/JDK-8034055 is fixed
- */
- if ( typeof Java === 'undefined' ) { // Java is an object in Nashorn
- delete global.self;
- delete global.__engine;
- }
- }
- }
- if ( cmdName == 'jsp' ) {
- cmdModule.exec( jsArgs, sender );
- result = true;
- }
- return result;
- };
-
- plugins.autoload( global, new File(jsPluginsRootDir,'plugins'), logger );
- /*
- wph 20140102 - warn if legacy 'craftbukkit/js-plugins' or 'craftbukkit/scriptcraft' directories are present
- */
- (function(){
- var cbPluginsDir = jsPluginsRootDir.parentFile,
- cbDir = new File(cbPluginsDir.canonicalPath).parentFile,
- legacyExists = false,
- legacyDirs = [new File( cbDir, 'js-plugins' ),
- new File( cbDir, 'scriptcraft' )];
-
- for ( var i = 0; i < legacyDirs.length; i++ ) {
- if ( legacyDirs[i].exists()
- && legacyDirs[i].isDirectory() ) {
-
- legacyExists = true;
-
- console.warn('Legacy ScriptCraft directory %s was found. This directory is no longer used.',
- legacyDirs[i].canonicalPath);
- }
- }
- if ( legacyExists ) {
- console.info( 'Please note that the working directory for %s is %s',
- __plugin, jsPluginsRootDir.canonicalPath );
- }
- })();
-
+ if (__plugin.canary) {
+ // canary plugin doesn't get to handle its own plugin disable event
+ } else {
+ events.pluginDisable(_onDisable);
+ }
+ __onDisableImpl = _onDisable;
+ global.__onCommand = __onCommand;
+ plugins.autoload( global, new File(jsPluginsRootDir,'plugins') );
+ require('legacy-check')(jsPluginsRootDir);
}
diff --git a/src/main/js/lib/tabcomplete-jsp.js b/src/main/js/lib/tabcomplete-jsp.js
index 3ed61cb3a..1928b3927 100644
--- a/src/main/js/lib/tabcomplete-jsp.js
+++ b/src/main/js/lib/tabcomplete-jsp.js
@@ -3,7 +3,7 @@ var _commands = require('command').commands;
/*
Tab completion for the /jsp commmand
*/
-var __onTabCompleteJSP = function( result, cmdSender, pluginCmd, cmdAlias, cmdArgs ) {
+var __onTabCompleteJSP = function( result, cmdArgs ) {
var cmdInput = cmdArgs[0],
opts,
cmd,
@@ -11,13 +11,13 @@ var __onTabCompleteJSP = function( result, cmdSender, pluginCmd, cmdAlias, cmdAr
i;
cmd = _commands[cmdInput];
if ( cmd ) {
- opts = cmd.options;
- len = opts.length;
- if ( cmdArgs.length == 1 ) {
- for ( i = 0; i < len; i++ ) {
- result.add( opts[i] );
- }
+ if (typeof cmd.options === 'function'){
+ opts = cmd.options();
} else {
+ opts = cmd.options;
+ }
+ len = opts.length;
+ if ( cmdArgs.length > 1 ) {
// partial e.g. /jsp chat_color dar
for ( i = 0; i < len; i++ ) {
if ( opts[i].indexOf( cmdArgs[1] ) == 0 ) {
diff --git a/src/main/js/lib/tabcomplete.js b/src/main/js/lib/tabcomplete.js
index 514e7bafa..fd7a00c02 100644
--- a/src/main/js/lib/tabcomplete.js
+++ b/src/main/js/lib/tabcomplete.js
@@ -72,8 +72,18 @@ var _getProperties = function( o ) {
}
for ( i in o ) {
if ( i.match( /^[^_]/ ) ) {
- if ( typeof o[i] == 'function' ) {
- result.push( i+'()' );
+ if ( typeof o[i] == 'function'){
+ if ( ! (o[i] instanceof java.lang.Object) ) {
+ try {
+ if (o[i].constructor){} // throws error for java objects in jre7
+ result.push(i + '()');
+ } catch (e ){
+ result.push(i);
+ }
+
+ }else {
+ result.push( i );
+ }
} else {
result.push( i );
}
@@ -83,7 +93,7 @@ var _getProperties = function( o ) {
return result.sort();
};
-var onTabCompleteJS = function( result, cmdSender, pluginCmd, cmdAlias, cmdArgs ) {
+var onTabCompleteJS = function( ) {
var _globalSymbols,
lastArg,
@@ -101,12 +111,33 @@ var onTabCompleteJS = function( result, cmdSender, pluginCmd, cmdAlias, cmdArgs
candidate,
re,
li,
- possibleCompletion;
+ possibleCompletion,
+ result,
+ cmdSender,
+ pluginCmd,
+ cmdArgs;
+ result = arguments[0];
+ cmdSender = arguments[1];
+ if (__plugin.bukkit){
+ pluginCmd = arguments[2].name;
+ cmdArgs = arguments[4];
+ }
+ if (__plugin.canary){
+ cmdArgs = arguments[2];
+ pluginCmd = arguments[3];
+ }
cmdArgs = Array.prototype.slice.call( cmdArgs, 0 );
- if ( pluginCmd.name == 'jsp' ) {
- return tabCompleteJSP( result, cmdSender, pluginCmd, cmdAlias, cmdArgs );
+ if (__plugin.canary){
+ // if 1st element is 'js' then splice
+ // there's probably a better way to do this
+ if (cmdArgs[0] == 'js'){
+ cmdArgs = cmdArgs.slice(1);
+ }
+ }
+ if ( pluginCmd == 'jsp' ) {
+ return tabCompleteJSP( result, cmdArgs );
}
global.self = cmdSender; // bring in self just for autocomplete
@@ -146,7 +177,13 @@ var onTabCompleteJS = function( result, cmdSender, pluginCmd, cmdAlias, cmdArgs
if ( !name ) { // fix issue #115
break;
}
- symbol = symbol[name]; // this causes problem in jre8 if name is ''
+ try {
+ // this causes problems in jre if symbol is an enum and name is partial-match
+ symbol = symbol[name]; // this causes problem in jre8 if name is ''
+ } catch (e){
+ symbol = null;
+ break;
+ }
if ( typeof symbol == 'undefined' ) {
break;
}
diff --git a/src/main/js/lib/task-bukkit.js b/src/main/js/lib/task-bukkit.js
new file mode 100644
index 000000000..959589df9
--- /dev/null
+++ b/src/main/js/lib/task-bukkit.js
@@ -0,0 +1,24 @@
+'use strict';
+/*global __plugin, module, server*/
+function bukkitSetTimeout( callback, delayInMillis ){
+ var delay = Math.ceil( delayInMillis / 50 );
+ var task = server.scheduler.runTaskLater( __plugin, callback, delay );
+ return task;
+}
+function bukkitClearTimeout( task ) {
+ task.cancel();
+}
+function bukkitSetInterval( callback, intervalInMillis ) {
+ var delay = Math.ceil( intervalInMillis / 50);
+ var task = server.scheduler.runTaskTimer( __plugin, callback, delay, delay );
+ return task;
+}
+function bukkitClearInterval( bukkitTask ) {
+ bukkitTask.cancel();
+}
+module.exports = function($){
+ $.setTimeout = bukkitSetTimeout;
+ $.clearTimeout = bukkitClearTimeout;
+ $.setInterval = bukkitSetInterval;
+ $.clearInterval = bukkitClearInterval;
+};
diff --git a/src/main/js/lib/task-canary.js b/src/main/js/lib/task-canary.js
new file mode 100644
index 000000000..cbe268250
--- /dev/null
+++ b/src/main/js/lib/task-canary.js
@@ -0,0 +1,35 @@
+'use strict';
+/*global Packages, __plugin, module*/
+/*
+ javascript programmers familiar with setTimeout know that it expects
+ a delay in milliseconds. However, bukkit's scheduler expects a delay in ticks
+ (where 1 tick = 1/20th second)
+ */
+function canarySetTimeout( callback, delayInMillis ){
+ var cmTaskManager = Packages.net.canarymod.tasks.ServerTaskManager;
+ var delay = Math.ceil( delayInMillis / 50 );
+ var task = __plugin.createServerTask(callback, delay, false);
+ cmTaskManager.addTask(task);
+ return task;
+}
+function canaryClearTimeout( task ){
+ var cmTaskManager = Packages.net.canarymod.tasks.ServerTaskManager;
+ cmTaskManager.removeTask( task );
+}
+function canarySetInterval( callback, intervalInMillis ) {
+ var cmTaskManager = Packages.net.canarymod.tasks.ServerTaskManager;
+ var delay = Math.ceil( intervalInMillis / 50 );
+ var task = __plugin.createServerTask(callback, delay, true);
+ cmTaskManager.addTask(task);
+ return task;
+}
+function canaryClearInterval( task ){
+ var cmTaskManager = Packages.net.canarymod.tasks.ServerTaskManager;
+ cmTaskManager.removeTask( task );
+}
+module.exports = function($){
+ $.setTimeout = canarySetTimeout;
+ $.clearTimeout = canaryClearTimeout;
+ $.setInterval = canarySetInterval;
+ $.clearInterval = canaryClearInterval;
+};
diff --git a/src/main/js/modules/at.js b/src/main/js/modules/at.js
new file mode 100644
index 000000000..03d33845d
--- /dev/null
+++ b/src/main/js/modules/at.js
@@ -0,0 +1,155 @@
+'use strict';
+/*global events, module, require, __plugin, setInterval, clearInterval, setTimeout, addUnloadHandler*/
+var utils = require('utils');
+/************************************************************************
+## The at Module
+
+The at module provides a single function `at()` which can be used to schedule
+repeating (or non-repeating) tasks to be done at a particular time.
+
+### at() function
+
+The utils.at() function will perform a given task at a given time in the
+(minecraft) day.
+
+#### Parameters
+
+ * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while
+ 9:30 pm is '21:30', midnight is '00:00' and midday is '12:00'
+ * callback : A javascript function which will be invoked at the given time.
+ * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used.
+ * repeat : (optional) true or false, default is true (repeat the task every day)
+
+#### Example
+
+To warn players when night is approaching:
+
+```javascript
+var utils = require('utils'),
+ at = require('at');
+function warning(){
+ utils.players(function( player ) {
+ echo( player, 'The night is dark and full of terrors!' );
+ });
+}
+at('19:00', warning);
+```
+To run a task only once at the next given time:
+```javascript
+var utils = require('utils'),
+ at = require('at');
+function wakeup(){
+ utils.players(function( player ) {
+ echo( player, "Wake Up Folks!" );
+ });
+}
+at('06:00', wakeup, null, false);
+```
+
+***/
+var SECOND = 1000;
+var POLLING_INTERVAL = 3 * SECOND; // this is probably precise enough
+
+function at(time24hr, callback, pWorlds, repeat) {
+ if (arguments.length === 0){
+ // TODO: Document this behaviour
+ console.log(tasksToString());
+ return;
+ }
+ var timeParts = time24hr.split( ':' );
+ var timeMins = (timeParts[0] * 60) + (timeParts[1] * 1);
+ if (!pWorlds || pWorlds === undefined ) {
+ pWorlds = utils.worlds();
+ }
+ if (repeat === undefined){
+ repeat = true;
+ }
+ utils.foreach( pWorlds, function ( world ) {
+ atAddTask( timeMins, callback, world, repeat);
+ });
+};
+var atTasks = {};
+
+function tasksToString(){
+ var result = '';
+ for (var world in atTasks){
+ result += 'world: ' + world +'\n';
+ for (var time in atTasks[world]){
+ var scheduledFuncs = atTasks[world][time];
+ for (var i = 0;i < scheduledFuncs.length; i++){
+ result += ' ' + time + ': ' + scheduledFuncs[i].constructor + '\n';
+ }
+ }
+ result += '(current world time: ' + utils.time24(world) + ')\n';
+ }
+ return result;
+}
+/*
+ constructs a function which will be called every x ticks to
+ track the schedule for a given world
+*/
+function atMonitorFactory(world){
+ var worldName = ''+ world.name;
+ var lastRun = null;
+
+ return function atMonitorForWorld(){
+ var timeMins = utils.time24(world);
+ if (timeMins === lastRun){
+ return;
+ }
+ if (lastRun === null ){
+ lastRun = timeMins - 1;
+ }else {
+ lastRun = lastRun % 1440;
+ }
+ var worldSchedule = atTasks[worldName];
+ if (!worldSchedule){
+ return;
+ }
+ while ( lastRun > timeMins ? (lastRun <= 1440) : ( lastRun < timeMins ) ){
+
+ var tasks = worldSchedule[lastRun++];
+ if (!tasks){
+ continue;
+ }
+ utils.foreach(tasks, function(task, i){
+ if (!task){
+ return;
+ }
+ setTimeout(task.callback.bind(null, timeMins, world), 1);
+ if (!task.repeat){
+ tasks[i] = null;
+ }
+ });
+ }
+ };
+}
+function atAddTask( timeMins, callback, world, repeat){
+ var worldName = ''+world.name;
+ if (!atTasks[worldName]){
+ atTasks[worldName] = {};
+ }
+ if (!atTasks[worldName][timeMins]){
+ atTasks[worldName][timeMins] = [];
+ }
+ atTasks[worldName][timeMins].push({callback: callback, repeat: repeat});
+}
+var atMonitors = [];
+function onLoadStartMonitor(event){
+ var monitor = setInterval( atMonitorFactory(event.world), POLLING_INTERVAL);
+ atMonitors.push( monitor );
+}
+if (__plugin.canary){
+ events.loadWorld( onLoadStartMonitor );
+}
+if (__plugin.bukkit){
+ events.worldLoad( onLoadStartMonitor );
+}
+
+addUnloadHandler(function(){
+ utils.foreach(atMonitors, function(atInterval){
+ clearInterval(atInterval);
+ });
+});
+
+module.exports = at;
diff --git a/src/main/js/modules/block-colors.js b/src/main/js/modules/block-colors.js
new file mode 100644
index 000000000..9c04f6510
--- /dev/null
+++ b/src/main/js/modules/block-colors.js
@@ -0,0 +1,19 @@
+var colors = {
+ white: 0,
+ orange: 1,
+ magenta: 2,
+ lightblue: 3,
+ yellow: 4,
+ lime: 5,
+ pink: 6,
+ gray: 7,
+ lightgray: 8,
+ cyan: 9,
+ purple: 10,
+ blue: 11,
+ brown: 12,
+ green: 13,
+ red: 14,
+ black: 15
+};
+module.exports = colors;
diff --git a/src/main/js/modules/blockhelper.js b/src/main/js/modules/blockhelper.js
new file mode 100644
index 000000000..8955f7add
--- /dev/null
+++ b/src/main/js/modules/blockhelper.js
@@ -0,0 +1,165 @@
+'use strict';
+/*global module, exports, require, Packages, __plugin, server*/
+var blocks = require('blocks'),
+ bountiful = false;
+
+if (__plugin.canary){
+ bountiful = parseFloat(server.canaryModVersion) > 1.7;
+}
+if (__plugin.bukkit){
+ /*
+ wph 20150103 - metadata still applies for Craftbukkit 1.8
+ bountiful = parseFloat(server.bukkitVersion) > 1.7;
+ */
+}
+var lookup = {};
+function initLookup(){
+ var Facing = Packages.net.minecraft.util.EnumFacing,
+ DyeColor = Packages.net.minecraft.item.EnumDyeColor;
+
+ lookup = {
+ facing: {
+ 0: Facing.EAST,
+ 1: Facing.SOUTH,
+ 2: Facing.WEST,
+ 3: Facing.NORTH,
+ 5: Facing.UP,
+ east: Facing.EAST,
+ south: Facing.SOUTH,
+ west: Facing.WEST,
+ north: Facing.NORTH,
+ up: Facing.UP,
+ down: Facing.DOWN
+ },
+ color: {
+ black: DyeColor.BLACK,
+ blue: DyeColor.BLUE,
+ brown: DyeColor.BROWN,
+ cyan: DyeColor.CYAN,
+ gray: DyeColor.GRAY,
+ green: DyeColor.GREEN,
+ lightblue: DyeColor.LIGHT_BLUE,
+ lime: DyeColor.LIME,
+ magenta: DyeColor.MAGENTA,
+ orange: DyeColor.ORANGE,
+ pink: DyeColor.PINK,
+ purple: DyeColor.PURPLE,
+ red: DyeColor.RED,
+ silver: DyeColor.SILVER,
+ white: DyeColor.WHITE,
+ yellow: DyeColor.YELLOW,
+ 0: DyeColor.WHITE,
+ 1: DyeColor.ORANGE,
+ 2: DyeColor.MAGENTA,
+ 3: DyeColor.LIGHT_BLUE,
+ 4: DyeColor.YELLOW,
+ 5: DyeColor.LIME,
+ 6: DyeColor.PINK,
+ 7: DyeColor.GRAY,
+ 8: DyeColor.SILVER,
+ 9: DyeColor.CYAN,
+ 10: DyeColor.PURPLE,
+ 11: DyeColor.BLUE,
+ 12: DyeColor.BROWN,
+ 13: DyeColor.GREEN,
+ 14: DyeColor.RED,
+ 15: DyeColor.BLACK
+ }
+ };
+}
+
+function property( block ){
+ var result;
+ result = {
+ get: function(p){
+ var bp = block.getPropertyForName(p);
+ return block.getValue(bp);
+ },
+ set: function(name,value){
+ var bp = block.getPropertyForName(name);
+ if (bp === null){
+ console.warn(block + ' has no property named ' + name);
+ return result;
+ }
+ if (lookup[bp.name]){
+ value = lookup[bp.name][value];
+ }
+ block.setPropertyValue(bp, value);
+ return result;
+ }
+ };
+ return result;
+}
+exports.property = property;
+/*
+ blocks which have facing
+ */
+function applyFacing( block, metadata ){
+ function face(direction){
+ property(block).set('facing', lookup.facing[direction]);
+ }
+ if ( blocks.isStair(block.typeId) ){
+ face( ['east','west','south','north'] [metadata] );
+ } else {
+ switch( block.typeId ){
+ case blocks.sign:
+ case blocks.ladder:
+ // bug: furnace, chest, dispenser don't always use the right metadata
+ case blocks.furnace:
+ case blocks.furnace_burning:
+ case blocks.chest:
+ case blocks.enderchest:
+ case blocks.dispenser:
+ face( [null,null,'north','south','west','east'][metadata] );
+ break;
+ case blocks.torch:
+ face( ['up'/* default */,'east','west','south','north','up'][metadata] );
+ break;
+ }
+ }
+}
+function applyColors( block, metadata ){
+ switch( block.typeId){
+ case blocks.wool.white:
+ case 35:
+ case blocks.stained_clay.white:
+ case 159:
+ case blocks.stained_glass.white:
+ case 95:
+ case blocks.stained_glass_pane.white:
+ case 160:
+ case blocks.carpet.white:
+ case 171:
+ property(block).set('color',metadata);
+ }
+}
+function applyRotation( block, metadata ){
+ switch (block.typeId){
+ case blocks.sign_post:
+ if (metadata !== 0){
+ property(block).set('rotation', new Packages.java.lang.Integer(metadata));
+ }
+ }
+}
+function applyVariant( block, metadata ){
+ var cmQuartzProperties = Packages.net.canarymod.api.world.blocks.properties.helpers.QuartzProperties;
+ switch (block.typeId){
+ case blocks.quartz:
+ cmQuartzProperties.applyVariant(block, cmQuartzProperties.Variant.valueOf(metadata));
+ break;
+ }
+}
+function applyProperties( block, metadata ){
+ if (!bountiful){
+ block.data = metadata;
+ return;
+ }
+ if (!lookup.facing){
+ initLookup();
+ }
+ applyFacing( block, metadata );
+ applyColors( block, metadata );
+ applyRotation( block, metadata );
+ applyVariant( block, metadata );
+}
+exports.applyProperties = applyProperties;
diff --git a/src/main/js/modules/blocks.js b/src/main/js/modules/blocks.js
index ae4fcd8b9..920c69a7c 100644
--- a/src/main/js/modules/blocks.js
+++ b/src/main/js/modules/blocks.js
@@ -20,22 +20,16 @@ approximations).
The blocks module is globally exported by the Drone module.
***/
+var colors = require('./block-colors');
+
var blocks = {
air: 0,
stone: 1,
grass: 2,
dirt: 3,
cobblestone: 4,
- oak: 5,
- spruce: '5:1',
- birch: '5:2',
- jungle: '5:3',
- sapling: {
- oak: 6,
- spruce: '6:1',
- birch: '6:2',
- jungle: '6:3'
- },
+ oak: 5, spruce: '5:1', birch: '5:2', jungle: '5:3', acacia: '5:4', dark_oak: '5:5',
+ sapling: { oak: 6, spruce: '6:1', birch: '6:2', jungle: '6:3', acacia: '6:4', dark_oak: '6:5' },
bedrock: 7,
water: 8,
water_still: 9,
@@ -49,11 +43,17 @@ var blocks = {
wood: 17,
leaves: 18,
sponge: 19,
+ sponge_wet: '19:1',
glass: 20,
lapis_lazuli_ore: 21,
lapis_lazuli_block: 22,
dispenser: 23,
sandstone: 24,
+ sandstone_chiseled: '24:1',
+ sandstone_smooth: '24:2',
+ sandstone_red: 179,
+ sandstone_red_chiseled: '179:1',
+ sandstone_red_smooth: '179:2',
note: 25,
bed: 26,
powered_rail: 27,
@@ -63,18 +63,78 @@ var blocks = {
grass_tall: 31,
dead_bush: 32,
piston: 33,
- piston_extn: 34,
- wool: {
- white: 35 // All other colors added below
- },
- dandelion: 37,
- flower_yellow: 37,
- rose: 38,
- flower_red: 38,
+ piston_extn: 34, piston_head: 34,
+ wool: { white: 35 /* All other colors added below */ },
+ piston_extended: 36,
+ dandelion: 37, flower_yellow: 37,
+ rose: 38, flower_red: 38,
mushroom_brown: 39,
mushroom_red: 40,
gold: 41,
iron: 42,
+ //http://minecraft.gamepedia.com/Data_values#Double_Stone_Slabs
+ double_slab: {
+ stone: 43,
+ sandstone: '43:1',
+ sandstone_red: 181,
+ sandstone_red_smooth: '181:8',
+ wooden: '43:2',
+ wood: {
+ oak: 125,
+ spruce: '125:1',
+ birch: '125:2',
+ jungle: '125:3',
+ acacia: '125:4',
+ dark_oak: '125:5'
+ },
+ cobblestone: '43:3',
+ brick: '43:4',
+ stonebrick: '43:5',
+ netherbrick:'43:6',
+ quartz: '43:7',
+ smooth_stone: '43:8',
+ smooth_sandstone: '43:9',
+ tile_quartz: '43:15',
+ purpur: 204,
+ },
+ slab: {
+ stone: '44:0',
+ sandstone: '44:1',
+ wooden: '44:2',
+ cobblestone: '44:3',
+ brick: '44:4',
+ stonebrick: '44:5',
+ netherbrick:'44:6',
+ quartz: '44:7',
+ upper: {
+ stone: '44:8',
+ sandstone: '44:9',
+ sandstone_red: '182:8',
+ wooden: '44:10',
+ cobblestone: '44:11',
+ brick: '44:12',
+ stonebrick: '44:13',
+ netherbrick:'44:14',
+ quartz: '44:15',
+ oak: '126:8',
+ spruce: '126:9',
+ birch: '126:10',
+ jungle: '126:11',
+ acacia: '126:12',
+ dark_oak: '126:13'
+ },
+ snow: 78,
+ stone: 44,
+ oak: 126,
+ spruce: '126:1',
+ birch: '126:2',
+ jungle: '126:3',
+ acacia: '126:4',
+ dark_oak: '126:5',
+ sandstone_red: 182,
+ purpur: 205,
+ },
+ // see brick.red 45
tnt: 46,
bookshelf: 47,
moss_stone: 48,
@@ -92,7 +152,11 @@ var blocks = {
spruce: 134,
birch: 135,
jungle: 136,
- quartz: 156
+ quartz: 156,
+ acacia: 163,
+ dark_oak: 164,
+ sandstone_red: 180,
+ purpur: 203,
},
chest: 54,
redstone_wire: 55,
@@ -132,8 +196,10 @@ var blocks = {
jackolantern: 91,
cake: 92,
redstone_repeater: 93,
- redeston_repeater_active: 94,
- chest_locked: 95,
+ redstone_repeater_active: 94,
+ stained_glass: {
+ white: 95 // all other colors added below
+ },
trapdoor: 96,
monster_egg: 97,
brick: {
@@ -166,35 +232,6 @@ var blocks = {
dragon_egg: 122,
redstone_lamp: 123,
redstone_lamp_active: 124,
- slab: {
- snow: 78,
- stone: 44,
- sandstone: '44:1',
- wooden: '44:2',
- cobblestone: '44:3',
- brick: '44:4',
- stonebrick: '44:5',
- netherbrick:'44:6',
- quartz: '44:7',
- oak: 126,
- spruce: '126:1',
- birch: '126:2',
- jungle: '126:3',
- upper: {
- stone: '44:8',
- sandstone: '44:9',
- wooden: '44:10',
- cobblestone: '44:11',
- brick: '44:12',
- stonebrick: '44:13',
- netherbrick:'44:14',
- quartz: '44:15',
- oak: '126:8',
- spruce: '126:9',
- birch: '126:10',
- jungle: '126:11'
- }
- },
cocoa: 127,
emerald_ore: 129,
enderchest: 130,
@@ -207,8 +244,9 @@ var blocks = {
flowerpot: 140,
carrots: 141,
potatoes: 142,
+ beetroot: 207,
button_wood: 143,
- mobhead: 144,
+ skull: 144, mobhead: 144,
anvil: 145,
chest_trapped: 146,
pressure_plate_weighted_light: 147,
@@ -217,65 +255,123 @@ var blocks = {
redstone_comparator_active: 150,
daylight_sensor: 151,
redstone: 152,
- netherquartzore: 153,
+ quartzore: 153, netherquartzore: 153,
hopper: 154,
quartz: 155,
+ quartz_chiseled: '155:1',
+ quartz_pillar_vertical: '155:2',
+ quartz_pillar_horizontal: '155:3',
+ quartz_pillar_cap: '155:4',
+ // see stairs.quartz
rail_activator: 157,
dropper: 158,
stained_clay: {
white: 159 // All other colors added below
},
+ stained_glass_pane: {
+ white: 160 // all other colors added below
+ },
+ slime: 165,
+ barrier: 166,
+ trapdoor_iron: 167,
+ prismarine: 168,
+ prismarine_brick: '168:1',
+ prismarine_dark: '168:2',
+ sealantern: 169,
hay: 170,
carpet: {
white: 171 // All other colors added below
},
hardened_clay: 172,
- coal_block: 173
+ coal_block: 173,
+ packed_ice: 174,
+ double_plant: 175,
+ sunflower: 175,
+ purpur: 201,
+ purpur_pillar: 202,
+ flower: {
+ sunflower: 175,
+ lilac: '175:1',
+ tallgrass: '175:2',
+ fern: '175:3',
+ rosebush: '175:4',
+ peony: '175:5',
+ yellow: 37,
+ dandelion: 37,
+ rose: 38,
+ red: 38,
+ poppy: 38,
+ blueorchid: '38:1',
+ allium: '38:2',
+ azure_bluet: '38:3',
+ red_tulip: '38:4',
+ orange_tulip: '38:5',
+ white_tulip: '38:6',
+ pink_tulip: '38:7',
+ oxeye_daisy: '38:8'
+ },
+ bonemeal: '351:15',
+ banner: {
+ standing: 176,
+ wallmounted: 177
+ },
+ daylight_sensor_inverted: 178,
+ gate: {
+ spruce: 183,
+ birch: 184,
+ jungle: 185,
+ oak: 186,
+ acacia: 187
+ },
+ fence: {
+ spruce: 188,
+ birch: 189,
+ jungle: 190,
+ oak: 191,
+ acacia: 192
+ }
};
// Add all available colors to colorized block collections
-var colors = {
- orange: ':1',
- magenta: ':2',
- lightblue: ':3',
- yellow: ':4',
- lime: ':5',
- pink: ':6',
- gray: ':7',
- lightgray: ':8',
- cyan: ':9',
- purple: ':10',
- blue: ':11',
- brown: ':12',
- green: ':13',
- red: ':14',
- black: ':15'
-};
-var colorized_blocks = ['wool', 'stained_clay', 'carpet'];
+var colorized_blocks = [
+ 'wool',
+ 'stained_clay',
+ 'carpet',
+ 'stained_glass',
+ 'stained_glass_pane'
+];
for (var i = 0, len = colorized_blocks.length; i < len; i++) {
var block = colorized_blocks[i],
data_value = blocks[block].white;
for (var color in colors) {
- blocks[block][color] = data_value + colors[color];
+ blocks[block][color] = data_value + ':' + colors[color];
}
};
/*
- rainbow colors - a convenience
- Color aliased properties that were a direct descendant of the blocks
- object are no longer used to avoid confusion with carpet and stained
- clay blocks.
- */
+ rainbow colors - a convenience
+ Color aliased properties that were a direct descendant of the blocks
+ object are no longer used to avoid confusion with carpet and stained
+ clay blocks.
+*/
blocks.rainbow = [
- blocks.wool.red,
- blocks.wool.orange,
- blocks.wool.yellow,
- blocks.wool.lime,
- blocks.wool.lightblue,
- blocks.wool.blue,
- blocks.wool.purple];
-
+ blocks.stained_glass.red,
+ blocks.stained_glass.orange,
+ blocks.stained_glass.yellow,
+ blocks.stained_glass.lime,
+ blocks.stained_glass.lightblue,
+ blocks.stained_glass.blue,
+ blocks.stained_glass.purple
+];
+blocks.isStair = function(id){
+ var p;
+ for (p in this.stairs){
+ if (this.stairs[p] == id)
+ return true;
+ }
+ return false;
+};
module.exports = blocks;
diff --git a/src/main/js/modules/fireworks/fireworks.js b/src/main/js/modules/bukkit/fireworks.js
similarity index 58%
rename from src/main/js/modules/fireworks/fireworks.js
rename to src/main/js/modules/bukkit/fireworks.js
index 345e96e7c..bf58840c0 100644
--- a/src/main/js/modules/fireworks/fireworks.js
+++ b/src/main/js/modules/bukkit/fireworks.js
@@ -1,42 +1,7 @@
-/************************************************************************
-## Fireworks Module
-
-The fireworks module makes it easy to create fireworks using
-ScriptCraft. The module has a single function `firework` which takes
-a `org.bukkit.Location` as its 1 and only parameter.
-
-### Examples
-
-The module also extends the `Drone` object adding a `firework` method
-so that fireworks can be created as a part of a Drone chain. For
-Example....
-
- /js firework()
-
-... creates a single firework, while ....
-
- /js firework().fwd(3).times(5)
-
-... creates 5 fireworks in a row. Fireworks have also been added as a
-possible option for the `arrow` module. To have a firework launch
-where an arrow strikes...
-
- /js arrows.firework()
-
-To call the fireworks.firework() function directly, you must provide a
-location. For example...
-
- /js var fireworks = require('fireworks');
- /js fireworks.firework( self.location );
-
-
-
-***/
-
/*
create a firework at the given location
*/
-var firework = function( location ) {
+function bukkitFirework( location ) {
var bkColor = org.bukkit.Color;
var bkFireworkEffect = org.bukkit.FireworkEffect;
var bkEntityType = org.bukkit.entity.EntityType;
@@ -77,7 +42,5 @@ var firework = function( location ) {
fwm.addEffect( effect );
fwm.setPower( randInt( 2 ) + 1 );
fw.setFireworkMeta( fwm );
-};
-
-exports.firework = firework;
-
+}
+module.exports = bukkitFirework;
diff --git a/src/main/js/modules/bukkit/input.js b/src/main/js/modules/bukkit/input.js
new file mode 100644
index 000000000..c0db66290
--- /dev/null
+++ b/src/main/js/modules/bukkit/input.js
@@ -0,0 +1,28 @@
+var bkPrompt = org.bukkit.conversations.Prompt,
+ bkConversationFactory = org.bukkit.conversations.ConversationFactory;
+
+function bukkitAsyncInput( sender, promptMesg, callback) {
+ var repeat = function(){
+ bukkitAsyncInput( sender, promptMesg, callback);
+ };
+ var prompt = new bkPrompt( {
+ getPromptText: function( ctx ) {
+ return promptMesg;
+ },
+ acceptInput: function( ctx, value ) {
+ callback.apply( { repeat: repeat, sender: sender, message: promptMesg, value: value },
+ [value, sender, repeat]);
+ return null;
+ },
+ blocksForInput: function( ctx ) {
+ return true;
+ }
+ });
+
+ new bkConversationFactory( __plugin )
+ .withModality( false )
+ .withFirstPrompt( prompt )
+ .buildConversation( sender )
+ .begin( );
+}
+module.exports = bukkitAsyncInput;
diff --git a/src/main/js/modules/bukkit/inventory.js b/src/main/js/modules/bukkit/inventory.js
new file mode 100644
index 000000000..bba4feaae
--- /dev/null
+++ b/src/main/js/modules/bukkit/inventory.js
@@ -0,0 +1,18 @@
+function inventory(entity){
+ var inv = entity.inventory;
+ var result = {
+ add: function(items){
+ inv.addItem([items]);
+ return result;
+ },
+ remove: function(items){
+ inv.removeItem([items]);
+ return result;
+ },
+ contains: function(items){
+ return inv['contains(org.bukkit.inventory.ItemStack)'](items);
+ }
+ };
+ return result;
+}
+module.exports = inventory;
diff --git a/src/main/js/modules/bukkit/items.js b/src/main/js/modules/bukkit/items.js
new file mode 100644
index 000000000..74152953c
--- /dev/null
+++ b/src/main/js/modules/bukkit/items.js
@@ -0,0 +1,29 @@
+/*global require, module, Packages */
+var bkItemStack = Packages.org.bukkit.inventory.ItemStack;
+var bkMaterial = Packages.org.bukkit.Material;
+var items = function( material, amount ) {
+ material = material.toUpperCase();
+ return new bkItemStack(bkMaterial[material],amount);
+};
+
+var materials = bkMaterial.values();
+
+for (var i = 0;i < materials.length; i++ ){
+ var name = (''+materials[i].name()).toLowerCase();
+ name = name.replace(/(_.)/g,function(a){ return a.replace(/_/,'').toUpperCase(); });
+
+ items[name] = (function(material){
+ return function(amount){
+ if (typeof amount == 'undefined'){
+ return material;
+ }
+ if (typeof amount == 'number'){
+ return new bkItemStack(material, amount);
+ } else {
+ return amount == material;
+ }
+ };
+ })(materials[i]);
+}
+
+module.exports = items;
diff --git a/src/main/js/modules/bukkit/recipes.js b/src/main/js/modules/bukkit/recipes.js
new file mode 100644
index 000000000..315fd0f3b
--- /dev/null
+++ b/src/main/js/modules/bukkit/recipes.js
@@ -0,0 +1,15 @@
+var items = require('items');
+var bkShapedRecipe = org.bukkit.inventory.ShapedRecipe;
+
+exports.add = function( recipe ){
+ var result = new bkShapedRecipe( recipe.result );
+ result.shape(recipe.shape[0], recipe.shape[1], recipe.shape[2]);
+ for (var i in recipe.ingredients ){
+ result.setIngredient( new java.lang.Character(i), recipe.ingredients[i].getData() );
+ }
+ server.addRecipe(result);
+ return result;
+};
+exports.remove = function( recipe ) {
+ server.removeRecipe(recipe);
+};
diff --git a/src/main/js/modules/bukkit/sounds.js b/src/main/js/modules/bukkit/sounds.js
new file mode 100644
index 000000000..dbf2875e2
--- /dev/null
+++ b/src/main/js/modules/bukkit/sounds.js
@@ -0,0 +1,61 @@
+var bkLocation = Packages.org.bukkit.Location,
+ i = 0,
+ foreach = require('utils').foreach,
+ allSounds = Packages.org.bukkit.Sound.values(),
+ len = allSounds.length,
+ sound,
+ soundName;
+
+for ( ; i < len; i++ ) {
+ sound = allSounds[i];
+ soundName = '' + sound.name();
+ var methodName = soundName.toLowerCase().replace(/_(.)/g,function(a,b){ return b.toUpperCase();});
+ exports[methodName] = (function(sound){
+ return function()
+ {
+ switch (arguments.length) {
+ case 3:
+ exports.play(sound, arguments[0], arguments[1], arguments[2]);
+ break;
+ case 2:
+ // TODO: possible combinations:
+ // location, volume,
+ // volume pitch
+ exports.play(sound, arguments[0],arguments[1]);
+ break;
+ case 1:
+ exports.play(sound, arguments[0]);
+ break;
+ case 0:
+ // play the sound at full vol, medium pitch for all players
+ //
+ foreach(server.onlinePlayers,function(player){
+ exports.play(sound, player, 1, 0);
+ });
+ default:
+ }
+ };
+ })(sound);
+}
+exports.play = function(sound, locationOrHasLocation, volume, pitch) {
+ var location = null;
+ if (!locationOrHasLocation)
+ return;
+ if (locationOrHasLocation instanceof bkLocation){
+ location = locationOrHasLocation;
+ } else {
+ locationOrHasLocation = locationOrHasLocation.location;
+ if (locationOrHasLocation && locationOrHasLocation instanceof bkLocation ){
+ location = locationOrHasLocation;
+ }
+ }
+ if (!location){
+ console.warn('sounds.play() needs a location');
+ return;
+ }
+ if (typeof volume == 'undefined')
+ volume = 1;
+ if (typeof pitch == 'undefined')
+ pitch = 1;
+ location.world.playSound(location, sound, volume, pitch);
+};
diff --git a/src/main/js/modules/canary/fireworks.js b/src/main/js/modules/canary/fireworks.js
new file mode 100644
index 000000000..1335c6d44
--- /dev/null
+++ b/src/main/js/modules/canary/fireworks.js
@@ -0,0 +1,32 @@
+'use strict';
+/*global require, Packages, module*/
+var items = require('items');
+var Canary = Packages.net.canarymod.Canary;
+var cmFireworkHelper = Packages.net.canarymod.api.inventory.helper.FireworkHelper;
+var cmExplosionType = Packages.net.canarymod.api.inventory.helper.FireworkHelper.ExplosionType;
+var explosionTypes = ['STAR','BURST','CREEPER','LARGE','SMALL'];
+var cmDyeColor = Packages.net.canarymod.api.DyeColor;
+var entityFactory = Canary.factory().entityFactory;
+var cmEntityType = Packages.net.canarymod.api.entity.EntityType;
+
+function canaryFirework( location ) {
+
+ var firework = items.fireworkStar(1);
+ cmFireworkHelper.addStarColors( firework, cmDyeColor.values() );
+ cmFireworkHelper.setDoesFlicker( firework, true );
+ cmFireworkHelper.setDoesTrail( firework, true );
+
+ // use a random explosion type
+ var rnd = Math.floor(Math.random() * explosionTypes.length);
+ var type = explosionTypes[rnd];
+ cmFireworkHelper.setStarExplosionType( firework, cmExplosionType[type]);
+ var rocket = items.fireworkRocket(1);
+ cmFireworkHelper.setFlightDuration( rocket, 3);
+ cmFireworkHelper.attachFireworkStars( rocket, [firework] );
+ var rocketEntity = entityFactory.newEntity(cmEntityType.FIREWORKROCKET, location);
+ rocketEntity.item = rocket;
+ rocketEntity.spawn();
+
+}
+
+module.exports = canaryFirework;
diff --git a/src/main/js/modules/canary/input.js b/src/main/js/modules/canary/input.js
new file mode 100644
index 000000000..de2ef63e2
--- /dev/null
+++ b/src/main/js/modules/canary/input.js
@@ -0,0 +1,26 @@
+
+function canaryAsyncInput( sender, promptMesg, callback) {
+ sender.message(promptMesg);
+ function repeat(){
+ setTimeout( function(){
+ listener.unregister(); // avoid CME
+ canaryAsyncInput( sender, promptMesg, callback);
+ },1);
+ }
+ var listener = events.chat(function (event) {
+ if (event.player == sender) {
+ var receivers = event.getReceiverList();
+ if (receivers.size() == 1 && receivers.contains(sender)){
+ var value = event.message;
+ var that = this;
+ event.setCanceled();
+ callback.apply( { repeat: repeat, sender: sender, message: promptMesg, value: value },
+ [value, sender, repeat]);
+ setTimeout(function(){that.unregister();},10);
+ }
+ }
+ },'CRITICAL');
+ // unregister after 30 seconds
+ setTimeout(function(){ listener.unregister(); }, 30000);
+}
+module.exports = canaryAsyncInput;
diff --git a/src/main/js/modules/canary/inventory.js b/src/main/js/modules/canary/inventory.js
new file mode 100644
index 000000000..4a0d25c0d
--- /dev/null
+++ b/src/main/js/modules/canary/inventory.js
@@ -0,0 +1,20 @@
+function inventory(entity){
+ var inv = entity.inventory;
+ var result = {
+ add: function(items){
+ inv['addItem(net.canarymod.api.inventory.Item)'](items);
+ return result;
+ },
+ remove: function(items){
+ inv['decreaseItemStackSize(int, int)'](items.id, items.amount);
+ return result;
+ },
+ contains: function(items){
+ var type = items.type;
+ var amount = items.amount;
+ return inv['hasItemStack(ItemType, int )'](type, amount);
+ }
+ };
+ return result;
+}
+module.exports = inventory;
diff --git a/src/main/js/modules/canary/items.js b/src/main/js/modules/canary/items.js
new file mode 100644
index 000000000..f943f1dae
--- /dev/null
+++ b/src/main/js/modules/canary/items.js
@@ -0,0 +1,70 @@
+/*global nashorn, require, Packages, module*/
+var ItemType = Packages.net.canarymod.api.inventory.ItemType;
+var Canary = Packages.net.canarymod.Canary;
+var itemFactory = Canary.factory().itemFactory;
+
+function items( material, amount ) {
+ material = material.toUpperCase();
+ var result = itemFactory["newItem(net.canarymod.api.inventory.ItemType)"](material);
+ result.amount = amount;
+ return result;
+}
+function getMaterialHandler( material ){
+ return function(amount){
+ if (typeof amount == 'undefined'){
+ return material;
+ }
+ if (typeof amount == 'number'){
+ var itemStack = itemFactory["newItem(net.canarymod.api.inventory.ItemType)"](material);
+ itemStack.amount = amount;
+ return itemStack;
+ } else {
+ var result = (amount == material);
+ if (!result){
+ if (amount.getId && amount.getData){
+ var m2 = ItemType.fromIdAndData(amount.id, amount.data);
+ result = (m2 == material);
+ }
+ }
+ return result;
+ }
+ };
+}
+if (nashorn){
+ /*
+ nashorn
+ */
+ var itemTypeClass = require('nashorn-type')(ItemType);
+ var materials = itemTypeClass.getDeclaredFields();
+ var name;
+ for (var i = 0;i < materials.length; i++ ){
+
+ if (materials[i].type != itemTypeClass) {
+ continue;
+ }
+ var materialField = materials[i];
+ name = (''+materialField.name);
+ name = name.replace(/^(.)/,function(a){
+ return a.toLowerCase();
+ });
+
+ items[name] = getMaterialHandler(materialField.get(ItemType));
+ }
+} else {
+ // non-nashorn
+ for (var field in ItemType){
+ if (ItemType[field] === undefined){
+ continue;
+ }
+ if (!(ItemType[field] instanceof ItemType)){
+ continue;
+ }
+ name = (''+field).replace(/^(.)/,function(a){
+ return a.toLowerCase();
+ });
+ items[name] = getMaterialHandler(ItemType[field]);
+ }
+}
+
+
+module.exports = items;
diff --git a/src/main/js/modules/canary/recipes.js b/src/main/js/modules/canary/recipes.js
new file mode 100644
index 000000000..3729291e2
--- /dev/null
+++ b/src/main/js/modules/canary/recipes.js
@@ -0,0 +1,48 @@
+var cm = Packages.net.canarymod;
+var cmRecipe = cm.api.inventory.recipes.CraftingRecipe;
+var cmRecipeRow = cm.api.inventory.recipes.RecipeRow;
+
+function addRecipe( recipe ){
+ return server.addRecipe( createRecipe( recipe ) );
+}
+function createRecipe( recipe ){
+ if (!recipe){
+ return null;
+ }
+ var result,
+ rows,
+ i,j,
+ cells,
+ rr;
+ if (recipe.shape){
+ rows = [];
+ for (i = 0; i < recipe.shape.length; i++){
+ cells = recipe.shape[i].split('');
+ rr = [];
+ for ( j = 0; j < cells.length ; j++){
+ if (cells[j] != ' '){
+ rr.push(recipe.ingredients[cells[j]]);
+ }
+ }
+ rows.push( new cmRecipeRow(recipe.shape[i], rr) );
+ }
+ /*
+ wph 20150607 short-term workaround for nashorn defect
+ https://bugs.openjdk.java.net/browse/JDK-8072596
+ */
+ if ( typeof Java !== 'undefined' && typeof Java.type === 'function' ) {
+ var RecipeRowArrayType = Java.type('net.canarymod.api.inventory.recipes.RecipeRow[]');
+ rows = Java.to( rows, RecipeRowArrayType );
+ }
+ result = cmRecipe.createShapedRecipe( recipe.result, rows);
+ } else {
+ result = cmRecipe.createShapelessRecipe( recipe.result, recipe.ingredients );
+ }
+ return result;
+}
+function removeRecipe( recipe ){
+ server.removeRecipe( recipe );
+}
+exports.create = createRecipe;
+exports.add = addRecipe;
+exports.remove = removeRecipe;
diff --git a/src/main/js/modules/canary/sounds.js b/src/main/js/modules/canary/sounds.js
new file mode 100644
index 000000000..1d8bcc4ba
--- /dev/null
+++ b/src/main/js/modules/canary/sounds.js
@@ -0,0 +1,65 @@
+var allSounds = Packages.net.canarymod.api.world.effects.SoundEffect.Type.values(),
+ cmSoundEffect = Packages.net.canarymod.api.world.effects.SoundEffect,
+ foreach = require('utils').foreach,
+ i = 0,
+ len = allSounds.length,
+ sound,
+ soundName;
+
+function playSound(sound, locationOrHasLocation, volume, pitch ) {
+ var location = null;
+ if (!locationOrHasLocation)
+ return;
+ if (locationOrHasLocation.world){
+ location = locationOrHasLocation;
+ } else {
+ locationOrHasLocation = locationOrHasLocation.location;
+ if (locationOrHasLocation && locationOrHasLocation.world ){
+ location = locationOrHasLocation;
+ }
+ }
+ if (!location){
+ console.warn('sounds.play() needs a location');
+ return;
+ }
+ if (typeof volume == 'undefined')
+ volume = 1;
+ if (typeof pitch == 'undefined')
+ pitch = 1;
+ var soundEffect = new cmSoundEffect(sound, location.x, location.y, location.z, volume, pitch);
+ location.world.playSound(soundEffect);
+}
+
+for ( ; i < len; i++ ) {
+ sound = allSounds[i];
+ soundName = '' + sound.name();
+ var methodName = soundName.toLowerCase().replace(/_(.)/g,function(a,b){ return b.toUpperCase();});
+ exports[methodName] = (function(sound){
+ return function()
+ {
+ switch (arguments.length) {
+ case 3:
+ playSound(sound, arguments[0], arguments[1], arguments[2]);
+ break;
+ case 2:
+ // TODO: possible combinations:
+ // location, volume,
+ // volume pitch
+ playSound(sound, arguments[0],arguments[1]);
+ break;
+ case 1:
+ playSound(sound, arguments[0]);
+ break;
+ case 0:
+ // play the sound at full vol, medium pitch for all players
+ //
+ foreach( server.playerList, function(player) {
+ playSound(sound, player, 1, 0);
+ });
+ default:
+ }
+ };
+ })(sound);
+}
+
+exports.play = playSound;
diff --git a/src/main/js/modules/classroom/index.js b/src/main/js/modules/classroom/index.js
new file mode 100644
index 000000000..31d17296c
--- /dev/null
+++ b/src/main/js/modules/classroom/index.js
@@ -0,0 +1,258 @@
+'use strict';
+/*global require, module, __plugin, __dirname, echo, persist, isOp, events, Packages, command, global */
+var utils = require('utils'),
+ watcher = require('watcher'),
+ autoload = require('plugin').autoload,
+ foreach = utils.foreach,
+ watchDir = watcher.watchDir,
+ unwatchDir = watcher.unwatchDir,
+ playersDir = __dirname + '/../../players/',
+ serverAddress = utils.serverAddress();
+
+/************************************************************************
+## Classroom Plugin
+
+The `classroom` object contains a couple of utility functions for use
+in a classroom setting. The goal of these functions is to make it
+easier for tutors to facilitate ScriptCraft for use by students in a
+classroom environment. Although granting ScriptCraft access to
+students on a shared server is potentially risky (Students can
+potentially abuse it), it is slighlty less risky than granting
+operator privileges to each student. (Enterprising students will
+quickly realise how to grant themselves and others operator privileges
+once they have access to ScriptCraft).
+
+The goal of this module is not so much to enforce restrictions
+(security or otherwise) but to make it easier for tutors to setup a
+shared server so students can learn Javascript. When scripting is
+turned on, every player who joins the server will have a dedicated
+directory into which they can save scripts. All scripts in such
+directories are automatically watched and loaded into a global
+variable named after the player.
+
+So for example, if player 'walterh' joins the server, a `walterh`
+global variable is created. If a file `greet.js` with the following
+content is dropped into the `scriptcraft/players/walterh`
+directory...
+
+```javascript
+exports.hi = function( player ){
+ echo( player, 'Hi ' + player.name);
+};
+```
+
+... then it can be invoked like this: `/js walterh.hi( self )` . This
+lets every player/student create their own functions without having
+naming collisions.
+
+It's strongly recommended that the
+`scriptcraft/players/` directory is shared so that
+others can connect to it and drop .js files into their student
+directories. On Ubuntu, select the folder in Nautilus (the default
+file browser) then right-click and choose *Sharing Options*, check the
+*Share this folder* checkbox and the *Allow others to create and
+delete files* and *Guest access* checkboxes. Click *Create Share*
+button to close the sharing options dialog. Students can then access
+the shared folder as follows...
+
+ * Windows: Open Explorer, Go to \\{serverAddress}\players\
+ * Macintosh: Open Finder, Go to smb://{serverAddress}/players/
+ * Linux: Open Nautilus, Go to smb://{serverAddress}/players/
+
+... where {serverAddress} is the ip address of the server (this is
+displayed to whoever invokes the classroom.allowScripting() function.)
+
+### jsp classroom command
+The `jsp classroom` command makes it easy for tutors to turn on or off
+classroom mode. This command can only be used by server operators. To
+turn on classroom mode (enable scripting for all players):
+
+ jsp classroom on
+
+To turn off classroom mode (disable scripting for all players):
+
+ jsp classroom off
+
+The `jsp classroom` command is provided as an easier way to turn on or
+off classroom mode. This should be used in preference to the
+classroom.allowScripting() function which is provided only for
+programmatically enabling or disabling classroom mode.
+
+### classroom.allowScripting() function
+
+Allow or disallow anyone who connects to the server (or is already
+connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
+to every student in a Minecraft classroom environment.
+
+Whenever any file is added/edited or removed from any of the players/
+directories the contents are automatically reloaded. This is to
+facilitate quick turnaround time for students getting to grips with
+Javascript.
+
+#### Parameters
+
+ * canScript : true or false
+
+#### Example
+
+To allow all players (and any players who connect to the server) to
+use the `js` and `jsp` commands...
+
+ /js classroom.allowScripting( true, self )
+
+To disallow scripting (and prevent players who join the server from using the commands)...
+
+ /js classroom.allowScripting( false, self )
+
+Only ops users can run the classroom.allowScripting() function - this is so that students
+don't try to bar themselves and each other from scripting.
+
+***/
+var store = persist('classroom', { enableScripting: false }),
+ File = java.io.File;
+
+function revokeScripting ( player ) {
+ if (__plugin.bukkit){
+ foreach( player.getEffectivePermissions(), function( perm ) {
+ if ( (''+perm.permission).indexOf( 'scriptcraft.' ) == 0 ) {
+ if ( perm.attachment ) {
+ perm.attachment.remove();
+ }
+ }
+ });
+ }
+ if (__plugin.canary){
+ //
+ var Canary = Packages.net.canarymod.Canary;
+ Canary.permissionManager().removePlayerPermission('scriptcraft.evaluate',player);
+ }
+ var playerName = '' + player.name;
+ playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,'');
+ var playerDir = new File( playersDir + playerName );
+ unwatchDir( playerDir );
+}
+var autoloadTime = {};
+
+var playerEventHandlers = {};
+
+function reloadPlayerModules( playerContext, playerDir ){
+ /*
+ wph 20150118 first unregister any event handlers registered by the player
+ */
+ var playerDirPath = ''+ playerDir.getAbsolutePath();
+ var eventHandlers = playerEventHandlers[playerDirPath];
+ if (eventHandlers){
+ for (var i = 0;i < eventHandlers.length; i++){
+ eventHandlers[i].unregister();
+ }
+ eventHandlers.length = 0;
+ } else {
+ playerEventHandlers[playerDirPath] = [];
+ eventHandlers = playerEventHandlers[playerDirPath];
+ }
+ /*
+ override events.on() so that the listener is stored here so it can be unregistered.
+ */
+ var oldOn = events.on;
+ var newOn = function( eventType, fn, priority){
+ var handler = oldOn(eventType, fn, priority);
+ eventHandlers.push(handler);
+ };
+ events.on = newOn;
+ autoload( playerContext, playerDir, { cache: false });
+ events.on = oldOn;
+}
+function grantScripting( player ) {
+ console.log('Enabling scripting for player ' + player.name);
+ var playerName = '' + player.name;
+ playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,'');
+
+ var playerDir = new File( playersDir + playerName );
+ if (!playerDir.exists()) {
+ playerDir.mkdirs();
+ var exampleJs = "//Try running this function from Minecraft with: /js $username.hi( self )\n" +
+ "//Remember to use your real username instead of $username!\n" +
+ "//So if you had username 'walterh', you would run: /js walterh.hi( self )\n" +
+ "exports.hi = function( player ){\n" +
+ "\techo( player, 'Hi ' + player.name);\n" +
+ "};"
+ createFile(playerDir, 'greet.js', exampleJs);
+ }
+
+ if (__plugin.bukkit){
+ player.addAttachment( __plugin, 'scriptcraft.*', true );
+ }
+ if (__plugin.canary){
+ player.permissionProvider.addPermission('scriptcraft.evaluate',true);
+ }
+ var playerContext = {};
+ reloadPlayerModules( playerContext, playerDir );
+ global[playerName] = playerContext;
+ watchDir( playerDir, function( changedDir ){
+ var currentTime = new java.util.Date().getTime();
+ //this check is here because this callback might get called multiple times for the watch interval
+ //one call for the file change and another for directory change
+ //(this happens only in Linux because in Windows the folder lastModifiedTime is not changed)
+ if (currentTime - autoloadTime[playerName]>1000 ) {
+ reloadPlayerModules(playerContext, playerDir );
+ }
+ autoloadTime[playerName] = currentTime;
+ });
+
+ function createFile(fileDir, fileName, fileContent) {
+ var out = new java.io.PrintWriter(new File(fileDir, fileName));
+ out.println(fileContent);
+ out.close();
+ }
+
+/*
+ echo( player, 'Create your own minecraft mods by adding javascript (.js) files');
+ echo( player, ' Windows: Open Explorer, go to \\\\' + serverAddress + '\\players\\' + player.name);
+ echo( player, ' Macintosh: Open Finder, Go to smb://' + serverAddress + '/players/' + player.name);
+ echo( player, ' Linux: Open Nautilus, Go to smb://' + serverAddress + '/players/' + player.name);
+*/
+
+}
+
+var _classroom = {
+ allowScripting: function (/* boolean: true or false */ canScript, sender ) {
+ sender = utils.player(sender);
+ if ( !sender ) {
+ console.log( 'Attempt to set classroom scripting without credentials' );
+ console.log( 'classroom.allowScripting(boolean, sender)' );
+ return;
+ }
+ /*
+ only operators should be allowed run this function
+ */
+ if ( !isOp(sender) ) {
+ console.log( 'Attempt to set classroom scripting without credentials: ' + sender.name );
+ echo( sender, 'Only operators can use this function');
+ return;
+ }
+ utils.players(function(player){
+ if (!isOp(player)){
+ canScript ? grantScripting(player) : revokeScripting(player);
+ }
+ });
+ store.enableScripting = canScript;
+
+ echo( sender, 'Scripting turned ' + ( canScript ? 'on' : 'off' ) +
+ ' for all players on server ' + serverAddress);
+ }
+};
+
+if (__plugin.canary){
+ events.connection( function( event ) {
+ if ( store.enableScripting ) {
+ grantScripting(event.player);
+ }
+ }, 'CRITICAL');
+} else {
+ events.playerJoin( function( event ) {
+ if ( store.enableScripting ) {
+ grantScripting(event.player);
+ }
+ }, 'HIGHEST');
+}
+module.exports = _classroom;
diff --git a/src/main/js/modules/drone/arc.js b/src/main/js/modules/drone/arc.js
new file mode 100644
index 000000000..6658a372a
--- /dev/null
+++ b/src/main/js/modules/drone/arc.js
@@ -0,0 +1,267 @@
+/*global require*/
+'use strict';
+/************************************************************************
+### Drone.arc() method
+
+The arc() method can be used to create 1 or more 90 degree arcs in the
+horizontal or vertical planes. This method is called by cylinder() and
+cylinder0() and the sphere() and sphere0() methods.
+
+#### Parameters
+
+arc() takes a single parameter - an object with the following named properties...
+
+ * radius - The radius of the arc.
+ * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv].
+ * meta - The metadata value. See [Data Values][dv].
+ * orientation (default: 'horizontal' ) - the orientation of the arc - can be 'vertical' or 'horizontal'.
+ * stack (default: 1 ) - the height or length of the arc (depending on the orientation - if orientation is horizontal then this parameter refers to the height, if vertical then it refers to the length ).
+ * strokeWidth (default: 1 ) - the width of the stroke (how many blocks) - if drawing nested arcs it's usually a good idea to set strokeWidth to at least 2 so that there are no gaps between each arc. The arc method uses a [bresenham algorithm][bres] to plot points along the circumference.
+ * fill - If true (or present) then the arc will be filled in.
+ * quadrants (default: `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An object with 4 properties indicating which of the 4 quadrants of a circle to draw. If the quadrants property is absent then all 4 quadrants are drawn.
+
+#### Examples
+
+To draw a 1/4 circle (top right quadrant only) with a radius of 10 and
+stroke width of 2 blocks ...
+
+ arc({blockType: blocks.iron,
+ meta: 0,
+ radius: 10,
+ strokeWidth: 2,
+ quadrants: { topright: true },
+ orientation: 'vertical',
+ stack: 1,
+ fill: false
+ } );
+
+
+
+[bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+[dv]: http://www.minecraftwiki.net/wiki/Data_values
+
+***/
+/*
+ do the bresenham thing
+ */
+function bresenham( x0,y0,radius, setPixel, quadrants ) {
+ //
+ // credit: Following code is copied almost verbatim from
+ // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
+ // Bresenham's circle algorithm
+ //
+ var f = 1 - radius;
+ var ddF_x = 1;
+ var ddF_y = -2 * radius;
+ var x = 0;
+ var y = radius;
+ var defaultQuadrants = {topleft: true, topright: true, bottomleft: true, bottomright: true};
+ quadrants = quadrants?quadrants:defaultQuadrants;
+ /*
+ II | I
+ ------------
+ III | IV
+ */
+ if ( quadrants.topleft || quadrants.topright )
+ setPixel(x0, y0 + radius ); // quadrant I/II topmost
+ if ( quadrants.bottomleft || quadrants.bottomright )
+ setPixel(x0, y0 - radius ); // quadrant III/IV bottommost
+ if ( quadrants.topright || quadrants.bottomright )
+ setPixel(x0 + radius, y0 ); // quadrant I/IV rightmost
+ if ( quadrants.topleft || quadrants.bottomleft )
+ setPixel(x0 - radius, y0 ); // quadrant II/III leftmost
+
+ while ( x < y ) {
+ if(f >= 0 ) {
+ y--;
+ ddF_y += 2;
+ f += ddF_y;
+ }
+ x++;
+ ddF_x += 2;
+ f += ddF_x;
+ if ( quadrants.topright ) {
+ setPixel(x0 + x, y0 + y ); // quadrant I
+ setPixel(x0 + y, y0 + x ); // quadrant I
+ }
+ if ( quadrants.topleft ) {
+ setPixel(x0 - x, y0 + y ); // quadrant II
+ setPixel(x0 - y, y0 + x ); // quadrant II
+ }
+ if ( quadrants.bottomleft ) {
+ setPixel(x0 - x, y0 - y ); // quadrant III
+ setPixel(x0 - y, y0 - x ); // quadrant III
+ }
+ if ( quadrants.bottomright ) {
+ setPixel(x0 + x, y0 - y ); // quadrant IV
+ setPixel(x0 + y, y0 - x ); // quadrant IV
+ }
+ }
+};
+
+function getStrokeDir( x,y ) {
+ var absY = Math.abs(y );
+ var absX = Math.abs(x );
+ var strokeDir = 0;
+ if ( y > 0 && absY >= absX )
+ strokeDir = 0 ; //down
+ else if ( y < 0 && absY >= absX )
+ strokeDir = 1 ; // up
+ else if ( x > 0 && absX >= absY )
+ strokeDir = 2 ; // left
+ else if ( x < 0 && absX >= absY )
+ strokeDir = 3 ; // right
+ return strokeDir;
+};
+
+/*
+ The daddy of all arc-related API calls -
+ if you're drawing anything that bends it ends up here.
+ */
+function arcImpl( params ) {
+ var drone = params.drone;
+ var orientation = params.orientation?params.orientation:'horizontal';
+ var quadrants = params.quadrants?params.quadrants:{
+ topright:1,
+ topleft:2,
+ bottomleft:3,
+ bottomright:4
+ };
+ var stack = params.stack?params.stack:1;
+ var radius = params.radius;
+ var strokeWidth = params.strokeWidth?params.strokeWidth:1;
+ drone.chkpt('arc2' );
+ var x0, y0, gotoxy,setPixel;
+
+ if ( orientation == 'horizontal' ) {
+ gotoxy = function( x,y ) { return drone.right(x ).fwd(y );};
+ drone.right(radius ).fwd(radius ).chkpt('center' );
+ switch ( drone.dir ) {
+ case 0: // east
+ case 2: // west
+ x0 = drone.z;
+ y0 = drone.x;
+ break;
+ case 1: // south
+ case 3: // north
+ x0 = drone.x;
+ y0 = drone.z;
+ }
+ setPixel = function( x, y ) {
+ x = ( x-x0 );
+ y = ( y-y0 );
+ if ( params.fill ) {
+ // wph 20130114 more efficient esp. for large cylinders/spheres
+ if ( y < 0 ) {
+ drone
+ .fwd( y ).right( x )
+ .cuboidX( params.blockType, params.meta, 1, stack, Math.abs( y * 2 ) + 1 )
+ .back( y ).left( x );
+ }
+ }else{
+ if ( strokeWidth == 1 ) {
+ gotoxy(x,y )
+ .cuboidX( params.blockType, params.meta,
+ 1, // width
+ stack, // height
+ strokeWidth // depth
+ )
+ .move('center' );
+ } else {
+ var strokeDir = getStrokeDir( x, y );
+ var width = 1, depth = 1;
+ switch ( strokeDir ) {
+ case 0: // down
+ y = y-( strokeWidth - 1 );
+ depth = strokeWidth;
+ break;
+ case 1: // up
+ depth = strokeWidth;
+ break;
+ case 2: // left
+ width = strokeWidth;
+ x = x-(strokeWidth-1 );
+ break;
+ case 3: // right
+ width = strokeWidth;
+ break;
+ }
+ gotoxy( x, y )
+ .cuboidX( params.blockType, params.meta, width, stack, depth )
+ .move( 'center' );
+
+ }
+ }
+ };
+ }else{
+ // vertical
+ gotoxy = function( x,y ) { return drone.right(x ).up(y );};
+ drone.right(radius ).up(radius ).chkpt('center' );
+ switch ( drone.dir ) {
+ case 0: // east
+ case 2: // west
+ x0 = drone.z;
+ y0 = drone.y;
+ break;
+ case 1: // south
+ case 3: // north
+ x0 = drone.x;
+ y0 = drone.y;
+ }
+ setPixel = function( x, y ) {
+ x = ( x - x0 );
+ y = ( y - y0 );
+ if ( params.fill ) {
+ // wph 20130114 more efficient esp. for large cylinders/spheres
+ if ( y < 0 ) {
+ drone
+ .up( y ).right( x )
+ .cuboidX( params.blockType, params.meta, 1, Math.abs( y * 2 ) + 1, stack )
+ .down( y ).left( x );
+ }
+ }else{
+ if ( strokeWidth == 1 ) {
+ gotoxy( x, y )
+ .cuboidX( params.blockType, params.meta, strokeWidth, 1, stack )
+ .move( 'center' );
+ }else{
+ var strokeDir = getStrokeDir( x,y );
+ var width = 1, height = 1;
+ switch ( strokeDir ) {
+ case 0: // down
+ y = y - ( strokeWidth - 1 );
+ height = strokeWidth;
+ break;
+ case 1: // up
+ height = strokeWidth;
+ break;
+ case 2: // left
+ width = strokeWidth;
+ x = x - ( strokeWidth - 1 );
+ break;
+ case 3: // right
+ width = strokeWidth;
+ break;
+ }
+ gotoxy(x,y )
+ .cuboidX(params.blockType, params.meta, width, height, stack )
+ .move('center' );
+
+ }
+ }
+ };
+ }
+ /*
+ setPixel assumes a 2D plane - need to put a block along appropriate plane
+ */
+ bresenham(x0,y0,radius,setPixel,quadrants );
+
+ params.drone.move('arc2' );
+};
+
+module.exports = function(Drone){
+ Drone.extend(function arc( params ) {
+ params.drone = this;
+ arcImpl( params );
+ });
+};
diff --git a/src/main/js/modules/drone/bed.js b/src/main/js/modules/drone/bed.js
new file mode 100644
index 000000000..98442c601
--- /dev/null
+++ b/src/main/js/modules/drone/bed.js
@@ -0,0 +1,55 @@
+'use strict';
+/*global require, Packages, __plugin, module*/
+var blocks = require('blocks');
+/************************************************************************
+### Drone.bed() method
+
+Creates a bed. The foot of the bed will be at the drone's location and
+the head of the bed will extend away from the drone.
+
+#### Example
+To create a bed at the in-game prompt, look at a block then type:
+
+```javascript
+/js bed()
+```
+
+Like most Drone methods, this returns the drone so it can be chained like so:
+
+```javascript
+this
+ .fwd(3)
+ .bed()
+ .back(3)
+```
+***/
+var bedDirections = {
+ 0:3, // east
+ 1:0, // south
+ 2:1, // west
+ 3:2 // north
+};
+module.exports = function(Drone){
+
+ Drone.extend( function bed(){
+ this.then(function(){
+ var foot = this.setBlock(blocks.bed, bedDirections[this.dir], 0,0,0, false);
+ var head = this.setBlock(blocks.bed, bedDirections[this.dir] + 8, 0,0,1, false);
+ if (Drone.bountiful){
+ var prop = require('blockhelper').property;
+ var BedHalf = Packages.net.canarymod.api.world.blocks.properties.helpers.BedProperties.Half;
+ prop(foot)
+ .set('facing',this.dir)
+ .set('part', BedHalf.FOOT);
+ prop(head)
+ .set('facing',this.dir)
+ .set('part', BedHalf.HEAD);
+ }
+ if (__plugin.canary){
+ foot.update();
+ head.update();
+ }
+ });
+ });
+};
+
diff --git a/src/main/js/plugins/drone/blocktype.js b/src/main/js/modules/drone/blocktype.js
similarity index 92%
rename from src/main/js/plugins/drone/blocktype.js
rename to src/main/js/modules/drone/blocktype.js
index 0067d5578..0d9160e38 100644
--- a/src/main/js/plugins/drone/blocktype.js
+++ b/src/main/js/modules/drone/blocktype.js
@@ -1,4 +1,3 @@
-var Drone = require('./drone').Drone;
var blocks = require('blocks');
/************************************************************************
@@ -16,7 +15,7 @@ Creates the text out of blocks. Useful for large-scale in-game signs.
To create a 2-line high message using glowstone...
- blocktype("Hello\nWorld",blocks.glowstone);
+ blocktype('Hello\nWorld', blocks.glowstone);
![blocktype example][imgbt1]
@@ -295,6 +294,18 @@ var bitmaps = {
' '+
' '+
' ',
+
+ '(': ' # '+
+ ' # '+
+ ' # '+
+ ' # '+
+ ' # ',
+
+ ')': ' # '+
+ ' # '+
+ ' # '+
+ ' # '+
+ ' # ',
' ':' '+
' '+
@@ -326,17 +337,7 @@ for ( c in bitmaps.raw ) {
}
}
}
-
-
-//
-// message
-// string with text to be displayed
-// fg
-// foreground material. The material the text will be in.
-// bg
-// background material, optional. The negative space within the bounding box of the text.
-//
-Drone.extend('blocktype', function( message, fg, bg ) {
+function blocktype( message, fg, bg, immediate ) {
var bmfg,
bmbg,
@@ -358,12 +359,12 @@ Drone.extend('blocktype', function( message, fg, bg ) {
fg = blocks.wool.black;
}
- bmfg = this._getBlockIdAndMeta( fg );
+ bmfg = this.getBlockIdAndMeta( fg );
bmbg = null;
if ( typeof bg != 'undefined' ) {
- bmbg = this._getBlockIdAndMeta( bg );
+ bmbg = this.getBlockIdAndMeta( bg );
}
- lines = message.split( '\n' );
+ lines = (''+ message).split( '\n' );
lineCount = lines.length;
for ( h = 0; h < lineCount; h++) {
@@ -383,7 +384,7 @@ Drone.extend('blocktype', function( message, fg, bg ) {
charWidth = bits.width;
if ( typeof bg != 'undefined' ) {
- this.cuboidX( bmbg[0], bmbg[1], charWidth, 7, 1 );
+ this.cuboidX( bmbg[0], bmbg[1], charWidth, 7, 1 , immediate);
}
for ( j = 0; j < bits.pixels.length; j++ ) {
@@ -391,7 +392,7 @@ Drone.extend('blocktype', function( message, fg, bg ) {
this.chkpt( 'btbl' );
x = bits.pixels[ j ][ 0 ];
y = bits.pixels[ j ][ 1] ;
- this.up( 6 - y ).right( x ).cuboidX( bmfg[ 0 ], bmfg[ 1 ] );
+ this.up( 6 - y ).right( x ).cuboidX( bmfg[ 0 ], bmfg[ 1 ], 1, 1, 1, immediate);
this.move( 'btbl' );
}
@@ -402,7 +403,10 @@ Drone.extend('blocktype', function( message, fg, bg ) {
}
return this.move( 'blocktext' );
-});
+}
+module.exports = function(Drone){
+ Drone.extend(blocktype);
+};
diff --git a/src/main/js/modules/drone/copypaste.js b/src/main/js/modules/drone/copypaste.js
new file mode 100644
index 000000000..d6dbb645b
--- /dev/null
+++ b/src/main/js/modules/drone/copypaste.js
@@ -0,0 +1,159 @@
+'use strict';
+/*global require, module*/
+
+/************************************************************************
+### Copy & Paste using Drone
+
+A drone can be used to copy and paste areas of the game world.
+
+#### Deprecated
+As of January 10 2015 the copy-paste functions in Drone are no longer
+supported. Copy/Paste is:
+
+1. Difficult to do correctly in a way which works for both Minecraft 1.7 and 1.8
+ due to how blocks changed in 1.8
+2. Not aligned with the purpose of ScriptCraft's Drone module which is to provide
+ a simple set of functions for scripting and in-game building.
+
+### Drone.copy() method
+
+Copies an area so it can be pasted elsewhere. The name can be used for
+pasting the copied area elsewhere...
+
+#### Parameters
+
+ * name - the name to be given to the copied area (used by `paste`)
+ * width - the width of the area to copy
+ * height - the height of the area to copy
+ * length - the length of the area (extending away from the drone) to copy
+
+#### Example
+
+ drone.copy('somethingCool',10,5,10 ).right(12 ).paste('somethingCool' );
+
+### Drone.paste() method
+
+Pastes a copied area to the current location.
+
+#### Example
+
+To copy a 10x5x10 area (using the drone's coordinates as the starting
+point) into memory. the copied area can be referenced using the name
+'somethingCool'. The drone moves 12 blocks right then pastes the copy.
+
+ drone.copy('somethingCool',10,5,10 )
+ .right(12 )
+ .paste('somethingCool' );
+
+***/
+var clipBoard = {};
+
+function paste( name, immediate ){
+ console.warn('Drone copy/paste is no longer in active development');
+ var Drone = this.constructor;
+ var ccContent = clipBoard[name];
+ if (ccContent == undefined){
+ console.warn('Nothing called ' + name + ' in clipboard!');
+ return;
+ }
+ var srcBlocks = ccContent.blocks;
+ var srcDir = ccContent.dir; // direction player was facing when copied.
+ var dirOffset = (4 + (this.dir - srcDir ) ) %4;
+
+ this.traverseWidth(srcBlocks.length,function( ww ) {
+ var h = srcBlocks[ww].length;
+ this.traverseHeight(h,function( hh ) {
+ var d = srcBlocks[ww][hh].length;
+ this.traverseDepth(d,function( dd ) {
+ var b = srcBlocks[ww][hh][dd],
+ cb = b.type,
+ md = b.data,
+ newDir,
+ dir,
+ a,
+ c,
+ len;
+ //
+ // need to adjust blocks which face a direction
+ //
+ switch ( cb ) {
+ //
+ // doors
+ //
+ case 64: // wood
+ case 71: // iron
+ // top half of door doesn't need to change
+ if ( md < 8 ) {
+ md = (md + dirOffset ) % 4;
+ }
+ break;
+ //
+ // stairs
+ //
+ case 53: // oak
+ case 67: // cobblestone
+ case 108: // red brick
+ case 109: // stone brick
+ case 114: // nether brick
+ case 128: // sandstone
+ case 134: // spruce
+ case 135: // birch
+ case 136: // junglewood
+ dir = md & 0x3;
+ a = Drone.PLAYER_STAIRS_FACING;
+ len = a.length;
+ for ( c = 0; c < len; c++ ) {
+ if ( a[c] == dir ) {
+ break;
+ }
+ }
+ c = (c + dirOffset ) %4;
+ newDir = a[c];
+ md = (md >>2<<2 ) + newDir;
+ break;
+ //
+ // signs , ladders etc
+ //
+ case 23: // dispenser
+ case 54: // chest
+ case 61: // furnace
+ case 62: // burning furnace
+ case 65: // ladder
+ case 68: // wall sign
+ a = Drone.PLAYER_SIGN_FACING;
+ len = a.length;
+ for ( c=0; c < len; c++ ) {
+ if ( a[c] == md ) {
+ break;
+ }
+ }
+ c = (c + dirOffset ) %4;
+ newDir = a[c];
+ md = newDir;
+ break;
+ }
+ this.setBlock(cb,md);
+ } );
+ } );
+ } );
+}
+function copy( name, w, h, d ) {
+ console.warn('Drone copy/paste is no longer in active development');
+ var ccContent = [];
+ this.traverseWidth(w,function( ww ) {
+ ccContent.push([] );
+ this.traverseHeight(h,function( hh ) {
+ ccContent[ww].push([] );
+ this.traverseDepth(d,function( dd ) {
+ var b = this.getBlock();
+ ccContent[ww][hh][dd] = {type:b.getTypeId(), data:b.data};
+ } );
+ } );
+ } );
+ clipBoard[name] = {dir: this.dir, blocks: ccContent};
+}
+
+module.exports = function(Drone){
+ Drone.extend( copy );
+ Drone.extend( paste );
+};
diff --git a/src/main/js/modules/drone/cylinders.js b/src/main/js/modules/drone/cylinders.js
new file mode 100644
index 000000000..cd5094ef2
--- /dev/null
+++ b/src/main/js/modules/drone/cylinders.js
@@ -0,0 +1,76 @@
+'use strict';
+
+/**************************************************************************
+### Drone.cylinder() method
+
+A convenience method for building cylinders. Building begins radius blocks to the right and forward.
+
+#### Parameters
+
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * radius
+ * height
+
+#### Example
+
+To create a cylinder of Iron 7 blocks in radius and 1 block high...
+
+ cylinder(blocks.iron, 7 , 1);
+
+
+
+### Drone.cylinder0() method
+
+A version of cylinder that hollows out the middle.
+
+#### Example
+
+To create a hollow cylinder of Iron 7 blocks in radius and 1 block high...
+
+ cylinder0(blocks.iron, 7, 1);
+
+
+
+***/
+
+function cylinder0( block,radius,height,exactParams ) {
+ var arcParams = {
+ radius: radius,
+ fill: false,
+ orientation: 'horizontal',
+ stack: height
+ };
+
+ if ( exactParams ) {
+ for ( var p in exactParams ) {
+ arcParams[p] = exactParams[p];
+ }
+ }else{
+ var md = this.getBlockIdAndMeta(block );
+ arcParams.blockType = md[0];
+ arcParams.meta = md[1];
+ }
+ return this.arc(arcParams );
+};
+function cylinder( block,radius,height,exactParams ) {
+ var arcParams = {
+ radius: radius,
+ fill: true,
+ orientation: 'horizontal',
+ stack: height
+ };
+
+ if ( exactParams ) {
+ arcParams.blockType = exactParams.blockType;
+ arcParams.meta = exactParams.meta;
+ }else{
+ var md = this.getBlockIdAndMeta(block );
+ arcParams.blockType = md[0];
+ arcParams.meta = md[1];
+ }
+ return this.arc(arcParams );
+};
+module.exports = function(Drone){
+ Drone.extend(cylinder0 );
+ Drone.extend(cylinder );
+};
diff --git a/src/main/js/modules/drone/doors.js b/src/main/js/modules/drone/doors.js
new file mode 100644
index 000000000..54f522916
--- /dev/null
+++ b/src/main/js/modules/drone/doors.js
@@ -0,0 +1,101 @@
+/*global module*/
+'use strict';
+/*************************************************************************
+### Drone.door() method
+
+create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created.
+
+#### Parameters
+
+ * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
+
+#### Example
+
+To create a wooden door at the crosshairs/drone's location...
+
+ var drone = new Drone(self);
+ drone.door();
+
+To create an iron door...
+
+ drone.door( blocks.door_iron );
+
+
+
+### Drone.door_iron() method
+
+create an Iron door.
+
+### Drone.door2() method
+
+Create double doors (left and right side)
+
+#### Parameters
+
+ * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
+
+#### Example
+
+To create double-doors at the cross-hairs/drone's location...
+
+ drone.door2();
+
+
+
+### Drone.door2_iron() method
+
+Create double iron doors
+
+***/
+
+var blocks = require('blocks');
+/*global require, Packages, __plugin*/
+function door( doorMaterial, hinge) {
+ if ( typeof doorMaterial == 'undefined' ) {
+ doorMaterial = blocks.door_wood; // wood
+ }
+ if (typeof hinge == 'undefined') {
+ hinge = 'left';
+ }
+ var Drone = this.constructor;
+ this.then(function(){
+ var lower = this.setBlock(doorMaterial, this.dir, 0, 0, 0, false);
+ var upper = this.setBlock(doorMaterial, hinge=='left' ? 8 : 9, 0,1,0, false);
+ if (Drone.bountiful){
+ var DoorHalf = Packages.net.minecraft.block.BlockDoor.EnumDoorHalf,
+ HingePosition = Packages.net.minecraft.block.BlockDoor.EnumHingePosition,
+ prop = require('blockhelper').property;
+ prop(lower)
+ .set('facing', this.dir)
+ .set('half', DoorHalf.LOWER );
+ prop(upper)
+ .set('hinge', hinge == 'left' ? HingePosition.LEFT: HingePosition.RIGHT)
+ .set('half', DoorHalf.UPPER);
+ }
+ if (__plugin.canary){
+ lower.update();
+ upper.update();
+ }
+ });
+}
+module.exports = function(Drone){
+ Drone.extend( door );
+
+ Drone.extend( function door_iron( ) {
+ this.door(blocks.door_iron);
+ } );
+
+ Drone.extend( function door2( doorMaterial ) {
+ if ( typeof doorMaterial == 'undefined' ) {
+ doorMaterial = blocks.door_wood;
+ }
+ this
+ .door( doorMaterial, 'left')
+ .right()
+ .door( doorMaterial, 'right')
+ .left();
+ } );
+ Drone.extend( function door2_iron( ) {
+ this.door2( blocks.door_iron );
+ } );
+};
diff --git a/src/main/js/modules/drone/firework.js b/src/main/js/modules/drone/firework.js
new file mode 100644
index 000000000..764750a29
--- /dev/null
+++ b/src/main/js/modules/drone/firework.js
@@ -0,0 +1,21 @@
+'use strict';
+/*global module, require*/
+var fireworks = require('fireworks');
+/*************************************************************************
+### Drone.firework() method
+
+Launches a firework at the drone's location.
+
+#### Example
+
+To launch a firework:
+
+ var drone = new Drone(self);
+ drone.firework();
+
+***/
+module.exports = function(Drone){
+ Drone.extend( function firework( ) {
+ fireworks.firework( this.getLocation() );
+ });
+};
diff --git a/src/main/js/modules/drone/garden.js b/src/main/js/modules/drone/garden.js
new file mode 100644
index 000000000..9baf2275f
--- /dev/null
+++ b/src/main/js/modules/drone/garden.js
@@ -0,0 +1,49 @@
+/*global module, require*/
+'use strict';
+/************************************************************************
+### Drone.garden() method
+
+places random flowers and long grass (similar to the effect of placing bonemeal on grass)
+
+#### Parameters
+
+ * width - the width of the garden
+ * length - how far from the drone the garden extends
+
+#### Example
+
+To create a garden 10 blocks wide by 5 blocks long...
+
+ garden(10,5);
+
+
+
+***/
+var blocks = require('blocks');
+
+function garden( width, depth ) {
+ if ( typeof width == 'undefined' ) {
+ width = 10;
+ }
+ if ( typeof depth == 'undefined' ) {
+ depth = width;
+ }
+ // make sure grass is present first
+ this
+ .box( blocks.grass, width, 1, depth )
+ .up();
+
+ // make flowers more common than long grass
+ var dist = { };
+ dist[blocks.rose] = 3;
+ dist[blocks.dandelion] = 3;
+ dist[blocks.grass_tall] = 2;
+ dist[blocks.air] = 1;
+
+ this
+ .rand( dist, width, 1, depth, false /* don't overwrite */ )
+ .down();
+}
+module.exports = function(Drone){
+ Drone.extend(garden);
+};
diff --git a/src/main/js/modules/drone/index.js b/src/main/js/modules/drone/index.js
new file mode 100644
index 000000000..b4d405c1a
--- /dev/null
+++ b/src/main/js/modules/drone/index.js
@@ -0,0 +1,922 @@
+'use strict';
+/*global __plugin, require, org, setTimeout, addUnloadHandler, global, Packages, server, module*/
+var utils = require('utils'),
+ blocks = require('blocks'),
+ THOUSAND = 1000,
+ MILLION = THOUSAND * THOUSAND;
+
+
+/*********************************************************************
+## Drone Plugin
+
+The Drone is a convenience class for building.
+
+It uses a fluent interface which means all of the Drone's methods return `this` and can be chained together like so...
+
+ var theDrone = new Drone(self);
+ theDrone.up().left().box(blocks.oak).down().fwd(3).cylinder0(blocks.lava,8);
+
+### Constructing a Drone Object
+
+Drones can be created in any of the following ways...
+
+ 1. Calling any one of the methods listed below will return a Drone object. For example...
+
+ var d = box( blocks.oak )
+
+ ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all of the Drone class's methods are also global functions that return new Drone objects. This is short-hand for creating drones and is useful for playing around with Drones at the in-game command prompt. It's shorter than typing ...
+
+ var d = new Drone(self).box( blocks.oak )
+
+ ... All of the Drone's methods return `this` so you can chain operations together like this...
+
+ var d = box( blocks.oak )
+ .up()
+ .box( blocks.oak ,3,1,3)
+ .down()
+ .fwd(2)
+ .box( blocks.oak )
+ .turn()
+ .fwd(2)
+ .box( blocks.oak )
+ .turn()
+ .fwd(2)
+ .box( blocks.oak );
+
+ 2. Using the following form...
+
+ d = new Drone(self)
+
+ ...will create a new Drone taking the current player as the parameter. If the player's cross-hairs are pointing at a block at the time then, that block's location becomes the drone's starting point. If the cross-hairs are _not_ pointing at a block, then the drone's starting location will be 2 blocks directly in front of the player. TIP: Building always happens right and front of the drone's position...
+
+ Plan View:
+
+ ^
+ |
+ |
+ D---->
+
+ For convenience you can use a _corner stone_ to begin building. The corner stone should be located just above ground level. If the cross-hair is point at or into ground level when you create a new Drone() with either a player or location given as a parameter, then building begins at the location the player was looking at or at the location. You can get around this by pointing at a 'corner stone' just above ground level or alternatively use the following statement...
+
+ d = new Drone(self).up();
+
+ ... which will move the drone up one block as soon as it's created.
+
+ 
+
+ 3. Or by using the following form...
+
+ d = new Drone(x,y,z,direction,world);
+
+ This will create a new Drone at the location you specified using x, y, z In minecraft, the X axis runs west to east and the Z axis runs north to south. The direction parameter says what direction you want the drone to face: 0 = east, 1 = south, 2 = west, 3 = north. If the direction parameter is omitted, the player's direction is used instead. Both the `direction` and `world` parameters are optional.
+
+ 4. Create a new Drone based on a Location object...
+
+ d = new Drone(location);
+
+ This is useful when you want to create a drone at a given `org.bukkit.Location` . The `Location` class is used throughout the bukkit API. For example, if you want to create a drone when a block is broken at the block's location you would do so like this...
+
+ events.blockBreak( function( event ) {
+ var location = event.block.location;
+ var drone = new Drone(location);
+ // do more stuff with the drone here...
+ });
+
+#### Parameters
+
+ * Player : If a player reference is given as the sole parameter then the block the player was looking at will be used as the starting point for the drone. If the player was not looking at a block then the player's location will be used as the starting point. If a `Player` object is provided as a paramter then it should be the only parameter.
+ * location : *NB* If a `Location` object is provided as a parameter, then it should be the only parameter.
+ * x : The x coordinate of the Drone (x,y,z,direction and world are not needed if either a player or location parameter is provided)
+ * y : The y coordinate of the Drone
+ * z : The z coordinate of the Drone
+ * direction : The direction in which the Drone is facing. Possible values are 0 (east), 1 (south), 2 (west) or 3 (north)
+ * world : The world in which the drone is created.
+
+### Drone.box() method
+
+the box() method is a convenience method for building things. (For the more performance-oriented method - see cuboid)
+
+#### parameters
+
+ * b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * w (optional - default 1) - the width of the structure
+ * h (optional - default 1) - the height of the structure
+ * d (optional - default 1) - the depth of the structure - NB this is not how deep underground the structure lies - this is how far away (depth of field) from the drone the structure will extend.
+
+#### Example
+
+To create a black structure 4 blocks wide, 9 blocks tall and 1 block long...
+
+ box(blocks.wool.black, 4, 9, 1);
+
+... or the following code does the same but creates a variable that can be used for further methods...
+
+ var drone = new Drone(self);
+ drone.box(blocks.wool.black, 4, 9, 1);
+
+
+
+### Drone.box0() method
+
+Another convenience method - this one creates 4 walls with no floor or ceiling.
+
+#### Parameters
+
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * width (optional - default 1) - the width of the structure
+ * height (optional - default 1) - the height of the structure
+ * length (optional - default 1) - the length of the structure - how far
+ away (depth of field) from the drone the structure will extend.
+
+#### Example
+
+To create a stone building with the insided hollowed out 7 wide by 3 tall by 6 long...
+
+ box0( blocks.stone, 7, 3, 6);
+
+
+
+### Drone.boxa() method
+
+Construct a cuboid using an array of blocks. As the drone moves first along the width axis, then the height (y axis) then the length, each block is picked from the array and placed.
+
+#### Parameters
+
+ * blocks - An array of blocks - each block in the array will be placed in turn.
+ * width
+ * height
+ * length
+
+#### Example
+
+Construct a rainbow-colored road 100 blocks long...
+
+ var rainbowColors = [blocks.wool.red, blocks.wool.orange, blocks.wool.yellow, blocks.wool.lime,
+ blocks.wool.lightblue, blocks.wool.blue, blocks.wool.purple];
+
+ boxa(rainbowColors,7,1,30);
+
+
+
+### Chaining
+
+All of the Drone methods return a Drone object, which means methods can be 'chained' together so instead of writing this...
+
+ drone = new Drone( self );
+ drone.fwd( 3 );
+ drone.left( 2 );
+ drone.box( blocks.grass ); // create a grass block
+ drone.up();
+ drone.box( blocks.grass ); // create another grass block
+ drone.down();
+
+...you could simply write ...
+
+ var drone = new Drone(self).fwd(3).left(2).box(blocks.grass).up().box(blocks.grass).down();
+
+... since each Drone method is also a global function that constructs a drone if none is supplied, you can shorten even further to just...
+
+ fwd(3).left(2).box(blocks.grass).up().box(blocks.grass).down()
+
+The Drone object uses a [Fluent Interface][fl] to make ScriptCraft scripts more concise and easier to write and read. Minecraft's in-game command prompt is limited to about 80 characters so chaining drone commands together means more can be done before hitting the command prompt limit. For complex building you should save your commands in a new script file and load it using /js load()
+
+[fl]: http://en.wikipedia.org/wiki/Fluent_interface
+
+### Drone Properties
+
+ * x - The Drone's position along the west-east axis (x increases as you move east)
+ * y - The Drone's position along the vertical axis (y increses as you move up)
+ * z - The Drone's position along the north-south axis (z increases as you move south)
+ * dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north.
+
+### Extending Drone
+
+The Drone object can be easily extended - new buidling recipes/blueprints can be added and can become part of a Drone's chain using the *static* method `Drone.extend`.
+
+### Drone.extend() static method
+
+Use this method to add new methods (which also become chainable global functions) to the Drone object.
+
+#### Parameters
+
+ * name - The name of the new method e.g. 'pyramid'.
+ * function - The method body.
+
+Alternatively if you provide just a function as a parameter, then the function name will be used as the new method name. For example the following two approaches are both valid.
+
+#### Example 1 Using name and function as parameters
+
+ // submitted by [edonaldson][edonaldson]
+ var Drone = require('drone');
+ Drone.extend('pyramid', function( block, height ) {
+ this.chkpt('pyramid');
+ for ( var i = height; i > 0; i -= 2) {
+ this.box(block, i, 1, i).up().right().fwd();
+ }
+ return this.move('pyramid');
+ });
+
+#### Example 2 Using just a named function as a parameter
+
+ var Drone = require('drone');
+ function pyramid( block, height ) {
+ this.chkpt('pyramid');
+ for ( var i = height; i > 0; i -= 2) {
+ this.box(block, i, 1, i).up().right().fwd();
+ }
+ return this.move('pyramid');
+ }
+ Drone.extend( pyramid );
+
+Once the method is defined (it can be defined in a new pyramid.js file) it can be used like so...
+
+ var d = new Drone(self);
+ d.pyramid(blocks.brick.stone, 12);
+
+... or simply ...
+
+ pyramid(blocks.brick.stone, 12);
+
+[edonaldson]: https://github.com/edonaldson
+
+### Drone Constants
+
+#### Drone.PLAYER_STAIRS_FACING
+
+An array which can be used when constructing stairs facing in the Drone's direction...
+
+ var d = new Drone(self);
+ d.box(blocks.stairs.oak + ':' + Drone.PLAYER_STAIRS_FACING[d.dir]);
+
+... will construct a single oak stair block facing the drone.
+
+#### Drone.PLAYER_SIGN_FACING
+
+An array which can be used when placing signs so they face in a given direction. This is used internally by the Drone.sign() method. It should also be used for placing any of the following blocks...
+
+ * chest
+ * ladder
+ * furnace
+ * dispenser
+
+By default, chests, dispensers, signs, ladders and furnaces are placed facing towards the drone so to place a chest facing the Drone just use:
+
+ drone.box( blocks.chest );
+
+To place a chest facing _away_ from the Drone:
+
+ drone.box( blocks.chest + ':' + Drone.PLAYER_SIGN_FACING[(drone.dir + 2) % 4]);
+
+#### Drone.PLAYER_TORCH_FACING
+
+Used when placing torches. By default torches will be placed facing up. If you want to place a torch so that it faces towards the drone:
+
+ drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[drone.dir]);
+
+If you want to place a torch so it faces _away_ from the drone:
+
+ drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[(drone.dir + 2) % 4]);
+
+#### Drone.MAX_SIDE
+
+Specifies the maximum length (in any dimension) when calling the Drone.cuboidX (box) method.
+The default value is 1,000 blocks.
+
+If you see an error message in the console `Build too big!` It's because the width, height or length paramete was greater than the Drone.MAX_SIDE value.
+
+#### Drone.MAX_VOLUME
+
+Specifies the maximum value for any call to Drone.cuboidX (box) method.
+The default value is 1,000,000 (1 million) blocks.
+
+If the volume (width X height X length) of any single call to the Drone.cuboidX() method exceeds this value, you will see an error message in the console `Build too big!` .
+
+The values of both the `Drone.MAX_SiDE` and `Drone.MAX_VOLUME` variables _can_ be overridden but it's not recommended.
+
+***/
+
+//
+// Implementation
+// ==============
+//
+// There is no need to read any further unless you want to understand how the Drone object works.
+//
+function getDirFromRotation( location ) {
+ // 0 = east, 1 = south, 2 = west, 3 = north
+ // 46 to 135 = west
+ // 136 to 225 = north
+ // 226 to 315 = east
+ // 316 to 45 = south
+ var r;
+ if (__plugin.canary ) {
+ r = location.rotation;
+ }
+ if (__plugin.bukkit) {
+ r = location.yaw;
+ }
+
+ // west = -270
+ // north = -180
+ // east = -90
+ // south = 0
+
+ r = (r + 360 ) % 360; // east could be 270 or -90
+
+ if ( r > 45 && r <= 135 )
+ return 2; // west
+ if ( r > 135 && r <= 225 )
+ return 3; // north
+ if ( r > 225 && r <= 315 )
+ return 0; // east
+ return 1; // south
+}
+/*
+ low-level function to place a block in the world - all drone methods which
+ place blocks ultimately invoke this function.
+*/
+function putBlock( x, y, z, blockId, metadata, world, update ) {
+ if ( typeof metadata == 'undefined' ) {
+ metadata = 0;
+ }
+ var block = world.getBlockAt( x, y, z );
+
+ if (__plugin.canary) {
+ var BlockType = Packages.net.canarymod.api.world.blocks.BlockType;
+ block.type = BlockType.fromId(blockId);
+ var applyProperties = require('blockhelper').applyProperties;
+ applyProperties(block, metadata);
+ if (typeof update === 'undefined'){
+ update = true;
+ }
+ if (update){
+ block.update();
+ }
+ }
+ if (__plugin.bukkit) {
+ block.setTypeIdAndData( blockId, metadata, false );
+ block.data = metadata;
+ }
+ return block;
+}
+/*
+ Drone constructs a new Drone object
+*/
+function Drone( x, y, z, dir, world ) {
+ this.record = false;
+ var usePlayerCoords = false;
+ var player = (typeof self !== 'undefined' ? self : null);
+ var playerPos;
+ if ( x.location && x.name) {
+ player = x;
+ }
+ playerPos = x.location;
+
+ var that = this;
+ var populateFromLocation = function( loc ) {
+ that.x = loc.x;
+ that.y = loc.y;
+ that.z = loc.z;
+ that.dir = getDirFromRotation(loc);
+ that.world = loc.world;
+ };
+ var mp = utils.getMousePos( player );
+ if ( typeof x == 'undefined' || x.location ) {
+ if ( mp ) {
+ populateFromLocation( mp );
+ if ( playerPos ) {
+ this.dir = getDirFromRotation(playerPos);
+ }
+ } else {
+ // base it on the player's current location
+ usePlayerCoords = true;
+ //
+ // it's possible that drone.js could be loaded by a non-playing op
+ // (from the server console)
+ //
+ if ( !playerPos ) {
+ return null;
+ }
+ populateFromLocation( playerPos );
+ }
+ } else {
+ if ( arguments[0].x && arguments[0].y && arguments[0].z ) {
+ populateFromLocation( arguments[ 0 ] );
+ } else {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ if ( typeof dir == 'undefined' ) {
+ this.dir = getDirFromRotation( playerPos);
+ } else {
+ this.dir = dir%4;
+ }
+ if ( typeof world == 'undefined' ) {
+ this.world = playerPos.world;
+ } else {
+ this.world = world;
+ }
+ }
+ }
+
+ if ( usePlayerCoords ) {
+ this.fwd( 3 );
+ }
+ this.chkpt( 'start' );
+ this.record = true;
+ this.history = [];
+ this.player = player;
+ return this;
+}
+
+Drone.getDirFromRotation = getDirFromRotation;
+
+Drone.opsPerSec = 10;
+var theQueue = [];
+function processQueue(){
+ var process,
+ i = 0,
+ queues = getAllQueues();
+
+ for ( ; i < queues.length; i++ ) {
+ process = queues[i].shift();
+ if (process){
+ try {
+ process();
+ } catch( e ) {
+ console.log('Drone build error: ' + e + ' while processing ' + process);
+ }
+ }
+ }
+ setTimeout( processQueue, 1000 / Drone.opsPerSec );
+};
+setTimeout( processQueue, 1000 / Drone.opsPerSec );
+
+addUnloadHandler( function() {
+ var pendingBuildOps = 0;
+ var allQueues = getAllQueues();
+ for (var i = 0; i < allQueues.length; i++){
+ pendingBuildOps += allQueues[i].length;
+ }
+ if (pendingBuildOps > 0){
+ console.warn('There were ' + pendingBuildOps + ' pending build operations which were cancelled');
+ }
+});
+//
+// add custom methods to the Drone object using this function
+//
+Drone.extend = function( name, func ) {
+ if (arguments.length == 1){
+ func = name;
+ if ( !func.name ){
+ throw 'A Drone extension function must have a name!';
+ }
+ name = func.name;
+ }
+ Drone.prototype[ '_' + name ] = func;
+ Drone.prototype[ name ] = function( ) {
+ if ( this.record ) {
+ this.history.push( [ name, arguments ] );
+ }
+ var oldVal = this.record;
+ this.record = false;
+ this[ '_' + name ].apply( this, arguments );
+ this.record = oldVal;
+ return this;
+ };
+
+ global[name] = function( ) {
+ var result = new Drone( self );
+ result[name].apply( result, arguments );
+ return result;
+ };
+};
+
+/**************************************************************************
+### Drone.times() Method
+
+The `times()` method makes building multiple copies of buildings
+easy. It's possible to create rows or grids of buildings without
+resorting to `for` or `while` loops.
+
+#### Parameters
+
+ * numTimes : The number of times you want to repeat the preceding statements.
+
+#### Limitation
+
+For now, don't use `times()` inside a Drone method implementation – only use it at the in-game prompt as a short-hand workaround for loops.
+
+#### Example
+
+Say you want to do the same thing over and over. You have a couple of options:
+
+ * You can use a `for` loop …
+
+ d = new Drone(); for ( var i = 0; i < 4; i++ ) { d.cottage().right(8); }
+
+While this will fit on the in-game prompt, it's awkward. You need to
+declare a new Drone object first, then write a `for` loop to create the
+4 cottages. It's also error prone – even the `for` loop is too much
+syntax for what should really be simple.
+
+ * You can use a `while` loop …
+
+ d = new Drone(); var i=4; while (i--) { d.cottage().right(8); }
+
+… which is slightly shorter but still too much syntax. Each of the
+above statements is fine for creating a 1-dimensional array of
+structures. But what if you want to create a 2-dimensional or
+3-dimensional array of structures? Enter the `times()` method.
+
+The `times()` method lets you repeat commands in a chain any number of
+times. So to create 4 cottages in a row you would use the following
+statement:
+
+ cottage().right(8).times(4);
+
+… which will build a cottage, then move right 8 blocks, then do it
+again 4 times over so that at the end you will have 4 cottages in a
+row. What's more, the `times()` method can be called more than once in
+a chain. So if you wanted to create a *grid* of 20 houses ( 4 x 5 ),
+you would do so using the following statement:
+
+ cottage().right(8).times(4).fwd(8).left(32).times(5);
+
+… breaking it down …
+
+ 1. The first 3 calls in the chain ( `cottage()`, `right(8)`, `times(4)` ) build a single row of 4 cottages.
+
+ 2. The last 3 calls in the chain ( `fwd(8)`, `left(32)`, `times(5)` ) move the drone forward 8 then left 32 blocks (4 x 8) to return to the original X coordinate, then everything in the chain is repeated again 5 times so that in the end, we have a grid of 20 cottages, 4 x 5. Normally this would require a nested loop but the `times()` method does away with the need for loops when repeating builds.
+
+Another example: This statement creates a row of trees 2 by 3:
+
+ oak().right(10).times(2).left(20).fwd(10).times(3)
+
+… You can see the results below.
+
+
+
+***/
+Drone.prototype.times = function( numTimes, commands ) {
+ if ( typeof commands == 'undefined' ) {
+ commands = this.history.concat();
+ }
+
+ this.history = [ [ 'times', [ numTimes + 1, commands ] ] ];
+ var oldVal = this.record;
+ this.record = false;
+ for ( var j = 1; j < numTimes; j++ ) {
+ for ( var i = 0; i < commands.length; i++) {
+ var command = commands[i];
+ var methodName = command[0];
+ var args = command[1];
+ this[ methodName ].apply( this, args );
+ }
+ }
+ this.record = oldVal;
+ return this;
+};
+
+
+Drone.prototype.getBlock = function(){
+ return this.world.getBlockAt(this.x,this.y,this.z);
+};
+Drone.prototype.setBlock = function(blockType, data, ow, oh, od, update){
+ if (typeof ow == 'undefined')
+ ow = 0;
+ if (typeof oh == 'undefined')
+ oh = 0;
+ if (typeof od == 'undefined')
+ od = 0;
+ this
+ .right(ow)
+ .up(oh)
+ .fwd(od);
+ var result = putBlock(this.x, this.y, this.z, blockType, data, this.world, update);
+ this
+ .left(ow)
+ .down(oh)
+ .back(od);
+ return result;
+};
+Drone.prototype.traverseWidth = function(width, callback){
+ _traverse[this.dir].width(this, width, callback);
+};
+Drone.prototype.traverseHeight = function(height, callback){
+ traverseHeight(this, height, callback);
+};
+Drone.prototype.traverseDepth = function(depth, callback){
+ _traverse[this.dir].depth(this, depth, callback);
+};
+//
+// building
+//
+
+var playerQueues = {};
+/*
+ if the drone has an associated player, then use that player's queue otherwise
+ use the global queue.
+*/
+function getQueue( drone ){
+ if ( drone.player ) {
+ var playerName = ''+drone.player.name;
+ var result = playerQueues[playerName];
+ if (result === undefined){
+ playerQueues[playerName] = [];
+ return playerQueues[playerName];
+ }
+ return result;
+ } else {
+ return theQueue;
+ }
+}
+function getAllQueues() {
+ var result = [ theQueue ];
+ for (var pq in playerQueues) {
+ result.push(playerQueues[pq]) ;
+ }
+ return result;
+}
+Drone.prototype.cuboida = function(/* Array */ blocks, w, h, d, overwrite) {
+ if ( typeof overwrite == 'undefined' ) {
+ overwrite = true;
+ }
+ if ( typeof h == 'undefined' ) {
+ h = 1;
+ }
+ if ( typeof d == 'undefined' ) {
+ d = 1;
+ }
+ if ( typeof w == 'undefined' ) {
+ w = 1;
+ }
+ //
+ // wph 20140823 make a copy because don't want to modify array in background
+ //
+ var blocksForBuild = blocks.slice();
+ var len = blocksForBuild.length,
+ i = 0;
+ for ( ; i < len; i++ ) {
+ blocksForBuild[i] = this.getBlockIdAndMeta( blocksForBuild[ i ] );
+ }
+ this.then(function(){
+ var bi = 0;
+ traverseDHW( this, d,h,w, function traverseWidthCallback( ) {
+ var properBlock = blocksForBuild[ bi % len ];
+ this.setBlock(properBlock[0], properBlock[1]);
+ bi++;
+ });
+ });
+ return this;
+};
+Drone.MAX_VOLUME = 1 * MILLION;
+Drone.MAX_SIDE = 1 * THOUSAND;
+
+function isTooBig(w, h, d ) {
+ return ( w * h * d ) >= Drone.MAX_VOLUME ||
+ ( w >= Drone.MAX_SIDE ) ||
+ ( h >= Drone.MAX_SIDE ) ||
+ ( d >= Drone.MAX_SIDE );
+};
+/*
+ faster cuboid because blockid, meta and world must be provided
+ use this method when you need to repeatedly place blocks
+ */
+Drone.prototype.cuboidX = function( blockType, meta, w, h, d, immediate ) {
+
+ if ( typeof h == 'undefined' ) {
+ h = 1;
+ }
+ if ( typeof d == 'undefined' ) {
+ d = 1;
+ }
+ if ( typeof w == 'undefined' ) {
+ w = 1;
+ }
+ if ( isTooBig( w, h, d ) ) {
+ this.sign([
+ 'Build too Big!',
+ 'width:' + w,
+ 'height:' + h,
+ 'depth:' + d
+ ], 68);
+ console.warn('Build too big! ' + w + ' X ' + h + ' X ' + d);
+ return this;
+ }
+ if ( !immediate ) {
+ this.then(function(){
+ traverseDHW( this, d,h,w, function( ) {
+ this.setBlock( blockType, meta );
+ });
+ });
+ } else {
+ traverseDHW( this, d,h,w, function( ) {
+ this.setBlock( blockType, meta );
+ });
+ }
+ return this;
+
+};
+/*
+ deferred execution of a drone method
+*/
+var thenID = 0;
+Drone.prototype.then = function( next ){
+ var chkptThen = '_now' + (thenID++);
+ this.chkpt(chkptThen);
+ var thisNext = next.bind(this);
+ function wrapperFn(){
+ var chkNow = '_now' + (thenID++);
+ this.chkpt(chkNow);
+ this.move(chkptThen);
+ thisNext();
+ this.move(chkNow);
+ }
+ getQueue(this).push( wrapperFn.bind(this) );
+ return this;
+};
+Drone.prototype.cuboid = function( block, w, h, d, immediate ) {
+ var bm = this.getBlockIdAndMeta( block );
+ return this.cuboidX( bm[0], bm[1], w, h, d, immediate);
+};
+
+Drone.prototype.cuboid0 = function( block, w, h, d, immediate ) {
+ var start = 'cuboid0' + w + h + d + immediate;
+ this
+ .chkpt( start )
+ .cuboid( block, w, h, 1, immediate ) // Front wall
+ .cuboid( block, 1, h, d, immediate ) // Left wall
+ .right( w - 1 )
+ .cuboid( block, 1, h, d, immediate ) // Right wall
+ .left( w - 1 )
+ .fwd( d - 1 )
+ .cuboid( block, w, h, 1, immediate ) // Back wall
+ .move( start );
+};
+
+
+
+// player dirs: 0 = east, 1 = south, 2 = west, 3 = north
+// block dirs: 0 = east, 1 = west, 2 = south , 3 = north
+// sign dirs: 5 = east, 3 = south, 4 = west, 2 = north
+Drone.PLAYER_STAIRS_FACING = [ 0, 2, 1, 3 ];
+
+// for blocks 68 (wall signs) 65 (ladders) 61,62 (furnaces) 23 (dispenser) and 54 (chest)
+Drone.PLAYER_SIGN_FACING = [ 4, 2, 5, 3 ];
+Drone.PLAYER_TORCH_FACING = [ 2, 4, 1, 3 ];
+
+Drone.extend('box', Drone.prototype.cuboid );
+Drone.extend('box0',Drone.prototype.cuboid0 );
+Drone.extend('boxa',Drone.prototype.cuboida );
+//
+// show the Drone's position and direction
+//
+Drone.prototype.toString = function( ) {
+ var dirs = ['east','south','west','north'];
+ return 'x: ' + this.x + ' y: '+this.y + ' z: ' + this.z + ' dir: ' + this.dir + ' '+dirs[this.dir];
+};
+Drone.prototype.debug = function( ) {
+ console.log(this.toString( ) );
+ return this;
+};
+
+function getBlockIdAndMeta( b ) {
+ var defaultMeta = 0,
+ i = 0,
+ bs,
+ md,
+ sp;
+ if (typeof b === 'number' || /^[0-9]+$/.test(b)) {
+ // wph 20130414 - use sensible defaults for certain blocks e.g. stairs
+ // should face the drone.
+ if ( blocks.isStair(b) ) {
+ defaultMeta = Drone.PLAYER_STAIRS_FACING[ this.dir % 4 ];
+ } else {
+ switch (b) {
+ case blocks.sign:
+ case blocks.ladder:
+ // bug: furnace, chest, dispenser don't always use the right metadata
+ case blocks.furnace:
+ case blocks.furnace_burning:
+ case blocks.chest:
+ case blocks.enderchest:
+ case blocks.dispenser:
+ defaultMeta = Drone.PLAYER_SIGN_FACING[ this.dir % 4 ];
+ break;
+ case blocks.sign_post:
+ defaultMeta = ( 12 + ( ( this.dir + 2 ) * 4 ) ) % 16;
+ break;
+ }
+ }
+ return [ b, defaultMeta ];
+ }
+ if ( typeof b === 'string' ) {
+ bs = b;
+ sp = bs.indexOf(':' );
+ if ( sp == -1 ) {
+ b = parseInt( bs );
+ return [ b, defaultMeta ];
+ }
+ b = parseInt(bs.substring(0,sp ) );
+ md = parseInt(bs.substring(sp+1,bs.length ) );
+ return [b,md];
+ }
+ if (b.id){
+ // wph 20141230 we are dealing with an object
+ var blockInfo = b;
+ var metadata = {};
+ for (i in b){
+ if (i !== 'id')
+ metadata[i] = b[i];
+ }
+ return [b.id, metadata];
+ }
+}
+var _traverse = [{},{},{},{}];
+// east
+function walkWidthEast( drone, n,callback ) {
+ var s = drone.z, e = s + n;
+ for ( ; drone.z < e; drone.z++ ) {
+ callback.call(drone ,drone.z-s );
+ }
+ drone.z = s;
+}
+function walkDepthEast( drone,n,callback ) {
+ var s = drone.x, e = s+n;
+ for ( ;drone.x < e;drone.x++ ) {
+ callback.call(drone, drone.x-s );
+ }
+ drone.x = s;
+}
+function walkWidthSouth( drone,n,callback ) {
+ var s = drone.x, e = s-n;
+ for ( ;drone.x > e;drone.x-- ) {
+ callback.call(drone, s-drone.x );
+ }
+ drone.x = s;
+}
+function walkWidthWest( drone,n,callback ) {
+ var s = drone.z, e = s-n;
+ for ( ;drone.z > e;drone.z-- ) {
+ callback.call(drone, s-drone.z );
+ }
+ drone.z = s;
+}
+_traverse[0].width = walkWidthEast;
+_traverse[0].depth = walkDepthEast;
+// south
+_traverse[1].width = walkWidthSouth;
+_traverse[1].depth = walkWidthEast;
+// west
+_traverse[2].width = walkWidthWest;
+_traverse[2].depth = walkWidthSouth;
+// north
+_traverse[3].width = walkDepthEast;
+_traverse[3].depth = walkWidthWest;
+function traverseHeight( drone,n,callback ) {
+ var s = drone.y, e = s + n;
+ for ( ; drone.y < e; drone.y++ ) {
+ callback.call(drone, drone.y-s );
+ }
+ drone.y = s;
+};
+function traverseDHW( drone, d,h,w, callback ){
+ _traverse[drone.dir].depth( drone, d, function traverseDepthCallback( ) {
+ traverseHeight( this, h, function traverseHeightCallback( ) {
+ _traverse[this.dir].width( this, w, callback);
+ });
+ });
+}
+
+//
+// wph 20130130 - make this a method - extensions can use it.
+//
+Drone.prototype.getBlockIdAndMeta = getBlockIdAndMeta;
+Drone.prototype._getBlockIdAndMeta = function(b){
+ console.warn('_getBlockIdAndMeta is deprecated. Use .getBlockIdAndMeta() instead');
+ return this.getBlockIdAndMeta(b);
+};
+Drone.bountiful = __plugin.canary ? parseFloat(server.canaryModVersion) > 1.7 : false;
+
+var droneCoreExts = [
+ './arc',
+ './bed',
+ './blocktype',
+ './copypaste',
+ './cylinders',
+ './doors',
+ './firework',
+ './garden',
+ './ladder',
+ './movement',
+ './prism',
+ './rand',
+ './sign',
+ './sphere',
+ './stairs',
+ './trees'
+];
+utils.foreach(droneCoreExts, function(path){
+ require(path)(Drone);
+});
+module.exports = Drone;
diff --git a/src/main/js/modules/drone/ladder.js b/src/main/js/modules/drone/ladder.js
new file mode 100644
index 000000000..312648a57
--- /dev/null
+++ b/src/main/js/modules/drone/ladder.js
@@ -0,0 +1,46 @@
+'use strict';
+/*global require, module*/
+/************************************************************************
+### Drone.ladder() method
+
+Creates a ladder extending skyward.
+
+#### Parameters
+
+ * height (optional - default 1)
+
+#### Example
+
+To create a ladder extending 10 blocks high:
+
+ var drone = new Drone(self);
+ drone.ladder(10)
+
+At the in-game prompt, look at a block and then type:
+
+ /js ladder(10)
+
+A ladder 10 blocks high will be created at the point you were looking at.
+
+#### Since
+##### 3.0.3
+***/
+var blocks = require('blocks');
+
+function ladder( height ){
+ this.then(function ladderLater(){
+ var block = this.getBlock();
+ if (block.typeId == blocks.air || block.typeId == blocks.ladder){
+ this.box(blocks.ladder, 1, height, 1, true);
+ } else {
+ this
+ .back()
+ .box(blocks.ladder, 1, height, 1, true)
+ .fwd();
+ }
+ });
+}
+
+module.exports = function(Drone){
+ Drone.extend( ladder );
+};
diff --git a/src/main/js/modules/drone/movement.js b/src/main/js/modules/drone/movement.js
new file mode 100644
index 000000000..2d63d4a27
--- /dev/null
+++ b/src/main/js/modules/drone/movement.js
@@ -0,0 +1,189 @@
+'use strict';
+/*global require,__plugin, module, Packages, org*/
+
+/************************************************************************
+### Drone Movement
+
+Drones can move freely in minecraft's 3-D world. You control the
+Drone's movement using any of the following methods..
+
+ * up()
+ * down()
+ * left()
+ * right()
+ * fwd()
+ * back()
+ * turn()
+
+... Each of these methods takes a single optional parameter
+`numBlocks` - the number of blocks to move in the given direction. If
+no parameter is given, the default is 1.
+
+To change direction use the `turn()` method which also takes a single
+optional parameter (numTurns) - the number of 90 degree turns to
+make. Turns are always clock-wise. If the drone is facing north, then
+drone.turn() will make the turn face east. If the drone is facing east
+then drone.turn(2) will make the drone turn twice so that it is facing
+west.
+
+### Drone Positional Info
+
+ * getLocation() - Returns a native Java Location object for the drone
+
+### Drone Markers
+
+Markers are useful when your Drone has to do a lot of work. You can
+set a check-point and return to the check-point using the move()
+method. If your drone is about to undertake a lot of work -
+e.g. building a road, skyscraper or forest you should set a
+check-point before doing so if you want your drone to return to its
+current location.
+
+A 'start' checkpoint is automatically created when the Drone is first created.
+
+Markers are created and returned to using the followng two methods...
+
+ * chkpt - Saves the drone's current location so it can be returned to later.
+ * move - moves the drone to a saved location. Alternatively you can provide a Java Location object or x,y,z and direction parameters.
+
+#### Parameters
+
+ * name - the name of the checkpoint to save or return to.
+
+#### Example
+
+ drone.chkpt('town-square');
+ //
+ // the drone can now go off on a long excursion
+ //
+ for ( i = 0; i< 100; i++) {
+ drone.fwd(12).box(6);
+ }
+ //
+ // return to the point before the excursion
+ //
+ drone.move('town-square');
+
+***/
+var _movements = [{},{},{},{}];
+// east
+_movements[0].right = function( drone,n ) { drone.z +=n; return drone;};
+_movements[0].left = function( drone,n ) { drone.z -=n; return drone;};
+_movements[0].fwd = function( drone,n ) { drone.x +=n; return drone;};
+_movements[0].back = function( drone,n ) { drone.x -= n; return drone;};
+// south
+_movements[1].right = _movements[0].back;
+_movements[1].left = _movements[0].fwd;
+_movements[1].fwd = _movements[0].right;
+_movements[1].back = _movements[0].left;
+// west
+_movements[2].right = _movements[0].left;
+_movements[2].left = _movements[0].right;
+_movements[2].fwd = _movements[0].back;
+_movements[2].back = _movements[0].fwd;
+// north
+_movements[3].right = _movements[0].fwd;
+_movements[3].left = _movements[0].back;
+_movements[3].fwd = _movements[0].left;
+_movements[3].back = _movements[0].right;
+
+function turn( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ this.dir += n;
+ this.dir %=4;
+}
+function chkpt( name ) {
+ this._checkpoints[ name ] = { x:this.x, y:this.y, z:this.z, dir:this.dir };
+}
+function move( ) {
+ var Drone = this.constructor;
+ if ( arguments[0].x && arguments[0].y && arguments[0].z) {
+ this.x = arguments[0].x;
+ this.y = arguments[0].y;
+ this.z = arguments[0].z;
+ this.dir = Drone.getDirFromRotation(arguments[0] );
+ this.world = arguments[0].world;
+ } else if ( typeof arguments[0] === 'string' ) {
+ var coords = this._checkpoints[arguments[0]];
+ if ( coords ) {
+ this.x = coords.x;
+ this.y = coords.y;
+ this.z = coords.z;
+ this.dir = coords.dir%4;
+ }
+ } else {
+ // expect x,y,z,dir
+ switch( arguments.length ) {
+ case 4:
+ this.dir = arguments[3];
+ case 3:
+ this.z = arguments[2];
+ case 2:
+ this.y = arguments[1];
+ case 1:
+ this.x = arguments[0];
+ }
+ }
+}
+function right( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ _movements[ this.dir ].right( this, n );
+}
+function left( n ) {
+ if ( typeof n == 'undefined') {
+ n = 1;
+ }
+ _movements[ this.dir ].left( this, n );
+}
+function fwd( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ _movements[ this.dir ].fwd( this, n );
+}
+function back( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ _movements[ this.dir ].back( this, n );
+}
+function up( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ this.y+= n;
+}
+function down( n ) {
+ if ( typeof n == 'undefined' ) {
+ n = 1;
+ }
+ this.y-= n;
+}
+function getLocation( ) {
+ if (__plugin.canary) {
+ var cmLocation = Packages.net.canarymod.api.world.position.Location;
+ return new cmLocation( this.world, this.x, this.y, this.z, 0, 0);
+ }
+ if (__plugin.bukkit) {
+ var bkLocation = org.bukkit.Location;
+ return new bkLocation( this.world, this.x, this.y, this.z );
+ }
+}
+module.exports = function(Drone){
+ Drone.prototype._checkpoints = {};
+ Drone.prototype.getLocation = getLocation;
+ Drone.extend( chkpt );
+ Drone.extend( move );
+ Drone.extend( turn );
+ Drone.extend( right );
+ Drone.extend( left );
+ Drone.extend( fwd );
+ Drone.extend( back );
+ Drone.extend( up );
+ Drone.extend( down );
+};
+
diff --git a/src/main/js/modules/drone/prism.js b/src/main/js/modules/drone/prism.js
new file mode 100644
index 000000000..109651402
--- /dev/null
+++ b/src/main/js/modules/drone/prism.js
@@ -0,0 +1,98 @@
+'use strict';
+/*global require, module*/
+/************************************************************************
+### Drone.prism() method
+
+Creates a prism. This is useful for roofs on houses.
+
+#### Parameters
+
+ * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
+ Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
+ * width - the width of the prism
+ * length - the length of the prism (will be 2 time its height)
+
+#### Example
+
+ prism(blocks.oak,3,12);
+
+
+
+### Drone.prism0() method
+
+A variation on `prism` which hollows out the inside of the prism. It
+uses the same parameters as `prism`.
+
+***/
+var STAIRBLOCKS = {
+ 53: '5:0' // oak wood
+ ,67: 4 // cobblestone
+ ,108: 45 // brick
+ ,109: 98 // stone brick
+ ,114: 112 // nether brick
+ ,128: 24 // sandstone
+ ,134: '5:1' // spruce wood
+ ,135: '5:2' // birch wood
+ ,136: '5:3' // jungle wood
+ ,156: 155 // quartz
+};
+//
+// prism private implementation
+//
+function prism( block, w, d ) {
+ var stairEquiv = STAIRBLOCKS[block];
+ if ( stairEquiv ) {
+ this
+ .fwd()
+ .prism( stairEquiv,w,d-2 )
+ .back()
+ .stairs(block, w, d / 2)
+ .fwd(d - 1)
+ .right(w - 1)
+ .turn(2)
+ .stairs(block, w, d / 2)
+ .turn(2)
+ .left(w - 1)
+ .back(d - 1);
+ }else{
+ var c = 0;
+ var d2 = d;
+ while ( d2 >= 1 ) {
+ this.cuboid(block,w,1,d2 );
+ d2 -= 2;
+ this.fwd( ).up( );
+ c++;
+ }
+ this.down(c ).back(c );
+ }
+ return this;
+};
+//
+// prism0 private implementation
+//
+function prism0( block,w,d ) {
+ this
+ .stairs(block,w,d/2)
+ .fwd(d-1)
+ .right(w-1)
+ .turn(2)
+ .stairs(block,w,d/2)
+ .turn(2)
+ .left(w-1)
+ .back(d-1);
+
+ var se = STAIRBLOCKS[block];
+ if (se) {
+ this
+ .fwd()
+ .prism(se,1,d-2)
+ .right(w-1)
+ .prism(se,1,d-2)
+ .left(w-1)
+ .back();
+ }
+}
+module.exports = function(Drone){
+ Drone.extend(prism0);
+ Drone.extend(prism);
+};
diff --git a/src/main/js/modules/drone/rand.js b/src/main/js/modules/drone/rand.js
new file mode 100644
index 000000000..8a7ec1ab2
--- /dev/null
+++ b/src/main/js/modules/drone/rand.js
@@ -0,0 +1,67 @@
+'use strict';
+/*global require, module*/
+/************************************************************************
+### Drone.rand() method
+
+rand takes either an array (if each blockid has the same chance of occurring) or an object where each property is a blockid and the value is it's weight (an integer)
+
+#### Example
+
+place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked)
+
+ rand( [blocks.brick.stone, blocks.brick.mossy, blocks.brick.cracked ],w,d,h)
+
+to place random blocks stone has a 50% chance of being picked,
+
+ var distribution = {};
+ distribution[ blocks.brick.stone ] = 5;
+ distribution[ blocks.brick.mossy ] = 3;
+ distribution[ blocks.brick.cracked ] = 2;
+
+ rand( distribution, width, height, depth)
+
+regular stone has a 50% chance, mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked.
+
+***/
+//
+// standard fisher-yates shuffle algorithm
+//
+function fisherYates( myArray ) {
+ var i = myArray.length;
+ if ( i == 0 ) return false;
+ while ( --i ) {
+ var j = Math.floor( Math.random( ) * ( i + 1 ) );
+ var tempi = myArray[i];
+ var tempj = myArray[j];
+ myArray[i] = tempj;
+ myArray[j] = tempi;
+ }
+}
+function _rand( blockDistribution ) {
+ if ( !(blockDistribution.constructor == Array ) ) {
+ var a = [];
+ for ( var p in blockDistribution ) {
+ var n = blockDistribution[p];
+ for ( var i = 0;i < n;i++ ) {
+ a.push(p );
+ }
+ }
+ blockDistribution = a;
+ }
+ while ( blockDistribution.length < 1000 ) {
+ // make array bigger so that it's more random
+ blockDistribution = blockDistribution.concat(blockDistribution );
+ }
+ fisherYates(blockDistribution );
+ return blockDistribution;
+}
+function rand( dist, width, height, depth, overwrite ) {
+ if ( typeof overwrite == 'undefined' ) {
+ overwrite = true;
+ }
+ var randomized = _rand( dist );
+ this.boxa( randomized, width, height, depth, overwrite);
+}
+module.exports = function(Drone){
+ Drone.extend( rand );
+};
diff --git a/src/main/js/modules/drone/sign.js b/src/main/js/modules/drone/sign.js
new file mode 100644
index 000000000..8d84a7f56
--- /dev/null
+++ b/src/main/js/modules/drone/sign.js
@@ -0,0 +1,144 @@
+'use strict';
+/*global require, echo,__plugin, module*/
+var blocks = require('blocks');
+/************************************************************************
+### Drone.wallsign() method
+
+Creates a wall sign (A sign attached to a wall)
+
+#### Parameters
+
+ * message - can be a string or an array of strings
+
+#### Example
+
+ drone.wallsign(['Welcome','to','Scriptopia']);
+
+
+
+### Drone.signpost() method
+
+Creates a free-standing signpost
+
+#### Parameters
+
+ * message - can be a string or an array of strings
+
+#### Example
+
+ drone.signpost(['Hello','World']);
+
+
+
+### Drone.sign() method
+
+Deprecated: Use signpost() or wallsign() methods instead.
+
+Signs must use block 63 (stand-alone signs) or 68 (signs on walls)
+
+#### Parameters
+
+ * message - can be a string or an array of strings.
+ * block - can be 63 or 68
+
+#### Example
+
+To create a free-standing sign...
+
+ drone.sign(["Hello","World"], blocks.sign_post);
+
+
+
+... to create a wall mounted sign...
+
+ drone.sign(["Welcome","to","Scriptopia"], blocks.sign );
+
+
+
+***/
+function putSign( drone, texts, blockId, meta ) {
+ var i,
+ len = texts.length,
+ block,
+ state,
+ getState,
+ isSign,
+ setLine;
+
+ if ( blockId != blocks.sign_post && blockId != blocks.sign ) {
+ throw new Error( 'Invalid Parameter: blockId must be blocks.sign_post or blocks.sign' );
+ }
+ block = drone.setBlock( blockId, meta);
+ if (__plugin.canary){
+ isSign = function(block){
+ var sign = block.getTileEntity();
+ return sign.setTextOnLine;
+ };
+ setLine = function( block, i) {
+ var sign = block.getTileEntity();
+ sign.setTextOnLine( texts[i], i );
+ sign.update();
+ };
+ }
+ if (__plugin.bukkit){
+ isSign = function(block){ return block.state && block.state.setLine; };
+ setLine = function( block, i) {
+ var sign = block.state;
+ sign.setLine( i, texts[i] );
+ sign.update(true);
+ };
+ }
+ if ( isSign(block) ) {
+ if (len > 4){
+ len = 4;
+ }
+ for ( i = 0; i < len; i++ ) {
+ setLine(block, i, texts[ i ] );
+ }
+ }
+};
+function signpost( message ){
+ this.then(function(){
+ this.sign(message, blocks.sign_post);
+ });
+}
+function wallsign( message ){
+ /*
+ must allow for /js wallsign() while looking at a wall block
+ */
+ this.then(function(){
+ var block = this.getBlock();
+ if (block.typeId == blocks.air || block.typeId == blocks.sign){
+ this.sign(message, blocks.sign);
+ } else {
+ this
+ .back()
+ .sign(message, blocks.sign)
+ .fwd();
+ }
+});
+
+}
+function sign( message, block ) {
+ if ( message.constructor != Array ) {
+ message = [message];
+ }
+ var bm = this.getBlockIdAndMeta( block );
+ block = bm[0];
+ var meta = bm[1];
+ if ( block !== blocks.sign_post && block !== blocks.sign ) {
+ var usage = 'Usage: sign("message", blocks.sign_post) or sign("message", blocks.sign)';
+ if ( this.player ) {
+ echo( this.player, usage);
+ }
+ console.error(usage);
+ return;
+ }
+ putSign( this, message, block, meta);
+
+}
+module.exports = function(Drone){
+ Drone.extend(sign);
+ Drone.extend(signpost);
+ Drone.extend(wallsign);
+};
diff --git a/src/main/js/plugins/drone/sphere.js b/src/main/js/modules/drone/sphere.js
similarity index 92%
rename from src/main/js/plugins/drone/sphere.js
rename to src/main/js/modules/drone/sphere.js
index bffea0a7d..c8056548b 100644
--- a/src/main/js/plugins/drone/sphere.js
+++ b/src/main/js/modules/drone/sphere.js
@@ -1,5 +1,5 @@
-var Drone = require('./drone').Drone;
-
+'use strict';
+/*global module*/
/************************************************************************
### Drone.sphere() method
@@ -22,11 +22,11 @@ Spheres are time-consuming to make. You *can* make large spheres (250 radius) bu
server to be very busy for a couple of minutes while doing so.
***/
-Drone.extend( 'sphere', function( block, radius ) {
+function sphere( block, radius ) {
var lastRadius = radius,
slices = [ [ radius , 0 ] ],
diameter = radius * 2,
- bm = this._getBlockIdAndMeta( block ),
+ bm = this.getBlockIdAndMeta( block ),
r2 = radius * radius,
i = 0,
newRadius,
@@ -83,7 +83,7 @@ Drone.extend( 'sphere', function( block, radius ) {
.down( v );
}
return this.move( 'sphere' );
-});
+}
/************************************************************************
### Drone.sphere0() method
@@ -104,12 +104,12 @@ Spheres are time-consuming to make. You *can* make large spheres (250 radius) bu
server to be very busy for a couple of minutes while doing so.
***/
-Drone.extend('sphere0', function(block,radius)
+function sphere0(block,radius)
{
var lastRadius = radius,
slices = [ [ radius, 0 ] ],
diameter = radius * 2,
- bm = this._getBlockIdAndMeta( block ),
+ bm = this.getBlockIdAndMeta( block ),
r2 = radius*radius,
i,
newRadius,
@@ -185,7 +185,7 @@ Drone.extend('sphere0', function(block,radius)
return this;
-});
+}
/************************************************************************
### Drone.hemisphere() method
@@ -206,11 +206,11 @@ To create a wood 'north' hemisphere with a radius of 7 blocks...

***/
-Drone.extend( 'hemisphere', function( block, radius, northSouth ) {
+function hemisphere( block, radius, northSouth ) {
var lastRadius = radius,
slices = [ [ radius, 0 ] ],
diameter = radius * 2,
- bm = this._getBlockIdAndMeta(block),
+ bm = this.getBlockIdAndMeta(block),
r2 = radius * radius,
i = 0,
newRadius;
@@ -260,7 +260,7 @@ Drone.extend( 'hemisphere', function( block, radius, northSouth ) {
}
}
return this.move( 'hsphere' );
-});
+}
/************************************************************************
### Drone.hemisphere0() method
@@ -281,22 +281,16 @@ To create a glass 'north' hemisphere with a radius of 20 blocks...

***/
-Drone.extend( 'hemisphere0', function( block, radius, northSouth ) {
+function hemisphere0( block, radius, northSouth ) {
if ( radius > 255 ) {
throw new Error('Hemisphere radius must be less than 256 blocks');
}
-/*
- return this.hemisphere( block, radius, northSouth)
- .fwd().right().up( northSouth == 'north' ? 0 : 1 )
- .hemisphere( 0, radius-1, northSouth )
- .back().left().down( northSouth == 'north' ? 0 : 1 );
-*/
var lastRadius = radius,
slices = [ [ radius, 0 ] ],
diameter = radius * 2,
- bm = this._getBlockIdAndMeta(block),
+ bm = this.getBlockIdAndMeta(block),
r2 = radius * radius,
i = 0,
len,
@@ -393,4 +387,10 @@ Drone.extend( 'hemisphere0', function( block, radius, northSouth ) {
}
return this.move( 'hsphere0' );
-});
+}
+module.exports = function(Drone){
+ Drone.extend( sphere );
+ Drone.extend( sphere0 );
+ Drone.extend( hemisphere );
+ Drone.extend( hemisphere0 );
+};
diff --git a/src/main/js/modules/drone/stairs.js b/src/main/js/modules/drone/stairs.js
new file mode 100644
index 000000000..519ee1ee2
--- /dev/null
+++ b/src/main/js/modules/drone/stairs.js
@@ -0,0 +1,61 @@
+'use strict';
+/*global module*/
+/**************************************************************************
+### Drone.stairs() function
+
+The stairs() function will build a flight of stairs
+
+#### Parameters
+
+ * blockType - should be one of the following:
+
+ * blocks.stairs.oak
+ * blocks.stairs.cobblestone
+ * blocks.stairs.brick
+ * blocks.stairs.stone
+ * blocks.stairs.nether
+ * blocks.stairs.sandstone
+ * blocks.stairs.spruce
+ * blocks.stairs.birch
+ * blocks.stairs.jungle
+ * blocks.stairs.quartz
+
+ * width - The width of the staircase - default is 1
+ * height - The height of the staircase - default is 1
+
+#### Example
+
+To build an oak staircase 3 blocks wide and 5 blocks tall:
+
+ /js stairs(blocks.stairs.oak, 3, 5)
+
+Staircases do not have any blocks beneath them.
+
+***/
+var blocks = require('blocks');
+/*global require*/
+function stairs(blockType, width, height){
+ if (typeof width === 'undefined')
+ width = 1;
+ if (typeof height === 'undefined')
+ height = 1;
+ if (typeof blockType === 'undefined'){
+ blockType = blocks.stairs.oak;
+ }
+ var bm = this.getBlockIdAndMeta(blockType);
+ this.then(function(){
+ this.chkpt('_stairs');
+ while (height > 0) {
+ this.traverseWidth(width, function(){
+ this.setBlock(bm[0], bm[1]);
+ });
+
+ this.fwd().up();
+ height -= 1;
+ }
+ this.move('_stairs');
+ });
+}
+module.exports = function(Drone){
+ Drone.extend(stairs);
+};
diff --git a/src/main/js/plugins/drone/test.js b/src/main/js/modules/drone/test.js
similarity index 60%
rename from src/main/js/plugins/drone/test.js
rename to src/main/js/modules/drone/test.js
index 5057151a1..5b870b854 100644
--- a/src/main/js/plugins/drone/test.js
+++ b/src/main/js/modules/drone/test.js
@@ -1,6 +1,6 @@
-var Drone = require('./drone').Drone;
-
-Drone.prototype.testHorizontalStrokeWidth = function(){
+'use strict';
+/*global module*/
+function testHorizontalStrokeWidth(){
this.arc({
blockType: 42,
meta: 0,
@@ -9,9 +9,8 @@ Drone.prototype.testHorizontalStrokeWidth = function(){
strokeWidth: 3,
quadrants: {topright:true,topleft:true,bottomleft:true,bottomright:true}
});
-};
-
-Drone.prototype.testVerticalStrokeWidth = function(){
+}
+function testVerticalStrokeWidth(){
this.arc({
blockType: 42,
meta: 0,
@@ -20,4 +19,8 @@ Drone.prototype.testVerticalStrokeWidth = function(){
strokeWidth: 3,
quadrants: {topright:true,topleft:true,bottomleft:true,bottomright:true}
});
+}
+module.exports = function(Drone){
+ Drone.prototype.testHorizontalStrokeWidth = testHorizontalStrokeWidth;
+ Drone.prototype.testVerticalStrokeWidth = testVerticalStrokeWidth;
};
diff --git a/src/main/js/modules/drone/trees.js b/src/main/js/modules/drone/trees.js
new file mode 100644
index 000000000..797c418e9
--- /dev/null
+++ b/src/main/js/modules/drone/trees.js
@@ -0,0 +1,99 @@
+'use strict';
+/*global require, __plugin, Packages, org, echo, module */
+var blocks = require('blocks');
+/************************************************************************
+### Drone Trees methods
+
+ * oak()
+ * spruce()
+ * birch()
+ * jungle()
+
+#### Example
+
+To create 4 trees in a row, point the cross-hairs at the ground then type `/js ` and ...
+
+ up( ).oak( ).right(8 ).spruce( ).right(8 ).birch( ).right(8 ).jungle( );
+
+Trees won't always generate unless the conditions are right. You
+should use the tree methods when the drone is directly above the
+ground. Trees will usually grow if the drone's current location is
+occupied by Air and is directly above an area of grass (That is why
+the `up()` method is called first).
+
+
+
+None of the tree methods require parameters. Tree methods will only be
+successful if the tree is placed on grass in a setting where trees can
+grow.
+
+***/
+function bukkitTreeFactory( k, v ) {
+ return function( ) {
+ var block = this.getBlock();
+ if ( block.typeId == blocks.grass ) {
+ this.up( );
+ }
+ var treeLoc = this.getLocation();
+ var successful = treeLoc.world.generateTree(treeLoc,v );
+ if ( block.typeId == blocks.grass ) {
+ this.down( );
+ }
+ };
+}
+function canaryTreeFactory( k, v ){
+ return function(){
+ var block = this.getBlock();
+ if ( block.typeId == blocks.grass ) {
+ this.up( );
+ }
+ var treeLoc = this.getLocation();
+ if (!treeLoc.world.generateTree){
+ var msg = k + '() is not supported in this version';
+ if (this.player){
+ echo(this.player, msg);
+ }
+ console.log(msg);
+ return;
+ }
+ var cmTreeType = Packages.net.canarymod.api.world.TreeType;
+ var trees = {
+ oak: cmTreeType.BIGOAK,
+ birch: cmTreeType.BIRCH,
+ jungle: cmTreeType.JUNGLE,
+ spruce: cmTreeType.SPRUCE
+ };
+
+ var successful = treeLoc.world.generateTree(treeLoc, trees[k] );
+ if ( block.typeId == blocks.grass ) {
+ this.down( );
+ }
+ };
+}
+module.exports = function (Drone){
+ var trees = {
+ oak: null,
+ birch: null,
+ jungle: null,
+ spruce: null
+ };
+ var p;
+ if (__plugin.canary){
+ for (p in trees ) {
+ Drone.extend(p, canaryTreeFactory ( p, trees[p] ) );
+ }
+ }
+ if (__plugin.bukkit){
+ var bkTreeType = org.bukkit.TreeType;
+ trees = {
+ oak: bkTreeType.BIG_TREE ,
+ birch: bkTreeType.BIRCH ,
+ jungle: bkTreeType.JUNGLE,
+ spruce: bkTreeType.REDWOOD
+ };
+ for (p in trees ) {
+ Drone.extend(p, bukkitTreeFactory ( p, trees[p] ) );
+ }
+ }
+};
+
diff --git a/src/main/js/modules/entities.js b/src/main/js/modules/entities.js
new file mode 100644
index 000000000..0b74a642b
--- /dev/null
+++ b/src/main/js/modules/entities.js
@@ -0,0 +1,38 @@
+'use strict';
+/*global __plugin, org, Packages, module, exports*/
+var entities = {},
+ entitytypes,
+ t, i, name;
+if (__plugin.bukkit) {
+ entitytypes = org.bukkit.entity.EntityType.values();
+}
+if (__plugin.canary) {
+ entitytypes = Packages.net.canarymod.api.entity.EntityType.values();
+}
+function getEntityHandler( entityType ) {
+ return function( entity ){
+ if (arguments.length == 0){
+ return entityType;
+ }
+ if (arguments.length == 1){
+ if (entity){
+ if (__plugin.bukkit){
+ return entity.type == entityType;
+ }
+ if (__plugin.canary){
+ return entity.entityType == entityType;
+ }
+ }
+ }
+ return null;
+ };
+}
+for (t in entitytypes) {
+ if (entitytypes[t] && entitytypes[t].ordinal) {
+ name = ('' + entitytypes[t].name()).replace(/^(.*)/,function(a){
+ return a.toLowerCase();
+ });
+ entities[name] = getEntityHandler(entitytypes[t]);
+ }
+}
+module.exports = entities;
diff --git a/src/main/js/modules/fireworks.js b/src/main/js/modules/fireworks.js
new file mode 100644
index 000000000..b8495b1b3
--- /dev/null
+++ b/src/main/js/modules/fireworks.js
@@ -0,0 +1,42 @@
+/************************************************************************
+## Fireworks Module
+
+The fireworks module makes it easy to create fireworks using
+ScriptCraft. The module has a single function `firework` which takes
+a `org.bukkit.Location` as its 1 and only parameter.
+
+### Examples
+
+The module also extends the `Drone` object adding a `firework` method
+so that fireworks can be created as a part of a Drone chain. For
+Example....
+
+ /js firework()
+
+... creates a single firework, while ....
+
+ /js firework().fwd(3).times(5)
+
+... creates 5 fireworks in a row. Fireworks have also been added as a
+possible option for the `arrow` module. To have a firework launch
+where an arrow strikes...
+
+ /js arrows.firework()
+
+To call the fireworks.firework() function directly, you must provide a
+location. For example...
+
+ /js var fireworks = require('fireworks');
+ /js fireworks.firework( self.location );
+
+
+
+***/
+
+if ( __plugin.canary ) {
+ exports.firework = require('./canary/fireworks');
+} else {
+ exports.firework = require('./bukkit/fireworks');
+}
+
+
diff --git a/src/main/js/modules/fireworks/package.json b/src/main/js/modules/fireworks/package.json
deleted file mode 100644
index 99595d8c0..000000000
--- a/src/main/js/modules/fireworks/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- name: 'fireworks',
- main: './fireworks.js'
-}
diff --git a/src/main/js/modules/http/request.js b/src/main/js/modules/http/request.js
index 713d69bdc..adbe9cb6f 100644
--- a/src/main/js/modules/http/request.js
+++ b/src/main/js/modules/http/request.js
@@ -28,57 +28,81 @@ main thread of execution.
The following example illustrates how to use http.request to make a request to a JSON web service and evaluate its response...
- var jsResponse;
- var http = require('./http/request');
- http.request("http://scriptcraftjs.org/sample.json",function(responseCode, responseBody){
- jsResponse = eval("(" + responseBody + ")");
- });
-
-... The following example illustrates a more complex use-case POSTing parameters to a CGI process on a server...
+```javascript
+var jsResponse;
+var http = require('http');
+http.request('http://scriptcraftjs.org/sample.json',function(responseCode, responseBody){
+ jsResponse = JSON.parse( responseBody );
+});
+```
+The following example illustrates a more complex use-case POSTing parameters to a CGI process on a server...
- var http = require('./http/request');
- http.request(
- {
- url: 'http://pixenate.com/pixenate/pxn8.pl',
- method: 'POST',
- params: {script: '[]'}
- },
- function( responseCode, responseBody ) {
- var jsObj = eval('(' + responseBody + ')');
- });
+```javascript
+var http = require('http');
+http.request( {
+ url: 'http://pixenate.com/pixenate/pxn8.pl',
+ method: 'POST',
+ params: {script: '[]'}
+ },
+ function( responseCode, responseBody ) {
+ var jsObj = JSON.parse( responseBody );
+ });
+```
***/
-exports.request = function( request, callback ) {
- var paramsToString = function( params ) {
- var result = '',
+
+/*global exports, encodeURI, server, __plugin, setTimeout*/
+function paramsToString( params ) {
+ var result = '',
paramNames = [],
i;
- for ( i in params ) {
- paramNames.push( i );
- }
- for ( i = 0; i < paramNames.length; i++ ) {
- result += paramNames[i] + '=' + encodeURI( params[ paramNames[i] ] );
- if ( i < paramNames.length-1 )
- result += '&';
- }
- return result;
- };
-
- server.scheduler.runTaskAsynchronously( __plugin, function() {
+ for ( i in params ) {
+ paramNames.push( i );
+ }
+ for ( i = 0; i < paramNames.length; i++ ) {
+ result += paramNames[i] + '=' + encodeURI( params[ paramNames[i] ] );
+ if ( i < paramNames.length-1 )
+ result += '&';
+ }
+ return result;
+}
+function invokeNow( fn ){
+ if (__plugin.bukkit){
+ server.scheduler.runTask( __plugin, fn);
+ return;
+ }
+ if (__plugin.canary){
+ fn();
+ return;
+ }
+}
+function invokeLater( fn ){
+ if (__plugin.bukkit){
+ server.scheduler.runTaskAsynchronously( __plugin, fn);
+ return;
+ }
+ if (__plugin.canary){
+ setTimeout(fn,20);
+ return;
+ }
+}
+exports.request = function( request, callback ) {
+ invokeLater( function() {
var url, paramsAsString, conn, requestMethod;
if (typeof request === 'string'){
url = request;
requestMethod = 'GET';
}else{
+ url = request.url;
paramsAsString = paramsToString( request.params );
if ( request.method ) {
- requestMethod = request.method;
+ requestMethod = request.method;
} else {
- requestMethod = 'GET';
+ requestMethod = 'GET';
}
if ( requestMethod == 'GET' && request.params ) {
- // append each parameter to the URL
- url = request.url + '?' + paramsAsString;
+ // append each parameter to the URL
+ url = request.url + '?' + paramsAsString;
}
}
conn = new java.net.URL( url ).openConnection();
@@ -89,11 +113,11 @@ exports.request = function( request, callback ) {
if ( conn.requestMethod == 'POST' ) {
conn.doInput = true;
// put each parameter in the outputstream
- conn.setRequestProperty('Content-Type', 'application/x-www-form-urlencoded');
+ conn.setRequestProperty('Content-Type', 'application/x-www-form-urlencoded');
conn.setRequestProperty('charset', 'utf-8');
conn.setRequestProperty('Content-Length', '' + paramsAsString.length);
conn.useCaches =false ;
- wr = new java.io.DataOutputStream(conn.getOutputStream ());
+ var wr = new java.io.DataOutputStream(conn.getOutputStream ());
wr.writeBytes(paramsAsString);
wr.flush();
wr.close();
@@ -105,8 +129,9 @@ exports.request = function( request, callback ) {
stream = conn.getInputStream();
response = new java.util.Scanner( stream ).useDelimiter("\\A").next();
}
- server.scheduler.runTask( __plugin, function( ) {
+ invokeNow( function( ) {
callback( rc, response );
});
});
+
};
diff --git a/src/main/js/modules/input.js b/src/main/js/modules/input.js
index 49410853f..20d75814f 100644
--- a/src/main/js/modules/input.js
+++ b/src/main/js/modules/input.js
@@ -20,20 +20,20 @@ This new `input()` function is best illustrated by example. The following code i
var input = require('input');
exports.numberguess = function(player){
var randomNumber = Math.ceil(Math.random() * 10);
- input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, repeat ) {
+ input( player, 'Think of a number between 1 and 10 (q to quit)', function( guess, guesser, repeat ) {
if ( guess == 'q'){
return;
}
if ( +guess !== randomNumber ) {
if (+guess < randomNumber ) {
- player.sendMessage('Too low - guess again');
+ echo( guesser, 'Too low - guess again');
}
if (+guess > randomNumber ) {
- player.sendMessage('Too high - guess again');
+ echo( guesser, 'Too high - guess again');
}
repeat();
} else {
- player.sendMessage('You guessed correctly');
+ echo( guesser, 'You guessed correctly');
}
});
};
@@ -50,37 +50,14 @@ The callback is bound to an object which has the following properties:
The callback function as well as being bound to an object with the above properties (so you can use this.value inside your callback to get the value which has just been input), can also take the following parameters (in exact order):
* value
- * repeat
* sender
+ * repeat
The `value` parameter will be the same as `this.value`, the `repeat` parameter will be the same as `this.repeat` and so on.
***/
-
-var bkPrompt = org.bukkit.conversations.Prompt,
- bkConversationFactory = org.bukkit.conversations.ConversationFactory;
-
-function asyncInput( sender, promptMesg, callback) {
- var repeat = function(){
- asyncInput( sender, promptMesg, callback);
- };
- var prompt = new bkPrompt( ) {
- getPromptText: function( ctx ) {
- return promptMesg;
- },
- acceptInput: function( ctx, value ) {
- callback.apply( { repeat: repeat, sender: sender, message: promptMesg, value: value },
- [value, repeat, sender]);
- return null;
- },
- blocksForInput: function( ctx ) {
- return true;
- }
- };
- new bkConversationFactory( __plugin )
- .withModality( true )
- .withFirstPrompt( prompt )
- .buildConversation( sender )
- .begin( );
+if (__plugin.canary) {
+ module.exports = require('./canary/input');
+} else {
+ module.exports = require('./bukkit/input');
}
-module.exports = asyncInput;
diff --git a/src/main/js/modules/inventory/index.js b/src/main/js/modules/inventory/index.js
new file mode 100644
index 000000000..72583dc1e
--- /dev/null
+++ b/src/main/js/modules/inventory/index.js
@@ -0,0 +1,43 @@
+/************************************************************************
+## Inventory Module
+This module provides functions to add items to, remove items from and check the
+contents of a player or NPC's inventory.
+
+### Usage
+The inventory module is best used in conjunction with the items module. See below for examples of usage.
+
+```javascript
+var inventory = require('inventory');
+var items = require('items');
+var utils = require('utils');
+
+// gives every player a cookie and a baked potatoe
+utils.players(function(player){
+ inventory(player)
+ .add( items.cookie(1) )
+ .add( items.bakedPotato(1) )
+});
+
+// give a player 6 cookies then take away 4 of them
+
+inventory(player)
+ .add( items.cookie(6) )
+ .remove ( items.cookie(4) )
+
+// check if a player has any cookies
+
+var hasCookies = inventory(player).contains( items.cookie(1) );
+
+```
+The inventory module exposes a single function which when passed a player or NPC will return an object with 3 methods:
+
+* add : Adds items to the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
+* remove : removes items from the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
+* contains : checks to see if there is the specified type and amount of item in the inventory (Expects parameters of type `net.canarymod.api.inventory.Item` - I strongly recommend using the `items` module for constructing items)
+
+***/
+if ( __plugin.canary ) {
+ module.exports = require('../canary/inventory');
+} else {
+ module.exports = require('../bukkit/inventory');
+}
diff --git a/src/main/js/modules/items.js b/src/main/js/modules/items.js
new file mode 100644
index 000000000..c5ae4cc0f
--- /dev/null
+++ b/src/main/js/modules/items.js
@@ -0,0 +1,5 @@
+if (__plugin.canary) {
+ module.exports = require('./canary/items');
+} else {
+ module.exports = require('./bukkit/items');
+}
diff --git a/src/main/js/modules/lightning.js b/src/main/js/modules/lightning.js
new file mode 100644
index 000000000..b1e8a4166
--- /dev/null
+++ b/src/main/js/modules/lightning.js
@@ -0,0 +1,30 @@
+'use strict';
+/************************************************************************
+## Lightning module
+
+Causes a bolt of lightning to strike.
+
+### Usage
+```javascript
+// strike lightning wherever a player's arrow lands
+var lightning = require('lightning');
+events.projectileHit( function( event ){
+ if ( entities.arrow( event.projectile ) // it's an arrow
+ && entities.player( event.projectile.owner ) // it was shot by a player
+ ) {
+ lightning( event.projectile ); // strike lightning at the arrow location
+ }
+});
+```
+
+***/
+module.exports = function lightning( something ) {
+ if (__plugin.canary && something.location){
+ return something.location.world.makeLightningBolt(something.location);
+ }
+ if (__plugin.bukkit && something.location){
+ return something.location.world.strikeLightning(something.location);
+ }
+ console.log('Need an object with a location property for lightning strike');
+ return null;
+};
diff --git a/src/main/js/modules/minigames/scoreboard.js b/src/main/js/modules/minigames/scoreboard.js
index 7fc4caee3..6fc34f436 100644
--- a/src/main/js/modules/minigames/scoreboard.js
+++ b/src/main/js/modules/minigames/scoreboard.js
@@ -1,50 +1,70 @@
-var bkDisplaySlot = org.bukkit.scoreboard.DisplaySlot;
+'use strict';
+/*global Packages, require, server, exports*/
+
/*
- The scoreboard is a simple wrapper around the Bukkit Scoreboard API.
- It's only concerned with display of scores, not maintaining them - that's the game's job.
+ wph 20150103 - temporarily commenting out - textcolors was removed.
+ var textcolors = require('textcolors');
*/
-module.exports = function( options ) {
- var temp = {};
- var ccScoreboard;
-
- return {
- start: function( ) {
- var objective,
- slot,
- ccObj;
- ccScoreboard = server.scoreboardManager.getNewScoreboard();
- for ( objective in options ) {
- ccObj = ccScoreboard.registerNewObjective( objective, 'dummy' );
- for ( slot in options[ objective ] ) {
- ccObj.displaySlot = bkDisplaySlot[ slot ];
- ccObj.displayName = options[ objective ][ slot ];
- }
- }
- },
- stop: function(){
- var objective, slot;
- for ( objective in options ) {
- ccScoreboard.getObjective(objective).unregister();
- for ( slot in options[ objective ] ) {
- ccScoreboard.clearSlot( bkDisplaySlot[ slot ] );
- }
- }
- },
- update: function( objective, player, score ) {
- if ( player.scoreboard && player.scoreboard != ccScoreboard ) {
- temp[player.name] = player.scoreboard;
- player.scoreboard = ccScoreboard;
- }
- ccScoreboard
- .getObjective( objective )
- .getScore( player )
- .score = score;
- },
- restore: function( player ) {
- // offlineplayers don't have a scoreboard
- if ( player.scoreboard ) {
- player.scoreboard = temp[ player.name ];
- }
+var sb;
+if (__plugin.canary){
+ var Canary = Packages.net.canarymod.Canary;
+ sb = Canary.scoreboards().getScoreboard();
+} else {
+ console.warn('Scoreboard not yet supported in CraftBukkit');
+ return;
+}
+function execCommand( command ){
+ server.executeVanillaCommand(server, command);
+}
+function getTeamByName( teamName ){
+ var allTeams = sb.getTeams().toArray();
+ for (var i = 0;i < allTeams.length; i++){
+ if (allTeams[i].displayName == teamName){
+ return allTeams[i];
}
- };
-};
+ }
+ return null;
+}
+function createScoreboard( objectiveName, displayName ){
+ execCommand('scoreboard objectives add ' + objectiveName + ' dummy ' + displayName);
+ execCommand('scoreboard objectives setdisplay sidebar ' + objectiveName);
+}
+function addTeamToScoreboard( teamName, color){
+ execCommand('scoreboard teams add ' + teamName);
+ var team = getTeamByName( teamName );
+ /*
+ wph 20150103 - temporarily commenting out - textcolors was removed.
+ team.prefix = textcolors.colorize(color, '');
+ */
+ //execCommand('scoreboard teams option ' + teamName + ' color ' + color);
+}
+function removeScoreboard( name ){
+ //execCommand('scoreboard objectives remove ' + name );
+ sb['removeScoreObjective(String)'](name);
+}
+function addPlayerToTeam( objectiveName, teamName, playerName ){
+ execCommand('scoreboard teams join ' + teamName + ' ' + playerName);
+ execCommand('scoreboard players set ' + playerName + ' ' + objectiveName + ' -1');
+ updatePlayerScore( objectiveName, playerName, 0);
+}
+
+function updatePlayerScore( objectiveName, playerName, score ){
+ /*
+ wph 20150801 - this fails with CanaryMod 1.8.2 so use command instead - messy for ops but non-ops won't see messages
+
+ var sc = sb['getScore(String, ScoreObjective)']( playerName, sb.getScoreObjective( objectiveName) );
+ sc.score = score;
+ */
+ execCommand('scoreboard players set ' + playerName + ' ' + objectiveName + ' ' + score);
+}
+
+function removeTeamFromScoreboard( teamName ){
+ execCommand('scoreboard teams remove ' + teamName);
+ //sb['removeTeam(String)'](teamName);
+}
+exports.create = createScoreboard;
+exports.addTeam = addTeamToScoreboard;
+exports.removeTeam = removeTeamFromScoreboard;
+exports.addPlayerToTeam = addPlayerToTeam;
+exports.updateScore = updatePlayerScore;
+exports.remove = removeScoreboard;
diff --git a/src/main/js/modules/recipes.js b/src/main/js/modules/recipes.js
new file mode 100644
index 000000000..ccefbb48e
--- /dev/null
+++ b/src/main/js/modules/recipes.js
@@ -0,0 +1,34 @@
+/*************************************************************************
+## The recipes module
+
+The Recipes module provides convenience functions for adding and removing recipes
+from the game.
+
+### Example
+To add an EnderBow to the game (assumes there's an enchanted Item variable called enderBow)...
+
+ var recipes = require('recipes');
+ var items = require('items');
+ ...
+ var enderBowRecipe = recipes.create( {
+ result: enderBow,
+ ingredients: {
+ E: items.enderPearl(1),
+ S: items.stick(1),
+ W: items.string(1)
+ },
+ shape: [ 'ESW',
+ 'SEW',
+ 'ESW' ]
+ } );
+ // add to server
+ var addedRecipe = server.addRecipe( enderBowRecipe );
+ // to remove...
+ server.removeRemove( addedRecipe );
+
+***/
+if (__plugin.canary) {
+ module.exports = require('./canary/recipes');
+} else {
+ module.exports = require('./bukkit/recipes');
+}
diff --git a/src/main/js/modules/signs/menu.js b/src/main/js/modules/signs/menu.js
index a792a76fa..02a43c5ee 100644
--- a/src/main/js/modules/signs/menu.js
+++ b/src/main/js/modules/signs/menu.js
@@ -1,28 +1,32 @@
+'use strict';
+/*global events, require, org, module, persist, __plugin*/
var utils = require('utils'),
stringExt = require('utils/string-exts'),
- _store = {},
- bkBukkit = org.bukkit.Bukkit,
- bkSign = org.bukkit.block.Sign;
+ store = persist('signs',{});
/*
Define the signs module - signs are persistent
(that is - a menu sign will still be a menu after the
server has shut down and started up) plugins now have persistent state - Yay!
*/
-var signs = plugin("signs", {
- /*
- construct an interactive menu which can then be attached to a Sign.
- */
- menu: function(
- /* String */ label,
- /* Array */ options,
- /* Function */ onInteract,
- /* Number */ defaultSelection ){},
- store: _store
- },
- true);
+var signs = { };
+var hasSign = null;
+module.exports = function(hs){
+ hasSign = hs;
+ return signs;
+};
-module.exports = signs;
+var setLine = null;
+if (__plugin.canary){
+ setLine = function(sign, i, text){
+ sign.setTextOnLine( text, i);
+ };
+}
+if (__plugin.bukkit){
+ setLine = function(sign, i, text){
+ sign.setLine( i, text);
+ };
+}
/*
redraw a menu sign
@@ -41,9 +45,14 @@ var _redrawMenuSign = function( p_sign, p_selectedIndex, p_displayOptions ) {
if ( offset+i == p_selectedIndex ) {
text = ('' + text).replace(/^ /,">");
}
- p_sign.setLine( i+1, text );
+ setLine(p_sign, i+1, text);
+ }
+ if (__plugin.canary){
+ p_sign.update();
+ }
+ if (__plugin.bukkit){
+ p_sign.update( true );
}
- p_sign.update( true );
};
var _updaters = {};
@@ -51,7 +60,9 @@ var _updaters = {};
construct an interactive menu to be subsequently attached to
one or more Signs.
*/
-signs.menu = function( /* String */ label, /* Array */ options, /* Function */ callback, /* Number */ selectedIndex ) {
+signs.menu = signMenu;
+
+function signMenu( label, options, callback, selectedIndex ) {
if ( typeof selectedIndex == "undefined" ) {
selectedIndex = 0;
@@ -76,33 +87,14 @@ signs.menu = function( /* String */ label, /* Array */ options, /* Function */ c
The function returned by signs.menu is for use by admins/ops.
*/
var convertToMenuSign = function(/* Sign */ sign, save) {
- var mouseLoc;
if (typeof save == "undefined") {
save = true;
}
- /*
- @deprecated start
- all calls should explicitly provide a [org.bukkit.block.Sign][buksign] parameter.
- */
- if ( typeof sign == "undefined" ) {
- mouseLoc = utils.getMousePos();
- if ( mouseLoc ) {
- sign = mouseLoc.block.state;
- if ( !( sign && sign.setLine ) ) {
- throw new Error("You must first provide a sign!");
- }
- } else {
- throw new Error("You must first provide a sign!");
- }
- }
- /*
- @deprecated end
- */
//
// per-sign variables go here
//
var cSelectedIndex = selectedIndex;
- sign.setLine( 0, paddedLabel.bold() );
+ setLine(sign, 0, paddedLabel.bold());
var _updateSign = function( p_player, p_sign ) {
cSelectedIndex = ( cSelectedIndex + 1 ) % optLen;
_redrawMenuSign( p_sign, cSelectedIndex, displayOptions );
@@ -136,17 +128,17 @@ signs.menu = function( /* String */ label, /* Array */ options, /* Function */ c
when the server starts up again.
*/
if ( save ) {
- if ( typeof _store.menus == "undefined") {
- _store.menus = {};
+ if ( typeof store.menus == "undefined") {
+ store.menus = {};
}
- var signLocations = _store.menus[label];
+ var signLocations = store.menus[label];
if ( typeof signLocations == "undefined" ) {
- signLocations = _store.menus[label] = [];
+ signLocations = store.menus[label] = [];
}
signLocations.push( menuSignSaveData );
}
return sign;
- };
+ }; // end of convertToMenuSign function
/*
a new sign definition - need to store (in-memory only)
@@ -155,19 +147,16 @@ signs.menu = function( /* String */ label, /* Array */ options, /* Function */ c
world with this same label and make dynamic again.
*/
- if ( _store.menus && _store.menus[label] ) {
- var signsOfSameLabel = _store.menus[ label ];
+ if ( store.menus && store.menus[label] ) {
+ var signsOfSameLabel = store.menus[ label ];
var defragged = [];
var len = signsOfSameLabel.length;
for ( i = 0; i < len; i++ ) {
- var loc = signsOfSameLabel[i];
- var world = bkBukkit.getWorld(loc.world);
- if ( !world ) {
- continue;
- }
- var block = world.getBlockAt( loc.x, loc.y, loc.z );
- if ( block.state instanceof bkSign ) {
- convertToMenuSign( block.state, false );
+ var loc = utils.locationFromJSON(signsOfSameLabel[i]);
+ var block = utils.blockAt(loc);
+ var sign = hasSign(block);
+ if ( sign) {
+ convertToMenuSign( sign, false );
defragged.push( loc );
}
}
@@ -175,29 +164,52 @@ signs.menu = function( /* String */ label, /* Array */ options, /* Function */ c
remove data for signs which no longer exist.
*/
if ( defragged.length != len ) {
- _store.menus[label] = defragged;
+ store.menus[label] = defragged;
}
}
return convertToMenuSign;
};
-//
-// update it every time player interacts with it.
-//
-events.playerInteract( function( event ) {
- /*
- look up our list of menu signs. If there's a matching location and there's
- a sign, then update it.
- */
-
- if ( ! event.clickedBlock.state instanceof bkSign ) {
- return;
- }
- var evtLocStr = utils.locationToString(event.clickedBlock.location);
- var signUpdater = _updaters[evtLocStr];
- if ( signUpdater ) {
- signUpdater( event.player, event.clickedBlock.state );
- }
-});
+/*
+if (__plugin.canary){
+ console.warn('signs/menu is not yet supported in CanaryMod');
+ return;
+}
+*/
+if (__plugin.canary){
+ events.blockRightClick( function( event ){
+ var sign = hasSign(event.blockClicked);
+ if (! sign){
+ // it's not a sign
+ return;
+ }
+ var evtLocStr = utils.locationToString(event.blockClicked.location);
+ var signUpdater = _updaters[evtLocStr];
+ if ( signUpdater ) {
+ signUpdater( event.player, sign);
+ }
+
+ });
+}
+if (__plugin.bukkit){
+ //
+ // update it every time player interacts with it.
+ //
+ events.playerInteract( function( event ) {
+ /*
+ look up our list of menu signs. If there's a matching location and there's
+ a sign, then update it.
+ */
+ var sign = hasSign(event.clickedBlock);
+ if ( ! sign ) {
+ return;
+ }
+ var evtLocStr = utils.locationToString(event.clickedBlock.location);
+ var signUpdater = _updaters[evtLocStr];
+ if ( signUpdater ) {
+ signUpdater( event.player, sign );
+ }
+ });
+}
diff --git a/src/main/js/modules/signs/signs.js b/src/main/js/modules/signs/signs.js
index 8851d83c0..5d6d2ab0c 100644
--- a/src/main/js/modules/signs/signs.js
+++ b/src/main/js/modules/signs/signs.js
@@ -1,3 +1,5 @@
+'use strict';
+/*global __plugin, require, module, exports*/
/************************************************************************
## Signs Module
@@ -85,28 +87,40 @@ var signs = require('signs'),
var player = utils.player('tom1234');
var sign = signs.getTargetedBy( player );
if ( !sign ) {
- player.sendMessage('Not looking at a sign');
+ echo( player, 'Not looking at a sign');
}
```
-[buksign]: http://jd.bukkit.org/dev/apidocs/org/bukkit/block/Sign.html
+[buksign]: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/block/Sign.html
+[bukle]: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/LivingEntity.html
***/
+function hasSign( block ){
+ if (__plugin.canary){
+ if (block && block.tileEntity && block.tileEntity.setTextOnLine){
+ return block.tileEntity;
+ }
+ }
+ if (__plugin.bukkit){
+ if (block && block.state && block.state.setLine){
+ return block.state;
+ }
+ }
+ return false;
+}
var utils = require('utils');
-var menu = require('./menu');
+var menu = require('./menu')(hasSign);
// include all menu exports
for ( var i in menu ) {
exports[i] = menu[i];
}
-exports.getTargetedBy = function( livingEntity ) {
+function getTargetedBy( livingEntity ) {
var location = utils.getMousePos( livingEntity );
if ( !location ) {
return null;
}
- var state = location.block.state;
- if ( ! (state || state.setLine) ) {
- return null;
- }
- return state;
-};
+ return hasSign(utils.blockAt(location));
+}
+exports.getTargetedBy = getTargetedBy;
+exports.hasSign = hasSign;
diff --git a/src/main/js/modules/slash.js b/src/main/js/modules/slash.js
new file mode 100644
index 000000000..92e66a7c1
--- /dev/null
+++ b/src/main/js/modules/slash.js
@@ -0,0 +1,65 @@
+'use strict';
+/*global module, require, server, __plugin*/
+var _ = require('underscore');
+/************************************************************************
+## The slash Module
+
+This module provides a single function which makes it easy to execute
+minecraft commands via javascript.
+
+### The slash() function
+
+This function makes it easy to execute one or more minecraft commands.
+
+#### Parameters
+
+ * commands : A String or Array of strings - each string is a command to be executed.
+ * sender: (optional) The player on whose behalf the commands should be executed. If not specified the commands will be executed as the server console user.
+
+#### Examples
+
+Invoke the `/defaultgamemode creative` command (as server).
+
+```javascript
+var slash = require('slash');
+slash('defaultgamemode creative');
+```
+
+Set the time of day to Midday and toggle downfall (as player 'JohnDoe'):
+
+```javascript
+var slash = require('slash'),
+ utils = require('utils');
+var johnDoe = utils.player('John_Doe');
+
+slash([
+ 'time set 6000',
+ 'toggledownfall'
+], johnDoe);
+```
+
+***/
+function slash( commands, sender ){
+ if (_.isArray(commands)){
+ _.each(commands, function(command){
+ slash(command, sender);
+ });
+ return;
+ }
+ if (__plugin.canary){
+ if (sender === server){
+ server.consoleCommand( commands );
+ } else {
+ server.consoleCommand( commands, sender );
+ }
+ }
+ if (__plugin.bukkit){
+ if (!sender){
+ // if sender is not specified assume server console
+ server.dispatchCommand(server.consoleSender, commands);
+ } else {
+ server.dispatchCommand(sender, commands);
+ }
+ }
+}
+module.exports = slash;
diff --git a/src/main/js/modules/sounds.js b/src/main/js/modules/sounds.js
index fca597281..4065cc39e 100644
--- a/src/main/js/modules/sounds.js
+++ b/src/main/js/modules/sounds.js
@@ -1,48 +1,32 @@
-var bkSound = org.bukkit.Sound,
- bkLocation = org.bukkit.Location,
- i = 0,
- allSounds = bkSound.values(),
- len = allSounds.length,
- sound,
- soundName;
-
-for ( ; i < len; i++ ) {
- sound = allSounds[i];
- soundName = '' + sound.name();
- exports[soundName] = sound;
-}
/*************************************************************************
## Sounds Module
This module is a simple wrapper around the Bukkit Sound class and provides
a simpler way to play sounds. All of the org.bukkit.Sound Enum values are attached.
-### Usage:
+### Usage (Bukkit) :
var sounds = require('sounds');
- sounds.play( self, sounds.VILLAGER_NO , 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch
- sounds.play( self, sounds.VILLAGER_NO ); // same as previous statement
+ sounds.play( bukkit.sound.VILLAGER_NO , self, 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch
+ sounds.play( bukkit.sound.VILLAGER_NO , self ); // same as previous statement
The play() function takes either a Location object or any object which has a location.
The volume parameter is in the range 0 to 1 and the pitch parameter is in the range 0 to 4.
+
+In addition, a play function is provided for each possible sound using the following rules:
+
+1. The sound is converted from ALL_CAPS_UNDERSCORE to camelCase so for example there is a sounds.villagerNo() function which will play the VILLAGER_NO sound.
+2. Each such function can take 3 parameters: location (which can be either an actual Location object or an object which has a location), volume and pitch
+3. Or... each such function can be called without parameters meaning the sound will be played for all online players to hear.
+
+ sounds.villagerNo(self, 1, 0); // plays VILLAGER_NO sound at full volume and medium pitch at invoker's location
+
+ sounds.villagerNo(); // plays VILLAGER_NO sound for all players online.
+
+These methods are provided for convenience to help beginners explore sounds using TAB completion.
***/
-exports.play = function(locationOrHasLocation, sound, volume, pitch) {
- var location = null;
- if (!locationOrHasLocation)
- return;
- if (locationOrHasLocation instanceof bkLocation){
- location = locationOrHasLocation;
- } else {
- locationOrHasLocation = locationOrHasLocation.location;
- if (locationOrHasLocation && locationOrHasLocation instanceof bkLocation ){
- location = locationOrHasLocation;
- }
- }
- if (!location)
- return;
- if (!volume)
- volume = 1;
- if (!pitch)
- pitch = 0;
- location.world.playSound(location, sound, volume, pitch);
-};
+if (__plugin.canary) {
+ module.exports = require('./canary/sounds');
+} else {
+ module.exports = require('./bukkit/sounds');
+}
diff --git a/src/main/js/modules/spawn.js b/src/main/js/modules/spawn.js
new file mode 100644
index 000000000..471d9d565
--- /dev/null
+++ b/src/main/js/modules/spawn.js
@@ -0,0 +1,46 @@
+/*global require, module, __plugin, Packages*/
+'use strict';
+var entities = require('entities');
+/************************************************************************
+## Spawn Module
+
+Provides a single function to 'spawn' an entity at a given location.
+
+### Parameters
+
+ * entityType - The type of entity to spawn. This can be a string (see entities module for reference) or a framework-specific object type (see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/entity/EntityType.html). A list of [all possible entities][ents] functions (equivalent to the EntityType enum).
+
+ * location - where the entity should be spawned.
+
+[ents]: #entities-module
+
+### Example
+
+Using the entities module as a helper, spawn a new polar bear at the world's default spawn location:
+
+```javascript
+var entities = require('entities'),
+ spawn = require('spawn');
+...
+var spawnLocation = world.spawnLocation;
+spawn(entities.polar_bear(), spawnLocation);
+```
+
+This module is in turn used by the Drone's `spawn()` method and the `jsp spawn` command.
+***/
+module.exports = function(entityType, location){
+ var entityTypeFn;
+ if (typeof entityType === 'string'){
+ entityTypeFn = entities[entityType.toLowerCase()];
+ entityType = entityTypeFn();
+ }
+ var world = location.world;
+ if (__plugin.bukkit){
+ world.spawnEntity( location, entityType);
+ }
+ if (__plugin.canary){
+ var Canary = Packages.net.canarymod.Canary,
+ entityInstance = Canary.factory().entityFactory.newEntity(entityType, location);
+ entityInstance.spawn();
+ }
+};
diff --git a/src/main/js/modules/teleport.js b/src/main/js/modules/teleport.js
new file mode 100644
index 000000000..f98524dc2
--- /dev/null
+++ b/src/main/js/modules/teleport.js
@@ -0,0 +1,59 @@
+'use strict';
+/*global __plugin, org, module, require*/
+var utils = require('utils');
+/************************************************************************
+## Teleport Module
+
+This module provides a function to teleport entities (Players or NPCs).
+
+### Parameters
+
+ * entity - The player or NPC to be teleported. If of type String, then a player with that name will be teleported.
+ * destination - The location to which they should be teleported. If not of type Location but is a Player, Block or any
+ object which has a `location` property then that works too. If of type String, then it's assumed that the destination is the player with that name.
+
+### Example
+
+The following code will teleport each player back to their spawn position.
+
+```javascript
+var teleport = require('teleport'),
+ utils = require('utils'),
+ players = utils.players(),
+ i = 0;
+for ( ; i < players.length; i++ ) {
+ teleport( players[i], players[i].spawnPosition );
+}
+```
+
+The following code will teleport 'tom' to 'jane's location.
+
+```javascript
+var teleport = require('teleport');
+teleport('tom' , 'jane');
+```
+***/
+function teleport( entity, destination){
+ if (typeof entity === 'String' || entity instanceof java.lang.String){
+ entity = utils.player(entity);
+ }
+ if (typeof destination === 'String' || destination instanceof java.lang.String){
+ var player = utils.player(destination);
+ if (player){
+ destination = player.location;
+ }
+ } else {
+ if (destination.location){
+ destination = destination.location;
+ }
+ }
+ if (__plugin.bukkit){
+ var bkTeleportCause = org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+ entity.teleport( destination, bkTeleportCause.PLUGIN);
+ }
+ if (__plugin.canary){
+ var cmTeleportCause = Packages.net.canarymod.hook.player.TeleportHook.TeleportCause;
+ entity.teleportTo(destination, cmTeleportCause.PLUGIN);
+ }
+}
+module.exports = teleport;
diff --git a/src/main/js/modules/utils/string-exts.js b/src/main/js/modules/utils/string-exts.js
index 816763682..14b767d9d 100644
--- a/src/main/js/modules/utils/string-exts.js
+++ b/src/main/js/modules/utils/string-exts.js
@@ -37,43 +37,43 @@ Example
-------
/js var boldGoldText = "Hello World".bold().gold();
- /js self.sendMessage( boldGoldText );
+ /js echo(self, boldGoldText );
Hello World
***/
-var c = org.bukkit.ChatColor;
+var COLOR_CHAR = '\u00a7';
var formattingCodes = {
- aqua: c.AQUA,
- black: c.BLACK,
- blue: c.BLUE,
- bold: c.BOLD,
- brightgreen: c.GREEN,
- darkaqua: c.DARK_AQUA,
- darkblue: c.DARK_BLUE,
- darkgray: c.DARK_GRAY,
- darkgreen: c.DARK_GREEN,
- purple: c.LIGHT_PURPLE,
- darkpurple: c.DARK_PURPLE,
- darkred: c.DARK_RED,
- gold: c.GOLD,
- gray: c.GRAY,
- green: c.GREEN,
- italic: c.ITALIC,
- lightpurple: c.LIGHT_PURPLE,
- indigo: c.BLUE,
- red: c.RED,
- pink: c.LIGHT_PURPLE,
- yellow: c.YELLOW,
- white: c.WHITE,
- strike: c.STRIKETHROUGH,
- random: c.MAGIC,
- magic: c.MAGIC,
- underline: c.UNDERLINE,
- reset: c.RESET
+ aqua: 'b',
+ black: '0',
+ blue: '9',
+ bold: 'l',
+ brightgreen: 'a',
+ darkaqua: '3',
+ darkblue: '1',
+ darkgray: '8',
+ darkgreen: '2',
+ purple: 'd',
+ darkpurple: '5',
+ darkred: '4',
+ gold: '6',
+ gray: '7',
+ green: 'a',
+ italic: 'o',
+ lightpurple: 'd',
+ indigo: '9',
+ red: 'c',
+ pink: 'd',
+ yellow: 'e',
+ white: 'f',
+ strike: 'm',
+ random: 'k',
+ magic: 'k',
+ underline: 'n',
+ reset: 'r'
};
for ( var method in formattingCodes ) {
String.prototype[method] = function( c ) {
return function(){ return c + this; };
- }( formattingCodes[method] );
+ }( COLOR_CHAR + formattingCodes[method] );
}
diff --git a/src/main/js/modules/utils/utils.js b/src/main/js/modules/utils/utils.js
index ac8676855..11dd21113 100644
--- a/src/main/js/modules/utils/utils.js
+++ b/src/main/js/modules/utils/utils.js
@@ -1,8 +1,15 @@
+/*global require, __plugin, org, exports, server, setTimeout, Packages, setInterval, addUnloadHandler, clearInterval, events*/
'use strict';
-var File = java.io.File,
- bkBukkit = org.bukkit.Bukkit,
- bkLocation = org.bukkit.Location,
- bkBlockCommandSender = org.bukkit.command.BlockCommandSender;
+var File = java.io.File;
+
+if (__plugin.bukkit){
+ var bkBukkit = org.bukkit.Bukkit,
+ bkLocation = org.bukkit.Location,
+ bkBlockCommandSender = org.bukkit.command.BlockCommandSender;
+}
+if (__plugin.canary){
+ var Canary = Packages.net.canarymod.Canary;
+}
/************************************************************************
## Utilities Module
@@ -12,9 +19,9 @@ miscellaneous utility functions and classes to help with programming.
### utils.player() function
-The utils.player() function will return a [bukkit Player][bkpl] object
+The utils.player() function will return a [Player][cmpl] object
with the given name. This function takes a single parameter
-`playerName` which can be either a String or a [Player][bkpl] object -
+`playerName` which can be either a String or a [Player][cmpl] object -
if it's a Player object, then the same object is returned. If it's a
String, then it tries to find the player with that name.
@@ -31,17 +38,19 @@ var utils = require('utils');
var name = 'walterh';
var player = utils.player(name);
if ( player ) {
- player.sendMessage('Got ' + name);
+ echo(player, 'Got ' + name);
} else {
- console.log('No player named ' + name);
+ console.log('No player named ' + name);
}
```
[bkpl]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html
+[cmpl]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/entity/living/humanoid/Player.html
+[cmloc]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/world/position/Location.html
[bkloc]: http://jd.bukkit.org/dev/apidocs/org/bukkit/Location.html
***/
-var _player = function ( playerName ) {
+function _player( playerName ) {
if ( typeof playerName == 'undefined' ) {
if ( typeof self == 'undefined' ) {
return null;
@@ -50,15 +59,70 @@ var _player = function ( playerName ) {
}
} else {
if ( typeof playerName == 'string' )
- return bkBukkit.getPlayer( playerName );
+ if (__plugin.canary) {
+ return Canary.server.getPlayer( playerName );
+ } else {
+ return bkBukkit.getPlayer( playerName );
+ }
else
return playerName; // assumes it's a player object
}
};
/*************************************************************************
+### utils.world( worldName ) function
+
+Returns a World object matching the given name
+
+***/
+function _world( worldName ){
+ if (__plugin.canary){
+ if (worldName instanceof Packages.net.canarymod.api.world.World){
+ return worldName;
+ }
+ var worldMgr = Canary.server.worldManager;
+ try {
+ if (worldName === undefined){
+ var worldNames = worldMgr.getLoadedWorldsNames();
+ worldName = worldNames[0];
+ }
+ return worldMgr.getWorld( worldName, true );
+ } catch (error) {
+ console.error( 'utils.world() failed to load ' + worldName + ',Error:' + error );
+ }
+ }
+ if (__plugin.bukkit){
+ if (worldName instanceof org.bukkit.World){
+ return worldName;
+ }
+ if (worldName === undefined){
+ return bkBukkit.getWorlds().get(0);
+ }
+ return bkBukkit.getWorld( worldName );
+ }
+ return null;
+}
+exports.world = _world;
+
+/*************************************************************************
+### utils.blockAt( Location ) function
+
+Returns the Block at the given location.
+
+***/
+function _blockAt( location ){
+ if (__plugin.canary){
+ return location.world.getBlockAt(location);
+ }
+ if (__plugin.bukkit){
+ return location.block;
+ }
+ return null;
+}
+exports.blockAt = _blockAt;
+/*************************************************************************
### utils.locationToJSON() function
-utils.locationToJSON() returns a [org.bukkit.Location][bkloc] object in JSON form...
+utils.locationToJSON() returns a [Location][cmloc] object in JSON form...
{ world: 'world5',
x: 56.9324,
@@ -72,20 +136,21 @@ This can be useful if you write a plugin that needs to store location data since
#### Parameters
- * location: An object of type [org.bukkit.Location][bkloc]
+ * location: An object of type [Location][cmloc]
#### Returns
A JSON object in the above form.
***/
-var _locationToJSON = function( location ) {
+function _locationToJSON( location ) {
+ var yaw = __plugin.bukkit ? location.yaw : (__plugin.canary ? location.rotation : 0);
return {
world: ''+location.world.name,
x: location.x,
y: location.y,
z: location.z,
- yaw: location.yaw,
+ yaw: yaw,
pitch: location.pitch
};
};
@@ -93,7 +158,7 @@ var _locationToJSON = function( location ) {
### utils.locationToString() function
The utils.locationToString() function returns a
-[org.bukkit.Location][bkloc] object in string form...
+[Location][cmloc] object in string form...
'{"world":"world5",x:56.9324,y:103.9954,z:43.1323,yaw:0.0,pitch:0.0}'
@@ -110,7 +175,7 @@ lookupTable[key] = player.name;
```
***/
-exports.locationToString = function( location ) {
+exports.locationToString = function locationToString( location ) {
return JSON.stringify( _locationToJSON( location ) );
};
exports.locationToJSON = _locationToJSON;
@@ -118,38 +183,43 @@ exports.locationToJSON = _locationToJSON;
/*************************************************************************
### utils.locationFromJSON() function
-This function reconstructs an [org.bukkit.Location][bkloc] object from
+This function reconstructs an [Location][cmloc] object from
a JSON representation. This is the counterpart to the
`locationToJSON()` function. It takes a JSON object of the form
returned by locationToJSON() and reconstructs and returns a bukkit
Location object.
***/
-exports.locationFromJSON = function( json ) {
+exports.locationFromJSON = function locationFromJSON( json ) {
var world;
- if ( json.constuctor == Array ) {
+ if ( json.constructor == Array ) {
// for support of legacy format
- world = bkBukkit.getWorld( json[0] );
+ world = _world( json[0] );
return new bkLocation( world, json[1], json[2] , json[3] );
} else {
- world = bkBukkit.getWorld( json.world );
- return new bkLocation( world, json.x, json.y , json.z, json.yaw, json.pitch );
+ if (__plugin.canary){
+ world = _world( json.world );
+ var cmLocation = Packages.net.canarymod.api.world.position.Location;
+ return new cmLocation(world, json.x, json.y, json.z, json.pitch?json.pitch:0, json.yaw?json.yaw:0);
+ } else {
+ world = _world( json.world );
+ return new bkLocation( world, json.x, json.y , json.z, json.yaw?json.yaw:0, json.pitch?json.pitch:0 );
+ }
}
};
exports.player = _player;
-exports.getPlayerObject = function( player ) {
+exports.getPlayerObject = function getPlayerObject( player ) {
console.warn( 'utils.getPlayerObject() is deprecated. Use utils.player() instead.' );
return _player(player);
};
/*************************************************************************
### utils.getPlayerPos() function
-This function returns the player's [Location][bkloc] (x, y, z, pitch
+This function returns the player's [Location][cmloc] (x, y, z, pitch
and yaw) for a named player. If the "player" is in fact a
-[org.bukkit.command.BlockCommandSender][bkbcs] then the attached
-Block's location is returned.
+[BlockCommand][bkbcs] then the attached Block's location is returned.
#### Parameters
@@ -157,25 +227,34 @@ Block's location is returned.
#### Returns
-An [org.bukkit.Location][bkloc] object.
+A [Location][cmloc] object.
[bkbcs]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/BlockCommandSender.html
[bksndr]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/command/CommandSender.html
***/
-exports.getPlayerPos = function( player ) {
+function getPlayerPos( player ){
player = _player( player );
if ( player ) {
- if ( player instanceof bkBlockCommandSender )
- return player.block.location;
- else
- return player.location;
+ if (__plugin.bukkit){
+ if ( player instanceof bkBlockCommandSender )
+ return player.block.location;
+ else
+ return player.location;
+ }
+ if (__plugin.canary){
+ if ( player instanceof Packages.net.canarymod.api.world.blocks.CommandBlock)
+ return player.block.location;
+ else
+ return player.location;
+ }
}
return null;
-};
+}
+exports.getPlayerPos = getPlayerPos;
/************************************************************************
### utils.getMousePos() function
-This function returns a [org.bukkit.Location][bkloc] object (the
+This function returns a [Location][cmloc] object (the
x,y,z) of the current block being targeted by the named player. This
is the location of the block the player is looking at (targeting).
@@ -192,24 +271,44 @@ var utils = require('utils');
var playerName = 'walterh';
var targetPos = utils.getMousePos(playerName);
if (targetPos){
- targetPos.world.strikeLightning(targetPos);
+ if (__plugin.canary){
+ targetPos.world.makeLightningBolt(targetPos);
+ }
+ if (__plugin.bukkit){
+ targetPos.world.strikeLightning(targetPos);
+ }
}
```
***/
-exports.getMousePos = function( player ) {
+exports.getMousePos = function getMousePos( player ) {
player = _player(player);
if ( !player ) {
return null;
}
- // player might be CONSOLE or a CommandBlock
- if ( !player.getTargetBlock ) {
- return null;
- }
- var targetedBlock = player.getTargetBlock( null, 5 );
- if ( targetedBlock == null || targetedBlock.isEmpty() ) {
- return null;
+ var targetedBlock ;
+ if ( __plugin.canary ) {
+ var cmLineTracer = Packages.net.canarymod.LineTracer;
+ var lineTracer = new cmLineTracer(player);
+ targetedBlock = lineTracer.getTargetBlock();
+ if (targetedBlock == null){
+ return null;
+ }
+ } else {
+ // player might be CONSOLE or a CommandBlock
+ if ( !player.getTargetBlock ) {
+ return null;
+ }
+ try {
+ targetedBlock = player.getTargetBlock( null, 5 );
+ }catch (e){
+ // spigot 1.8.7 adds new overload which causes problems with JDK 7
+ targetedBlock = player['getTargetBlock(java.util.Set,int)'](null, 5 );
+ }
+ if ( targetedBlock == null || targetedBlock.isEmpty() ) {
+ return null;
+ }
}
return targetedBlock.location;
};
@@ -217,14 +316,15 @@ exports.getMousePos = function( player ) {
### utils.foreach() function
The utils.foreach() function is a utility function for iterating over
-an array of objects (or a java.util.Collection of objects) and processing each object in turn. Where
-utils.foreach() differs from other similar functions found in
-javascript libraries, is that utils.foreach can process the array
-immediately or can process it *nicely* by processing one item at a
-time then delaying processing of the next item for a given number of
-server ticks (there are 20 ticks per second on the minecraft main
-thread). This method relies on Bukkit's [org.bukkit.scheduler][sched]
-package for scheduling processing of arrays.
+an array of objects (or a java.util.Collection of objects) and
+processing each object in turn. Where utils.foreach() differs from
+other similar functions found in javascript libraries, is that
+utils.foreach can process the array immediately or can process it
+*nicely* by processing one item at a time then delaying processing of
+the next item for a given number of server ticks (there are 20 ticks
+per second on the minecraft main thread). This method relies on
+Bukkit's [org.bukkit.scheduler][sched] package for scheduling
+processing of arrays.
[sched]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/package-summary.html
@@ -242,11 +342,9 @@ package for scheduling processing of arrays.
- array : The entire array.
* context (optional) : An object which may be used by the callback.
- * delay (optional, numeric) : If a delay is specified (in ticks - 20
- ticks = 1 second), then the processing will be scheduled so that
+ * delayInMilliseconds (optional, numeric) : If a delay is specified then the processing will be scheduled so that
each item will be processed in turn with a delay between the completion of each
- item and the start of the next. This is recommended for big builds (say 200 x 200 x 200
- blocks) or any CPU-intensive process.
+ item and the start of the next. This is recommended for any CPU-intensive process.
* onDone (optional, function) : A function to be executed when all processing
is complete. This parameter is only used when the processing is delayed. (It's optional even if a
delay parameter is supplied).
@@ -263,62 +361,30 @@ The following example illustrates how to use foreach for immediate processing of
```javascript
var utils = require('utils');
-var players = ['moe', 'larry', 'curly'];
-utils.foreach (players, function(item){
- server.getPlayer(item).sendMessage('Hi ' + item);
+var players = utils.players();
+utils.foreach (players, function( player ) {
+ echo( player , 'Hi ' + player);
});
```
-... The `utils.foreach()` function can work with Arrays or any Java-style collection. This is important
-because many objects in the Bukkit API use Java-style collections...
-
-```javascript
-utils.foreach( server.onlinePlayers, function(player){
- player.chat('Hello!');
-});
-```
-
-... the above code sends a 'Hello!' to every online player.
-
-The following example is a more complex use case - The need to build an enormous structure
-without hogging CPU usage...
-
-```javascript
-// build a structure 200 wide x 200 tall x 200 long
-// (That's 8 Million Blocks - enough to tax any machine!)
-var utils = require('utils');
-
-var a = [];
-a.length = 200;
-var drone = new Drone();
-var processItem = function(item, index, object, array){
- // build a box 200 wide by 200 long then move up
- drone.box(blocks.wood, 200, 1, 200).up();
-};
-// by the time the job's done 'self' might be someone else
-// assume this code is within a function/closure
-var player = self;
-var onDone = function(){
- player.sendMessage('Job Done!');
-};
-utils.foreach (a, processItem, null, 10, onDone);
-```
-
+... The `utils.foreach()` function can work with Arrays or any
+Java-style collection. This is important because many objects in the
+CanaryMod and Bukkit APIs use Java-style collections.
***/
-var _foreach = function( array, callback, context, delay, onCompletion ) {
+function _foreach( array, callback, context, delay, onCompletion ) {
if ( array instanceof java.util.Collection ) {
array = array.toArray();
}
var i = 0;
var len = array.length;
+ function next() {
+ callback(array[i], i, context, array);
+ i++;
+ }
+ function hasNext() {
+ return i < len;
+ }
if ( delay ) {
- var next = function( ) {
- callback(array[i], i, context, array);
- i++;
- };
- var hasNext = function( ) {
- return i < len;
- };
_nicely( next, hasNext, onCompletion, delay );
} else {
for ( ;i < len; i++ ) {
@@ -330,8 +396,7 @@ exports.foreach = _foreach;
/************************************************************************
### utils.nicely() function
-The utils.nicely() function is for performing processing using the
-[org.bukkit.scheduler][sched] package/API. utils.nicely() lets you
+The utils.nicely() function is for performing background processing. utils.nicely() lets you
process with a specified delay between the completion of each `next()`
function and the start of the next `next()` function.
`utils.nicely()` is a recursive function - that is - it calls itself
@@ -345,17 +410,17 @@ function and the start of the next `next()` function.
true if the `next` function should be called (processing is not
complete), false otherwise.
* onDone : A function which is to be called when all processing is complete (hasNext returned false).
- * delay : The delay (in server ticks - 20 per second) between each call.
+ * delayInMilliseconds : The delay between each call.
#### Example
See the source code to utils.foreach for an example of how utils.nicely is used.
***/
-var _nicely = function( next, hasNext, onDone, delay ) {
+function _nicely( next, hasNext, onDone, delay ) {
if ( hasNext() ){
next();
- server.scheduler.runTaskLater( __plugin, function() {
+ setTimeout( function() {
_nicely( next, hasNext, onDone, delay );
}, delay );
}else{
@@ -365,58 +430,60 @@ var _nicely = function( next, hasNext, onDone, delay ) {
}
};
exports.nicely = _nicely;
-/************************************************************************
-### utils.at() function
-The utils.at() function will perform a given task at a given time every
-(minecraft) day.
+function _at( time24hr, callback, pWorlds, repeat ) {
+ console.warn("utils.at() is deprecated, use require('at') instead");
+ var at = require('at');
+ return at( time24hr, callback, pWorlds, repeat);
+}
+exports.at = _at;
+/*************************************************************************
+### utils.time( world ) function
-#### Parameters
+Returns the timeofday (in minecraft ticks) for the given world. This function is necessary because
+canarymod and bukkit differ in how the timeofday is calculated.
- * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while
- 9:30 pm is '21:30', midnight is '00:00' and midday is '12:00'
- * callback : A javascript function which will be invoked at the given time.
- * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used.
+See http://minecraft.gamepedia.com/Day-night_cycle#Conversions
-#### Example
+***/
+function getTime(world){
+ world = _world(world);
-To warn players when night is approaching...
+ if (__plugin.bukkit){
+ return world.time;
+ }
+ if (__plugin.canary){
+ // there's a bug in canary where if you call world.setTime() the world.totalTime
+ // becomes huge.
+ if (world.totalTime < world.rawTime){
+ return world.totalTime;
+ } else {
+ return ((world.totalTime % world.rawTime) + world.relativeTime) % 24000;
+ }
+ }
+ return 0;
+}
+exports.time = getTime;
-```javascript
-var utils = require('utils');
+/*************************************************************************
+### utils.time24( world ) function
-utils.at( '19:00', function() {
+Returns the timeofday for the given world using 24 hour notation. (number of minutes)
- utils.foreach( server.onlinePlayers, function( player ) {
- player.chat( 'The night is dark and full of terrors!' );
- });
+See http://minecraft.gamepedia.com/Day-night_cycle#Conversions
+
+#### Parameters
+
+ * world : the name of the world or world object for which you want to get time
-});
-```
-
***/
-exports.at = function( time24hr, callback, worlds ) {
- var forever = function(){ return true; };
- var timeParts = time24hr.split( ':' );
- var hrs = ( (timeParts[0] * 1000) + 18000 ) % 24000;
- var mins;
- if ( timeParts.length > 1 ) {
- mins = ( timeParts[1] / 60 ) * 1000;
- }
- var timeMc = hrs + mins;
- if ( typeof worlds == 'undefined' ) {
- worlds = server.worlds;
- }
- _nicely( function() {
- _foreach( worlds, function ( world ) {
- var time = world.getTime();
- var diff = timeMc - time;
- if ( diff > 0 && diff < 100 ) {
- callback();
- }
- });
- }, forever, null, 100 );
-};
+function getTime24( world ){
+ world = _world(world); // accept world name or object or undeifned
+ var mcTime = getTime(world);
+ var mins = Math.floor( ( (mcTime + 6000) % 24000) / 16.6667 );
+ return mins;
+}
+exports.time24 = getTime24;
/************************************************************************
### utils.find() function
@@ -439,29 +506,10 @@ var jsFiles = utils.find('./', function(dir,name){
return name.match(/\.js$/);
});
```
-
***/
-exports.find = function( dir , filter ) {
- var result = [];
- var recurse = function( dir, store ) {
- var files, dirfile = new File( dir );
-
- if ( typeof filter == 'undefined' ) {
- files = dirfile.list();
- } else {
- files = dirfile.list(filter);
- }
- _foreach( files, function( file ) {
- file = new java.io.File( dir + '/' + file );
- if ( file.isDirectory() ) {
- recurse( file.canonicalPath, store );
- } else {
- store.push( file.canonicalPath );
- }
- });
- };
- recurse( dir, result );
- return result;
+exports.find = function( path, filter){
+ console.warn("utils.find() is deprecated, use require('find') instead");
+ return require('find')(path, filter);
};
/************************************************************************
### utils.serverAddress() function
@@ -474,7 +522,7 @@ var serverAddress = utils.serverAddress();
console.log(serverAddress);
```
***/
-exports.serverAddress = function() {
+exports.serverAddress = function serverAddress() {
var interfaces = java.net.NetworkInterface.getNetworkInterfaces();
var current,
addresses,
@@ -495,65 +543,201 @@ exports.serverAddress = function() {
}
return null;
};
-/************************************************************************
-### utils.watchFile() function
+/**************************************************************************
+### utils.array() function
-Watches for changes to the given file or directory and calls the function provided
-when the file changes.
-
-#### Parameters
+Converts Java collection objects to type Javascript array so they can avail of
+all of Javascript's Array goodness.
- * File - the file to watch (can be a file or directory)
- * Callback - The callback to invoke when the file has changed. The callback takes the
- changed file as a parameter.
-
#### Example
-```javascript
-var utils = require('utils');
-utils.watchFile( 'test.txt', function( file ) {
- console.log( file + ' has changed');
-});
-```
+ var utils = require('utils');
+ var worlds = utils.array(server.worldManager.getAllWorlds());
+
***/
-var filesWatched = {};
-exports.watchFile = function( file, callback ) {
- if ( typeof file == 'string' ) {
- file = new File(file);
+function toArray( ){
+ var result = [],
+ javaArray = null,
+ i = 0;
+ if (arguments[0] instanceof java.util.Collection){
+ // it's a java collection
+ javaArray = arguments[0].toArray();
+ for ( ;i < javaArray.length; i++) {
+ result.push(javaArray[i]);
+ }
+ } else if (arguments[0].constructor === Array){
+ // it's a javascript array
+ return arguments[0];
+ } else if (arguments[0].length) {
+ // it's a java array
+ javaArray = arguments[0];
+ for ( ;i < javaArray.length; i++) {
+ result.push(javaArray[i]);
+ }
}
- filesWatched[file.canonicalPath] = {
- callback: callback,
- lastModified: file.lastModified()
- };
-};
-/************************************************************************
-### utils.unwatchFile() function
+ return result;
+}
+exports.array = toArray;
-Removes a file from the watch list.
+function worlds(){
+ if (__plugin.canary){
+ return toArray(server.worldManager.allWorlds);
+ }
+ if (__plugin.bukkit){
+ return toArray(server.worlds);
+ }
+}
+exports.worlds = worlds;
+
+/*************************************************************************
+### utils.players() function
+
+This function returns a javascript array of all online players on the
+server. You can optionally provide a function which will be invoked
+with each player as a parameter. For example, to give each player the
+ability to shoot arrows which launch fireworks:
-#### Example
```javascript
-var utils = require('utils');
-utils.unwatchFile( 'test.txt');
+require('utils').players( arrows.firework )
```
+Any players with a bow will be able to launch fireworks by shooting.
+
+### utils.playerNames() function
+
+This function returns a javascript array of player names (as javascript strings)
+
***/
-exports.unwatchFile = function( file, callback ) {
- if ( typeof file == 'string' ) {
- file = new File(file);
+function getPlayersBukkit(){
+ var result = [];
+ var players = server.getOnlinePlayers();
+ for (var i = 0; i < players.size(); i++){
+ result.push(players.get(i));
}
- delete filesWatched[file.canonicalPath];
-};
+ return result;
+}
+function getPlayersCanary(){
+ var result = [];
+ var players = server.playerList;
+ for (var i = 0; i < players.size(); i++){
+ result.push(players.get(i));
+ }
+ return result;
+}
+var getPlayers = null;
+if (__plugin.canary) {
+ getPlayers = getPlayersCanary;
+} else {
+ getPlayers = getPlayersBukkit;
+}
-function fileWatcher() {
- for (var file in filesWatched) {
- var fileObject = new File(file);
- var lm = fileObject.lastModified();
- if ( lm != filesWatched[file].lastModified ) {
- filesWatched[file].lastModified = lm;
- filesWatched[file].callback(fileObject);
+function getStatBukkit(){
+ if (arguments.length == 1){
+ var stat = arguments[1];
+ return org.bukkit.Statistic[stat.toUpperCase()];
+ } else {
+ var player = arguments[0];
+ var stat = arguments[1];
+ return player.getStatistic(org.bukkit.Statistic[stat.toUpperCase()]);
+ }
+
+}
+function getStatCanary(){
+ var cmStatistics = Packages.net.canarymod.api.statistics.Statistics;
+ if (arguments.length == 1){
+ var stat = arguments[0];
+ return cmStatistics[stat.toUpperCase()].instance;
+ } else {
+ var player = arguments[0];
+ var stat = arguments[1];
+ return player.getStat(cmStatistics[stat.toUpperCase()].instance);
+ }
+}
+if (__plugin.canary){
+ var cmStatistics = Packages.net.canarymod.api.statistics.Statistics;
+ var values = cmStatistics.values();
+ for (var i = 0;i < values.length; i++){
+ var value = values[i];
+ try {
+ var stat = value.instance;
+ getStatCanary[value.name()] = stat;
+ }catch (e){
+ // as of 20141018 some calls to getInstance() will generate an NPE
+ // see https://github.com/CanaryModTeam/CanaryMod/issues/84
}
}
- setTimeout( fileWatcher, 5000 );
+}
+
+function getPlayerNames(){
+ return getPlayers().map(function(p){ return p.name; });
+}
+exports.players = function players(fn){
+ var result = getPlayers();
+ if (fn){
+ result.forEach(fn);
+ }
+ return result;
};
-setTimeout( fileWatcher, 5000 );
+exports.playerNames = getPlayerNames;
+
+/*************************************************************************
+### utils.stat() function
+
+This function returns a numeric value for a given player statistic.
+
+#### Parameters
+
+ * Player - The player object (optional - if only the statistic name parameter is provided then the statistic object is returned)
+ * Statistic - A string whose value should be one of the following (CanaryMod)
+ * ANIMALSBRED
+ * BOATONECM
+ * CLIMBONECM
+ * CROUCHONECM
+ * DAMAGEDEALT
+ * DAMAGETAKEN
+ * DEATHS
+ * DRIVEONECM
+ * DROP
+ * FALLONECM
+ * FISHCAUGHT
+ * FLYONECM
+ * HORSEONECM
+ * JUMP
+ * JUNKFISHED
+ * LEAVEGAME
+ * MINECARTONECM
+ * MOBKILLS
+ * PIGONECM
+ * PLAYERKILLS
+ * PLAYONEMINUTE
+ * SPRINTONECM
+ * SWIMONECM
+ * TALKEDTOVILLAGER
+ * TIMESINCEDEATH
+ * TRADEDWITHVILLAGER
+ * TREASUREFISHED
+ * WALKONECM
+
+See [CanaryMod's Statistic][cmstat] class for an up-to-date list of possible stat values
+
+[cmstat]: https://ci.visualillusionsent.net/job/CanaryLib/javadoc/net/canarymod/api/statistics/Statistics.html
+
+#### Example 1 Getting stats for a player
+
+ var utils = require('utils');
+ var jumpCount = utils.stat( player, 'jump');
+
+#### Example 2 Getting the JUMP statistic object (which can be used elsewhere)
+
+ var utils = require('utils');
+ var JUMPSTAT = utils.stat('jump');
+ var jumpCount = player.getStat( JUMPSTAT ); // canary-specific code
+
+This function also contains values for each possible stat so you can get at stats like this...
+
+ var utils = require('utils');
+ var JUMPSTAT = utils.stat.JUMP; // Accessing the value
+ var jumpCount = player.getStat ( JUMPSTAT ); // canary-specific code
+***/
+exports.stat = __plugin.canary ? getStatCanary: getStatBukkit;
+
diff --git a/src/main/js/modules/watcher.js b/src/main/js/modules/watcher.js
new file mode 100644
index 000000000..7970d6c83
--- /dev/null
+++ b/src/main/js/modules/watcher.js
@@ -0,0 +1,189 @@
+'use strict';
+/*global setTimeout, exports, require*/
+var File = java.io.File;
+/************************************************************************
+## The watcher Module
+
+This module exposes functions for watching for changes to files or directories.
+
+### watcher.watchFile() function
+
+Watches for changes to the given file or directory and calls the function provided
+when the file changes.
+
+#### Parameters
+
+ * File - the file to watch (can be a file or directory)
+ * Callback - The callback to invoke when the file has changed. The callback takes the
+ changed file as a parameter.
+
+#### Example
+
+```javascript
+var watcher = require('watcher');
+watcher.watchFile( 'test.txt', function( file ) {
+ console.log( file + ' has changed');
+});
+```
+***/
+var filesWatched = {};
+var dirsWatched = {};
+
+exports.watchFile = function( file, callback ) {
+ if ( typeof file == 'string' ) {
+ file = new File(file);
+ }
+ filesWatched[file.canonicalPath] = {
+ callback: callback,
+ lastModified: file.lastModified()
+ };
+};
+
+/************************************************************************
+### watcher.watchDir() function
+
+Watches for changes to the given directory and calls the function provided
+when the directory changes. It works by calling watchFile/watchDir for each
+file/subdirectory.
+
+#### Parameters
+
+ * Dir - the file to watch (can be a file or directory)
+ * Callback - The callback to invoke when the directory has changed.
+ The callback takes the changed file as a parameter.
+ For each change inside the directory the callback will also
+ be called.
+
+#### Example
+
+```javascript
+var watcher = require('watcher');
+watcher.watchDir( 'players/_ial', function( dir ) {
+ console.log( dir + ' has changed');
+});
+```
+***/
+
+exports.watchDir = function( dir, callback ) {
+ if ( typeof dir == 'string' ) {
+ dir = new File(dir);
+ }
+ dirsWatched[dir.canonicalPath] = {
+ callback: callback,
+ lastModified: dir.lastModified()
+ };
+
+ var files = dir.listFiles(),file;
+ if ( !files ) {
+ return;
+ }
+ for ( var i = 0; i < files.length; i++ ) {
+ file = files[i];
+ if (file.isDirectory( )) {
+ exports.watchDir(file,callback);
+ }else{
+ exports.watchFile(file,callback);
+ }
+ }
+};
+/************************************************************************
+### watcher.unwatchFile() function
+
+Removes a file from the watch list.
+
+#### Example
+```javascript
+var watcher = require('watcher');
+watcher.unwatchFile('test.txt');
+```
+
+***/
+exports.unwatchFile = function( file, callback ) {
+ if ( typeof file == 'string' ) {
+ file = new File(file);
+ }
+ delete filesWatched[file.canonicalPath];
+};
+
+/************************************************************************
+### watcher.unwatchDir() function
+
+Removes a directory from the watch list and all files inside the directory
+are also "unwatched"
+
+#### Example
+```javascript
+var watcher = require('watcher');
+watcher.unwatchDir ('players/_ial');
+```
+Would cause also
+```javascript
+watcher.unwatchFile (file);
+```
+for each file inside directory (and unwatchDir for each directory inside it)
+
+***/
+exports.unwatchDir = function( dir, callback ) {
+ if ( typeof dir == 'string' ) {
+ dir = new File(dir);
+ }
+ delete dirsWatched[dir.canonicalPath];
+
+ var files = dir.listFiles(),file;
+ if ( !files ) {
+ return;
+ }
+ for ( var i = 0; i < files.length; i++ ) {
+ file = files[i];
+ if (file.isDirectory( )) {
+ exports.unwatchDir(file,callback);
+ }else{
+ exports.unwatchFile(file,callback);
+ }
+ }
+};
+
+function fileWatcher(calledCallbacks) {
+ for (var file in filesWatched) {
+ var fileObject = new File(file);
+ var lm = fileObject.lastModified();
+ if ( lm != filesWatched[file].lastModified ) {
+ filesWatched[file].lastModified = lm;
+ filesWatched[file].callback(fileObject);
+ if (!fileObject.exists()) {
+ exports.unwatchFile(file,filesWatched[file].callback);
+ }
+ }
+ }
+};
+
+
+//monitors directories for time change
+//when a change is detected watchFiles are invoked for each of the files in directory
+//and callback is called
+function dirWatcher(calledCallbacks) {
+ for (var dir in dirsWatched) {
+ var dirObject = new File(dir);
+ var lm = dirObject.lastModified();
+ var dw = dirsWatched[dir];
+ if ( lm != dirsWatched[dir].lastModified ) {
+ dirsWatched[dir].lastModified = lm;
+ dirsWatched[dir].callback(dirObject);
+
+ exports.unwatchDir(dir, dw.callback);
+ //causes all files to be rewatched
+ if (dirObject.exists()) {
+ exports.watchDir(dir, dw.callback);
+ }
+ }
+ }
+};
+
+//guarantees that a callback is only called once for each change
+function monitorDirAndFiles() {
+ fileWatcher ();
+ dirWatcher ();
+ setTimeout( monitorDirAndFiles, 3000 );
+};
+
+setTimeout( monitorDirAndFiles, 3000 );
diff --git a/src/main/js/plugins/alias/alias.js b/src/main/js/plugins/alias/alias.js
index 6ffe4af41..9b0998cf5 100644
--- a/src/main/js/plugins/alias/alias.js
+++ b/src/main/js/plugins/alias/alias.js
@@ -93,18 +93,18 @@ var _set = function( params, player ) {
var o = _processParams( params );
playerAliases[o.cmd] = o.aliases;
_store.players[player.name] = playerAliases;
- player.sendMessage( 'Alias ' + o.cmd + ' created.' );
+ echo( player, 'Alias ' + o.cmd + ' created.' );
};
var _remove = function( params, player ) {
if ( _store.players[player.name] && _store.players[player.name][ params[0] ] ) {
delete _store.players[player.name][params[0]];
- player.sendMessage( 'Alias ' + params[0] + ' removed.' );
+ echo( player, 'Alias ' + params[0] + ' removed.' );
}
else{
- player.sendMessage( 'Alias ' + params[0] + ' does not exist.' );
+ echo( player, 'Alias ' + params[0] + ' does not exist.' );
}
- if ( player.op ) {
+ if ( isOp(player) ) {
if ( _store.global[params[0]] ) {
delete _store.global[params[0]];
}
@@ -112,31 +112,31 @@ var _remove = function( params, player ) {
};
var _global = function( params, player ) {
- if ( !player.op ) {
- player.sendMessage( 'Only operators can set global aliases. ' +
- 'You need to be an operator to perform this command.' );
+ if ( !isOp(player) ) {
+ echo( player, 'Only operators can set global aliases. ' +
+ 'You need to be an operator to perform this command.' );
return;
}
var o = _processParams( params );
_store.global[o.cmd] = o.aliases;
- player.sendMessage( 'Global alias ' + o.cmd + ' created.' );
+ echo( player, 'Global alias ' + o.cmd + ' created.' );
};
var _list = function( params, player ) {
var alias = 0;
try {
if ( _store.players[player.name] ) {
- player.sendMessage('Your aliases:');
+ echo( player, 'Your aliases:');
for ( alias in _store.players[player.name] ) {
- player.sendMessage( alias + ' = ' +
- JSON.stringify( _store.players[player.name][alias] ) );
+ echo( player, alias + ' = ' +
+ JSON.stringify( _store.players[player.name][alias] ) );
}
} else {
- player.sendMessage( 'You have no player-specific aliases.' );
+ echo( player, 'You have no player-specific aliases.' );
}
- player.sendMessage( 'Global aliases:' );
+ echo( player, 'Global aliases:' );
for ( alias in _store.global ) {
- player.sendMessage( alias + ' = ' + JSON.stringify( _store.global[alias] ) );
+ echo( player, alias + ' = ' + JSON.stringify( _store.global[alias] ) );
}
} catch( e ) {
console.error( 'Error in list function: ' + e.message );
@@ -144,7 +144,7 @@ var _list = function( params, player ) {
}
};
var _help = function( params, player ) {
- player.sendMessage( 'Usage:\n' + _usage );
+ echo( player, 'Usage:\n' + _usage );
};
var alias = plugin( 'alias', {
@@ -160,7 +160,7 @@ var aliasCmd = command( 'alias', function( params, invoker ) {
var operation = params[0],
fn;
if ( !operation ) {
- invoker.sendMessage( 'Usage:\n' + _usage );
+ echo( invoker, 'Usage:\n' + _usage );
return;
}
/*
@@ -175,7 +175,7 @@ var aliasCmd = command( 'alias', function( params, invoker ) {
return;
}
}
- invoker.sendMessage( 'Usage:\n' + _usage );
+ echo( invoker, 'Usage:\n' + _usage );
});
var _intercept = function( msg, invoker, exec ) {
@@ -223,6 +223,10 @@ var _intercept = function( msg, invoker, exec ) {
Intercept all command processing and replace with aliased commands if the
command about to be issued matches an alias.
*/
+if (__plugin.canary){
+ console.warn('alias plugin is not yet supported in CanaryMod');
+ return;
+}
events.playerCommandPreprocess( function( evt ) {
var invoker = evt.player;
var exec = function( cmd ) {
diff --git a/src/main/js/plugins/arrows.js b/src/main/js/plugins/arrows.js
index 2aa54e02c..212b1538a 100644
--- a/src/main/js/plugins/arrows.js
+++ b/src/main/js/plugins/arrows.js
@@ -1,7 +1,9 @@
+'use strict';
+/*global require, __plugin, exports, events, setTimeout */
/*************************************************************************
## Arrows Plugin
-The arrows mod adds fancy arrows to the game. Arrows which...
+The arrows mod adds fancy arrows to the game. Arrows which ...
* Launch fireworks.
* Explode on impact.
@@ -17,27 +19,26 @@ The arrows mod adds fancy arrows to the game. Arrows which...
* `/js arrows.flourish(self)` - makes a tree grow where the arrow lands.
* `/js arrows.explosive(self)` - makes arrows explode.
* `/js arrows.normal(self)` sets arrow type to normal.
- * `/js arrows.sign(self)` turns a targeted sign into a Arrows menu
+ * `/js arrows.sign(self)` turns a targeted sign into an Arrows menu
All of the above functions can take an optional player object or name
as a parameter. For example: `/js arrows.explosive('player23')` makes
player23's arrows explosive.
***/
-
-var signs = require('signs'),
- fireworks = require('fireworks'),
- utils = require('utils'),
- bkTeleportCause = org.bukkit.event.player.PlayerTeleportEvent.TeleportCause,
- bkArrow = org.bukkit.entity.Arrow,
- bkPlayer = org.bukkit.entity.Player,
- bkTreeType = org.bukkit.TreeType,
- EXPLOSIVE_YIELD = 2.5,
- _store = { players: { } },
- arrows = plugin( 'arrows', { store: _store }, true ),
- i,
- type,
- _types = [ 'Normal', 'Explosive', 'Teleport', 'Flourish', 'Lightning', 'Firework' ];
+var Drone = require('drone'),
+ teleport = require('teleport'),
+ signs = require('signs'),
+ fireworks = require('fireworks'),
+ utils = require('utils'),
+ bkArrow = org.bukkit.entity.Arrow,
+ bkPlayer = org.bukkit.entity.Player,
+ EXPLOSIVE_YIELD = 2.5,
+ store = persist('arrows',{ players: { } }),
+ arrows = {},
+ i,
+ type,
+ _types = [ 'Normal', 'Explosive', 'Teleport', 'Flourish', 'Lightning', 'Firework' ];
exports.arrows = arrows;
@@ -49,7 +50,7 @@ for ( i = 0; i < _types.length; i++ ) {
return function( player ) {
player = utils.player( player );
if ( player ) {
- arrows.store.players[ player.name ] = n;
+ store.players[ player.name ] = n;
} else {
console.warn('arrows.' + n + ' No player ' + player);
}
@@ -61,8 +62,9 @@ for ( i = 0; i < _types.length; i++ ) {
called when the player chooses an arrow option from a menu sign
*/
var _onMenuChoice = function( event ) {
- arrows.store.players[ event.player.name ] = event.number;
+ store.players[ event.player.name ] = event.number;
};
+
var convertToArrowSign = signs.menu( 'Arrow', _types, _onMenuChoice );
/*
@@ -79,23 +81,23 @@ arrows.sign = function( cmdSender ) {
/*
event handler called when a projectile hits something
*/
-var _onArrowHit = function( event ) {
+function onBukkitArrowHit( event ) {
var projectile = event.entity,
- world = projectile.world,
- shooter = projectile.shooter,
- fireworkCount = 5,
- arrowType,
- launch = function( ) {
- fireworks.firework( projectile.location );
- if ( --fireworkCount ) {
- setTimeout( launch, 2000 );
- }
- };
-
+ world = projectile.world,
+ shooter = projectile.shooter,
+ fireworkCount = 5,
+ arrowType;
+
+ function launch(){
+ fireworks.firework( projectile.location );
+ if ( --fireworkCount ) {
+ setTimeout( launch, 2000 );
+ }
+ }
if (projectile instanceof bkArrow
&& shooter instanceof bkPlayer) {
- arrowType = arrows.store.players[ shooter.name ];
+ arrowType = store.players[ shooter.name ];
switch ( arrowType ) {
case 1:
@@ -104,11 +106,11 @@ var _onArrowHit = function( event ) {
break;
case 2:
projectile.remove();
- shooter.teleport( projectile.location, bkTeleportCause.PLUGIN );
+ teleport(shooter, projectile.location);
break;
case 3:
projectile.remove();
- world.generateTree( projectile.location, bkTreeType.BIG_TREE );
+ new Drone(projectile.location).oak();
break;
case 4:
projectile.remove();
@@ -120,6 +122,50 @@ var _onArrowHit = function( event ) {
break;
}
}
-};
-events.projectileHit( _onArrowHit );
+}
+function onCanaryArrowHit( event ) {
+ var projectile = event.projectile,
+ world = projectile.world,
+ shooter = projectile.owner,
+ fireworkCount = 5,
+ arrowType,
+ cmArrow = Packages.net.canarymod.api.entity.Arrow,
+ cmPlayer = Packages.net.canarymod.api.entity.living.humanoid.Player,
+ loc = projectile.location,
+ launch = function( ) {
+ fireworks.firework( loc);
+ if ( --fireworkCount ) {
+ setTimeout( launch, 2000 );
+ }
+ };
+
+ if (projectile instanceof cmArrow && shooter instanceof cmPlayer) {
+
+ arrowType = store.players[ shooter.name ];
+
+ switch ( arrowType ) {
+ case 1:
+ projectile.destroy();
+ world.makeExplosion( shooter, loc, EXPLOSIVE_YIELD, true );
+ break;
+ case 2:
+ projectile.destroy();
+ teleport(shooter, loc);
+ break;
+ case 3:
+ projectile.destroy();
+ new Drone( loc ).oak();
+ break;
+ case 4:
+ projectile.destroy();
+ world.makeLightningBolt( loc );
+ break;
+ case 5:
+ projectile.destroy();
+ launch();
+ break;
+ }
+ }
+}
+events.projectileHit( __plugin.bukkit ? onBukkitArrowHit : onCanaryArrowHit);
diff --git a/src/main/js/plugins/at.js b/src/main/js/plugins/at.js
new file mode 100644
index 000000000..f19422112
--- /dev/null
+++ b/src/main/js/plugins/at.js
@@ -0,0 +1,4 @@
+// ensure that the at world-load event handlers are
+// registered early in server startup
+var at = require('at');
+// nothing more needed.
diff --git a/src/main/js/plugins/chat/color.js b/src/main/js/plugins/chat/color.js
deleted file mode 100644
index 482caca1a..000000000
--- a/src/main/js/plugins/chat/color.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*************************************************************************
-## chat Plugin
-
-This plugin lets players choose a text color to use when chatting. Players can list colors by typing...
-
- /jsp list_colors
-
-... and can set the color to use when chatting by typing...
-
- /jsp chat_color {color}
-
-... where {color} is one of the following colors...
-
- * black
- * blue
- * darkgreen
- * darkaqua
- * darkred
- * purple
- * gold
- * gray
- * darkgray
- * indigo
- * brightgreen
- * aqua
- * red
- * pink
- * yellow
- * white
-
-This plugin's source code is useful to study because it is short and demonstrates use of the `plugin()`, and `command()` functions, persistence and event handling.
-
-***/
-var _store = { players: { } },
- colorCodes = {},
- i,
- colors = [
- 'black',
- 'blue',
- 'darkgreen',
- 'darkaqua',
- 'darkred',
- 'purple',
- 'gold',
- 'gray',
- 'darkgray',
- 'indigo',
- 'brightgreen',
- 'aqua',
- 'red',
- 'pink',
- 'yellow',
- 'white'
- ],
- foreach = require('utils').foreach;
-
-/*
- declare a new javascript plugin for changing chat text color
-*/
-exports.chat = plugin( 'chat', {
- /*
- set the color of text for a given player
- */
- setColor: function( player, color ) {
- _store.players[ player.name ] = color;
- },
-
- store: _store
-
-},true);
-
-foreach( colors, function ( color, i ) {
- colorCodes[color] = i.toString( 16 );
-} );
-
-events.asyncPlayerChat( function( event ) {
- var player = event.player;
- var playerChatColor = _store.players[ player.name ];
- if ( playerChatColor ) {
- event.message = '§' + colorCodes[ playerChatColor ] + event.message;
- }
-});
-
-var listColors = function( params, sender ) {
- var colorNamesInColor = [];
- foreach (colors, function( color ) {
- colorNamesInColor.push( '§' + colorCodes[color] + color );
- } );
- sender.sendMessage( 'valid chat colors are ' + colorNamesInColor.join( ', ') );
-};
-
-command( 'list_colors', listColors );
-command( 'chat_color', function( params, sender ) {
- var color = params[0];
- if ( colorCodes[color] ) {
- chat.setColor( sender, color );
- } else {
- sender.sendMessage( color + ' is not a valid color' );
- listColors();
- }
-}, colors );
-
diff --git a/src/main/js/plugins/classroom.js b/src/main/js/plugins/classroom.js
new file mode 100644
index 000000000..47f7d45ac
--- /dev/null
+++ b/src/main/js/plugins/classroom.js
@@ -0,0 +1,11 @@
+/*global require, exports, command*/
+var cr = require('classroom');
+
+command(function classroom( params, sender){
+ if (params[0] == 'on'){
+ cr.allowScripting(true, sender);
+ }else {
+ cr.allowScripting(false, sender);
+ }
+},['on','off']);
+exports.classroom = cr;
diff --git a/src/main/js/plugins/classroom/classroom.js b/src/main/js/plugins/classroom/classroom.js
deleted file mode 100644
index fa4edfceb..000000000
--- a/src/main/js/plugins/classroom/classroom.js
+++ /dev/null
@@ -1,166 +0,0 @@
-var utils = require('utils'),
- autoload = require('plugin').autoload,
- logger = __plugin.logger,
- foreach = utils.foreach,
- watchFile = utils.watchFile,
- unwatchFile = utils.unwatchFile,
- playersDir = __dirname + '/../../players/',
- serverAddress = utils.serverAddress();
-
-/************************************************************************
-## Classroom Plugin
-
-The `classroom` object contains a couple of utility functions for use
-in a classroom setting. The goal of these functions is to make it
-easier for tutors to facilitate ScriptCraft for use by students in a
-classroom environment. Although granting ScriptCraft access to
-students on a shared server is potentially risky (Students can
-potentially abuse it), it is slighlty less risky than granting
-operator privileges to each student. (Enterprising students will
-quickly realise how to grant themselves and others operator privileges
-once they have access to ScriptCraft).
-
-The goal of this module is not so much to enforce restrictions
-(security or otherwise) but to make it easier for tutors to setup a
-shared server so students can learn Javascript. When scripting is
-turned on, every player who joins the server will have a dedicated
-directory into which they can save scripts. All scripts in such
-directories are automatically watched and loaded into a global
-variable named after the player.
-
-So for example, if player 'walterh' joins the server, a `walterh`
-global variable is created. If a file `greet.js` with the following
-content is dropped into the `plugins/scriptcraft/players/walterh`
-directory...
-
-```javascript
-exports.hi = function( player ){
- player.sendMessage('Hi ' + player.name);
-};
-```
-
-... then it can be invoked like this: `/js walterh.hi( self )` . This
-lets every player/student create their own functions without having
-naming collisions.
-
-It's strongly recommended that the
-`craftbukkit/plugins/scriptcraft/players/` directory is shared so that
-others can connect to it and drop .js files into their student
-directories. On Ubuntu, select the folder in Nautilus (the default
-file browser) then right-click and choose *Sharing Options*, check the
-*Share this folder* checkbox and the *Allow others to create and
-delete files* and *Guest access* checkboxes. Click *Create Share*
-button to close the sharing options dialog. Students can then access
-the shared folder as follows...
-
- * Windows: Open Explorer, Go to \\{serverAddress}\players\
- * Macintosh: Open Finder, Go to smb://{serverAddress}/players/
- * Linux: Open Nautilus, Go to smb://{serverAddress}/players/
-
-... where {serverAddress} is the ip address of the server (this is
-displayed to whoever invokes the classroom.allowScripting() function.)
-
-### classroom.allowScripting() function
-
-Allow or disallow anyone who connects to the server (or is already
-connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
-to every student in a Minecraft classroom environment.
-
-Whenever any file is added/edited or removed from any of the players/
-directories the contents are automatically reloaded. This is to
-facilitate quick turnaround time for students getting to grips with
-Javascript.
-
-#### Parameters
-
- * canScript : true or false
-
-#### Example
-
-To allow all players (and any players who connect to the server) to
-use the `js` and `jsp` commands...
-
- /js classroom.allowScripting( true, self )
-
-To disallow scripting (and prevent players who join the server from using the commands)...
-
- /js classroom.allowScripting( false, self )
-
-Only ops users can run the classroom.allowScripting() function - this is so that students
-don't try to bar themselves and each other from scripting.
-
-***/
-var _store = { enableScripting: false },
- File = java.io.File;
-
-function revokeScripting ( player ) {
- foreach( player.getEffectivePermissions(), function( perm ) {
- if ( (''+perm.permission).indexOf( 'scriptcraft.' ) == 0 ) {
- if ( perm.attachment ) {
- perm.attachment.remove();
- }
- }
- });
- var playerName = '' + player.name;
- playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,'');
- var playerDir = new File( playersDir + playerName );
- unwatchFile( playerDir );
-}
-
-function grantScripting( player ) {
- console.log('Enabling scripting for player ' + player.name);
- var playerName = '' + player.name;
- playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,'');
- var playerDir = new File( playersDir + playerName );
- playerDir.mkdirs();
- player.addAttachment( __plugin, 'scriptcraft.*', true );
- var playerContext = {};
- autoload( playerContext, playerDir, logger, { cache: false });
- global[playerName] = playerContext;
-
- watchFile( playerDir, function( changedDir ){
- autoload(playerContext, playerDir, logger, { cache: false });
- });
-
-/*
- player.sendMessage('Create your own minecraft mods by adding javascript (.js) files');
- player.sendMessage(' Windows: Open Explorer, go to \\\\' + serverAddress + '\\players\\' + player.name);
- player.sendMessage(' Macintosh: Open Finder, Go to smb://' + serverAddress + '/players/' + player.name);
- player.sendMessage(' Linux: Open Nautilus, Go to smb://' + serverAddress + '/players/' + player.name);
-*/
-
-}
-
-var classroom = plugin('classroom', {
- allowScripting: function (/* boolean: true or false */ canScript, sender ) {
- sender = utils.player(sender);
- if ( !sender ) {
- console.log( 'Attempt to set classroom scripting without credentials' );
- console.log( 'classroom.allowScripting(boolean, sender)' );
- return;
- }
- /*
- only operators should be allowed run this function
- */
- if ( !sender.op ) {
- console.log( 'Attempt to set classroom scripting without credentials: ' + sender.name );
- sender.sendMessage('Only operators can use this function');
- return;
- }
- foreach( server.onlinePlayers, canScript ? grantScripting : revokeScripting);
- _store.enableScripting = canScript;
-
- sender.sendMessage('Scripting turned ' + ( canScript ? 'on' : 'off' ) +
- ' for all players on server ' + serverAddress);
- },
- store: _store
-}, true);
-
-exports.classroom = classroom;
-
-events.playerJoin( function( event ) {
- if ( _store.enableScripting ) {
- grantScripting(event.player);
- }
-}, 'HIGHEST');
-
diff --git a/src/main/js/plugins/commando/commando-test.js b/src/main/js/plugins/commando/commando-test.js
index 5457e1153..cd9b987e9 100644
--- a/src/main/js/plugins/commando/commando-test.js
+++ b/src/main/js/plugins/commando/commando-test.js
@@ -2,6 +2,10 @@
A test of the commando plugin.
Adds a new `/js-time` command with 4 possible options: Dawn, Midday, Dusk, Midnight
*/
+if (__plugin.canary){
+ console.warn('commando-test not yet supported in CanaryMod');
+ return;
+}
var commando = require('./commando').commando,
times = ['Dawn','Midday','Dusk','Midnight'];
@@ -10,13 +14,13 @@ commando( 'js-time' , function( params, sender ) {
i = 0;
if ( sender.location ) {
for ( ; i < 4; i++ ) {
- if ( times.toLowerCase() == time ) {
+ if ( times[i].toLowerCase() == time ) {
sender.location.world.setTime( i * 6000 );
break;
}
}
} else {
- sender.sendMessage('This command only works in-world');
+ echo( sender, 'This command only works in-world');
}
},times);
diff --git a/src/main/js/plugins/commando/commando.js b/src/main/js/plugins/commando/commando.js
index 30f270785..331c99cdd 100644
--- a/src/main/js/plugins/commando/commando.js
+++ b/src/main/js/plugins/commando/commando.js
@@ -8,7 +8,7 @@ to Minecraft. Normally ScriptCraft only allows for provision of new
commands as extensions to the jsp command. For example, to create a
new simple command for use by all players...
- /js command('hi', function(args,player){ player.sendMessage('Hi ' + player.name); });
+ /js command('hi', function(args,player){ echo( player, 'Hi ' + player.name); });
... then players can use this command by typing...
@@ -46,7 +46,7 @@ of the ScriptCraft core.
var commando = require('../commando');
commando('hi', function(args,player){
- player.sendMessage('Hi ' + player.name);
+ echo( player, 'Hi ' + player.name);
});
...Displays a greeting to any player who issues the `/hi` command.
@@ -76,6 +76,10 @@ global commands for a plugin, please let me know.
[pcppevt]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/player/PlayerCommandPreprocessEvent.html
***/
+if (__plugin.canary){
+ console.warn('commando plugin is not yet supported in CanaryMod');
+ return;
+}
var commands = {};
exports.commando = function( name, func, options, intercepts ) {
diff --git a/src/main/js/plugins/drone/contrib/castle.js b/src/main/js/plugins/drone/contrib/castle.js
index c8ac9fcd4..3bb3f186e 100644
--- a/src/main/js/plugins/drone/contrib/castle.js
+++ b/src/main/js/plugins/drone/contrib/castle.js
@@ -1,23 +1,46 @@
-var Drone = require('../drone').Drone;
+'use strict';
+/*global require */
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.castle() method
-//
-// a castle is just a big wide fort with 4 taller forts at each corner
-//
-Drone.extend('castle', function( side, height ) {
+Creates a Castle. A castle is just a big wide fort with 4 taller forts at each corner.
+See also Drone.fort() method.
+
+#### Parameters
+
+ * side - How many blocks wide and long the castle will be (default: 24. Must be greater than 19)
+ * height - How tall the castle will be (default: 10. Must be geater than 7)
+
+#### Example
+
+At the in-game prompt you can create a castle by looking at a block and typing:
+
+```javascript
+/js castle()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the castle() method.
+
+```javascript
+var d = new Drone(player);
+d.castle();
+```
+
+
+***/
+function castle( side, height ) {
//
// use sensible default parameter values
// if no parameters are supplied
//
- if ( typeof side == "undefined" )
+ if ( typeof side == 'undefined' )
side = 24;
- if ( typeof height == "undefined" )
+ if ( typeof height == 'undefined' )
height = 10;
if ( height < 8 || side < 20 )
- throw new java.lang.RuntimeException("Castles must be at least 20 wide X 8 tall");
- //
- // remember where the drone is so it can return 'home'
- //
- this.chkpt('castle');
+ throw new java.lang.RuntimeException('Castles must be at least 20 wide X 8 tall');
//
// how big the towers at each corner will be...
//
@@ -27,23 +50,31 @@ Drone.extend('castle', function( side, height ) {
//
// the main castle building will be front and right of the first tower
//
- this.fwd(towerSide/2).right(towerSide/2);
- //
- // the castle is really just a big fort with 4 smaller 'tower' forts at each corner
- //
- this.fort(side,height);
- //
- // move back to start position
- //
- this.move('castle');
+ this
+ .chkpt('castle')
+ .fwd( towerSide / 2 )
+ .right( towerSide / 2 )
+ .fort( side, height )
+ .move( 'castle' );
//
// now place 4 towers at each corner (each tower is another fort)
//
for ( var corner = 0; corner < 4; corner++ ) {
// construct a 'tower' fort
- this.fort(towerSide,towerHeight);
- // move forward the length of the castle then turn right
- this.fwd(side+towerSide-1).turn();
+ this
+ .fort( towerSide, towerHeight )
+ .chkpt('tower-' + corner)
+ .fwd( towerSide - 1 )
+ .right( towerSide - 3 )
+ .up( towerHeight - 5 ) // create 2 doorways from main castle rampart into each tower
+ .box( blocks.air, 1, 2, 1 )
+ .back( 2 )
+ .right( 2 )
+ .box( blocks.air, 1, 2, 1 )
+ .move( 'tower-' + corner)
+ .fwd( side + towerSide - 1) // move forward the length of the castle then turn right
+ .turn();
}
- return this.move('castle');
-});
+ this.move('castle');
+}
+Drone.extend(castle);
diff --git a/src/main/js/plugins/drone/contrib/chessboard.js b/src/main/js/plugins/drone/contrib/chessboard.js
index 6f773e4f4..9088ef01c 100644
--- a/src/main/js/plugins/drone/contrib/chessboard.js
+++ b/src/main/js/plugins/drone/contrib/chessboard.js
@@ -1,21 +1,41 @@
-var Drone = require('../drone').Drone;
-var blocks = require('blocks');
-
-/**
-* Creates a tile pattern of given block types and size
-*
-* Paramters:
-* whiteBlock - blockId used for the traditional white portion of the chessboard
-* blackBlock - blockId used for the traditional black portion of the chessboard
-* width - width of the chessboard
-* height - height of the chessboard
-*/
+'use strict';
+/*global require */
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.chessboard() method
+
+Creates a tile pattern of given block types and size
+
+#### Parameters
+
+ * whiteBlock - (optional: default blocks.wool.white)
+ * blackBlock - (optional: default blocks.wool.black)
+ * width - width of the chessboard
+ * length - length of the chessboard
+
+#### Example
+
+At the in-game prompt you can create a chessboard by looking at a block and typing:
+
+```javascript
+/js chessboard()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the chessboard() method.
+
+```javascript
+var d = new Drone(player);
+d.chessboard();
+```
+
+
+***/
Drone.extend('chessboard', function( whiteBlock, blackBlock, width, depth ) {
var i,
- j,
- block;
+ j,
+ block;
- this.chkpt('chessboard-start');
if ( typeof whiteBlock == 'undefined' ) {
whiteBlock = blocks.wool.white;
@@ -29,10 +49,12 @@ Drone.extend('chessboard', function( whiteBlock, blackBlock, width, depth ) {
if ( typeof depth == 'undefined' ) {
depth = width;
}
- var wb = [ blackBlock, whiteBlock ];
+ var squares = [ blackBlock, whiteBlock ];
+
+ this.chkpt('chessboard-start');
for ( i = 0; i < depth; i++ ) {
- this.boxa( wb, width, 1, 1).fwd();
- wb = wb.reverse();
+ this.boxa( squares, width, 1, 1).fwd();
+ squares = squares.reverse();
}
- return this.move('chessboard-start');
+ this.move('chessboard-start');
});
diff --git a/src/main/js/plugins/drone/contrib/cottage.js b/src/main/js/plugins/drone/contrib/cottage.js
index b9c2272d4..b3a6405dc 100644
--- a/src/main/js/plugins/drone/contrib/cottage.js
+++ b/src/main/js/plugins/drone/contrib/cottage.js
@@ -1,39 +1,99 @@
-var Drone = require('../drone').Drone;
+'use strict';
+/*global require */
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.cottage() method
-//
-// usage:
-// [1] to build a cottage at the player's current location or the cross-hairs location...
-//
-// /js cottage();
-//
-// [2] to build a cottage using an existing drone...
-//
-// /js drone.cottage();
-//
+Creates a simple but cosy dwelling.
+
+#### Example
+
+At the in-game prompt you can create a cottage by looking at a block and typing:
+
+```javascript
+/js cottage()
+```
-Drone.extend('cottage',function ( ) {
- this.chkpt('cottage')
- .box0(48,7,2,6) // 4 walls
+Alternatively you can create a new Drone object from a Player or Location object and call the cottage() method.
+
+```javascript
+var d = new Drone(player);
+d.cottage();
+```
+
+
+***/
+function cottage( ) {
+ this
+ .chkpt('cottage')
+ .down()
+ .box(blocks.birch, 7, 1, 6) // birch wood floor
+ .up()
+ .box(blocks.air, 7, 5, 6) // clear area first
+ .box0( blocks.moss_stone, 7, 2, 6) // 4 walls
.right(3)
.door() // door front and center
.up(1)
.left(2)
- .box(102) // windows to left and right
+ .box( blocks.glass_pane ) // windows to left and right
.right(4)
- .box(102)
+ .box( blocks.glass_pane )
.left(5)
.up()
- .prism0(53,7,6)
+ .prism0( blocks.stairs.oak, 7, 6) // add a roof
.down()
.right(4)
- .sign(['Home','Sweet','Home'],68)
- .move('cottage');
-});
+ .back()
+ .wallsign(['Home','Sweet','Home'])
+ .fwd()
+ .move('cottage')
+ .right(3)
+ .fwd(4)
+ .up()
+ .hangtorch() // place a torch on wall
+ .move('cottage')
+ .right()
+ .fwd(3)
+ .bed() // place a bed against left wall
+ .fwd()
+ .right(4)
+ .box(blocks.furnace) // place a furnace against right wall
+ .move('cottage')
+ ;
+}
+/************************************************************************
+### Drone.cottage_road() method
+
+Creates a tree-lined avenue with cottages on both sides.
+
+#### Parameters
+
+ * numberOfCottages: The number of cottages to build in total (optional: default 6)
+
+#### Example
+
+At the in-game prompt you can create a cottage road by looking at a block and typing:
+
+```javascript
+/js cottage_road()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the cottage_road() method.
+
+```javascript
+var d = new Drone(player);
+d.cottage_road();
+```
+
+
+***/
+
//
// a more complex script that builds an tree-lined avenue with
// cottages on both sides.
//
-Drone.extend('cottage_road', function( numberCottages ) {
+function cottage_road( numberCottages ) {
if (typeof numberCottages == 'undefined'){
numberCottages = 6;
}
@@ -43,11 +103,15 @@ Drone.extend('cottage_road', function( numberCottages ) {
//
var cottagesPerSide = Math.floor(numberCottages/2);
this
- .chkpt('cottage_road') // make sure the drone's state is saved.
- .box( 43, 3, 1, cottagesPerSide * ( distanceBetweenTrees + 1 ) ) // build the road
+ // make sure the drone's state is saved.
+ .chkpt('cottage_road')
+ // build the road
+ .box( blocks.double_slab.stone, 3, 1, cottagesPerSide * ( distanceBetweenTrees + 1 ) )
.up()
- .right() // now centered in middle of road
- .chkpt('cr'); // will be returning to this position later
+ // now centered in middle of road
+ .right()
+ // will be returning to this position later
+ .chkpt('cottage_road_cr');
//
// step 2 line the road with trees
@@ -62,14 +126,14 @@ Drone.extend('cottage_road', function( numberCottages ) {
.fwd( distanceBetweenTrees + 1 ); // move forward.
}
this
- .move('cr')
+ .move('cottage_road_cr')
.back(6); // move back 1/2 the distance between trees
// this function builds a path leading to a cottage.
function pathAndCottage( drone ) {
drone
.down()
- .box(43,1,1,5)
+ .box(blocks.double_slab.stone, 1, 1, 5)
.fwd(5)
.left(3)
.up()
@@ -89,6 +153,8 @@ Drone.extend('cottage_road', function( numberCottages ) {
pathAndCottage( this.turn() ).move( 'r' + i );
}
// return drone to where it was at start of function
- return this.move('cottage_road');
-});
+ this.move('cottage_road');
+}
+Drone.extend(cottage_road);
+Drone.extend(cottage);
diff --git a/src/main/js/plugins/drone/contrib/dancefloor.js b/src/main/js/plugins/drone/contrib/dancefloor.js
index a1b17d641..10091d8c5 100644
--- a/src/main/js/plugins/drone/contrib/dancefloor.js
+++ b/src/main/js/plugins/drone/contrib/dancefloor.js
@@ -1,38 +1,74 @@
-var Drone = require('../drone').Drone;
+'use strict';
+/*global require, clearInterval, setInterval*/
+var Drone = require('drone'),
+ blocks = require('blocks'),
+ SECOND = 1000;
+/************************************************************************
+### Drone.dancefloor() method
+Create an animated dance floor of colored tiles some of which emit light.
+The tiles change color every second creating a strobe-lit dance-floor effect.
+See it in action here [http://www.youtube.com/watch?v=UEooBt6NTFo][ytdance]
+
+#### Parameters
+
+ * width - how wide the dancefloor should be (optional: default 5)
+ * length - how long the dancefloor should be (optional: default 5)
+ * duration - the time duration for which the lights should change (optional: default 30 seconds)
+
+#### Example
+
+At the in-game prompt you can create a dancefloor by looking at a block and typing:
+
+```javascript
+/js dancefloor()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the dancefloor() method.
+
+```javascript
+var d = new Drone(player);
+d.dancefloor();
+```
+
+[ytdance]: http://www.youtube.com/watch?v=UEooBt6NTFo
+
+***/
+
//
-// Create a floor of colored tiles some of which emit light.
-// The tiles change color every second creating a strobe-lit dance-floor.
-//
-// See it in action here => http://www.youtube.com/watch?v=UEooBt6NTFo
-//
-Drone.extend('dancefloor',function(width,length)
+function dancefloor(width, length, duration)
{
- if (typeof width == "undefined")
- width = 5;
- if (typeof length == "undefined")
- length = width;
- //
- // create a separate Drone object to lay down disco tiles
- //
- var disco = new Drone(this.x, this.y, this.z, this.dir, this.world);
- //
- // under-floor lighting
- //
- disco.down().box(89,width,1,length).up();
- var floorTiles = [35,35,'35:1','35:2','35:3','35:4','35:4','35:4','35:6',20,20];
- //
- // strobe gets called in a java thread - disco only lasts 30 seconds.
- //
- var discoTicks = 30;
- var task = null;
- var strobe = function() {
- disco.rand(floorTiles,width,1,length);
- if (!discoTicks--)
- task.cancel();
- };
- var now = 0;
- var everySecond = 20;
- task = server.scheduler.runTaskTimer(__plugin,strobe,now,everySecond);
- return this;
-});
+ if (typeof width == 'undefined')
+ width = 5;
+ if (typeof length == 'undefined')
+ length = width;
+ if (typeof duration === 'undefined'){
+ duration = 30;
+ }
+ //
+ // create a separate Drone object to lay down disco tiles
+ //
+ var disco = new Drone(this.x, this.y, this.z, this.dir, this.world);
+ //
+ // under-floor lighting
+ //
+ disco
+ .down()
+ .box( blocks.glowstone, width, 1, length)
+ .up();
+
+ //
+ // strobe gets called in a java thread - disco only lasts 30 seconds.
+ //
+ var task = null;
+ var strobe = function() {
+ disco.rand(blocks.rainbow, width, 1, length);
+ duration--;
+ if ( duration == 0 ){
+ // turn off the lights
+ clearInterval(task);
+ }
+ };
+ task = setInterval( strobe, 1 * SECOND);
+}
+Drone.extend( dancefloor );
diff --git a/src/main/js/plugins/drone/contrib/fort.js b/src/main/js/plugins/drone/contrib/fort.js
index 42a0f2c8e..0e4134661 100644
--- a/src/main/js/plugins/drone/contrib/fort.js
+++ b/src/main/js/plugins/drone/contrib/fort.js
@@ -1,14 +1,38 @@
-var Drone = require('../drone').Drone;
+'use strict';
+/*global require */
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.fort() method
-//
-// constructs a medieval fort
-//
-Drone.extend('fort', function( side, height ) {
- var brick = 98,
- turret,
+Constructs a medieval fort.
+
+#### Parameters
+
+ * side - How many blocks whide and long the fort will be (default: 18 . Must be greater than 9)
+ * height - How tall the fort will be (default: 6 . Must be greater than 3)
+
+#### Example
+
+At the in-game prompt you can create a fort by looking at a block and typing:
+
+```javascript
+/js fort()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the fort() method.
+
+```javascript
+var d = new Drone(player);
+d.fort();
+```
+
+
+***/
+function fort( side, height ) {
+ var turret,
i,
- torch,
- ladder;
+ torch;
if ( typeof side == 'undefined' ) {
side = 18;
@@ -30,8 +54,12 @@ Drone.extend('fort', function( side, height ) {
//
// build walls.
//
- this.chkpt('fort')
- .box0(brick,side,height-1,side)
+ this
+ .chkpt('fort')
+ .down()
+ .chessboard( blocks.wool.black, blocks.wool.white, side)
+ .up()
+ .box0( blocks.brick.stone, side, height - 1, side)
.up(height-1);
//
// build battlements
@@ -39,53 +67,60 @@ Drone.extend('fort', function( side, height ) {
for ( i = 0; i <= 3; i++ ) {
turret = [
- '109:'+ Drone.PLAYER_STAIRS_FACING[this.dir],
- '109:'+ Drone.PLAYER_STAIRS_FACING[(this.dir+2)%4]
+ blocks.stairs.stone ,
+ blocks.stairs.stone + ':'+ Drone.PLAYER_STAIRS_FACING[ (this.dir + 2) % 4 ]
];
- this.box(brick) // solid brick corners
+ this
+ .box( blocks.brick.stone ) // solid brick corners
.up()
- .box('50:5')
+ .box(blocks.torch)
.down() // light a torch on each corner
.fwd()
- .boxa(turret,1,1,side-2)
- .fwd(side-2)
+ .boxa( turret, 1, 1, side-2)
+ .fwd( side-2 )
.turn();
}
//
// build battlement's floor
//
- this.move('fort')
+ this
+ .move('fort')
.up(height-2)
.fwd()
.right();
for ( i = 0; i < battlementWidth; i++ ) {
-
- this.box0('126:0', side - ( 2 + (i * 2) ), 1, side - ( 2 + ( i * 2) ))
+ var bside = side - ( 2 + (i * 2) );
+ this
+ .box0( blocks.slab.oak, bside, 1, bside)
.fwd()
.right();
}
//
// add door
//
- torch = '50:' + Drone.PLAYER_TORCH_FACING[this.dir];
- this.move('fort')
- .right((side/2)-1)
+ torch = blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[this.dir];
+ this
+ .move('fort')
+ .right( ( side / 2 ) - 1 )
.door2() // double doors
.back()
.left()
.up()
- .box(torch) // left torch
- .right(3)
- .box(torch); // right torch
+ .box( torch ) // left torch
+ .right( 3 )
+ .box( torch ); // right torch
//
// add ladder up to battlements
//
- ladder = '65:' + Drone.PLAYER_SIGN_FACING[(this.dir+2)%4];
- this.move('fort')
- .right((side/2)-3)
- .fwd(1) // move inside fort
- .box(ladder, 1,height-1,1)
- .move('fort');
-});
+ this
+ .move('fort')
+ .right( ( side / 2 ) - 3 )
+ .fwd() // move inside fort
+ .turn( 2 )
+ .box( blocks.air, 1, height - 1, 1)
+ .ladder( height - 1 )
+ .move( 'fort' );
+}
+Drone.extend(fort);
diff --git a/src/main/js/plugins/drone/contrib/hangtorch.js b/src/main/js/plugins/drone/contrib/hangtorch.js
index aa15c57d7..9aacc665a 100644
--- a/src/main/js/plugins/drone/contrib/hangtorch.js
+++ b/src/main/js/plugins/drone/contrib/hangtorch.js
@@ -1,31 +1,69 @@
-var Drone = require('../drone').Drone;
-var bkMaterial = org.bukkit.Material;
+'use strict';
+/*global require, __plugin, org*/
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.hangtorch() method
-function canHang( material ) {
+Adds a hanging torch to a wall. This method will try to hang a torch
+against a wall. It will traverse backwards until it finds a block
+adjacent to air and hang the torch. If it can't find a block next to
+air it will log a message in the server.
- if ( material.equals(bkMaterial.AIR) ||
- material.equals(bkMaterial.VINE) ) {
- return true;
- } else {
- return false;
+#### Example
+
+At the in-game prompt you can create a hanging torch by looking at a
+block and typing:
+
+```javascript
+/js hangtorch()
+```
+
+Alternatively you can create a new Drone object from a Player or
+Location object and call the hangtorch() method.
+
+```javascript
+var d = new Drone(player);
+d.hangtorch();
+```
+
+***/
+function canHang( block ) {
+
+ if (__plugin.bukkit){
+ var bkMaterial = org.bukkit.Material;
+ if ( block.type.equals(bkMaterial.AIR) ||
+ block.type.equals(bkMaterial.VINE) ) {
+ return true;
+ }
+ }
+ if (__plugin.canary){
+ if (block.typeId == blocks.air ||
+ block.typeId == blocks.vines ) {
+ return true;
+ }
}
+ return false;
}
-Drone.extend('hangtorch', function () {
- var torch = '50:' + Drone.PLAYER_TORCH_FACING[this.dir];
+function hangtorch() {
+ var torch = blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[this.dir];
var moves = 0;
- var block = this.world.getBlockAt(this.x, this.y, this.z);
+ var block = this.getBlock();
- while ( !canHang(block.type) ){
+ while ( !canHang(block) ){
moves++;
this.back();
if (moves == 10){
- this.fwd(moves);
+ this
+ .fwd(moves);
console.log('nowhere to hang torch');
return;
}
- block = this.world.getBlockAt(this.x, this.y, this.z);
+ block = this.getBlock();
}
- this.box(torch)
+ this
+ .box(torch)
.fwd(moves);
-});
+}
+Drone.extend(hangtorch);
diff --git a/src/main/js/plugins/drone/contrib/lcd-clock.js b/src/main/js/plugins/drone/contrib/lcd-clock.js
index b6fc49205..300b844e5 100644
--- a/src/main/js/plugins/drone/contrib/lcd-clock.js
+++ b/src/main/js/plugins/drone/contrib/lcd-clock.js
@@ -1,75 +1,105 @@
-/*
- Experimental:
- Point at a block and issue the following ...
- /js var d = new Drone();
- /js var clock = new LCDClock(d);
- /js clock.start24();
- ... start the clock...
- /js clock.stop24();
- ... stops the clock...
-*/
-var Drone = require('../drone').Drone;
-var blocktype = require('../blocktype');
+'use strict';
+/*global require, setInterval, clearInterval, __plugin, exports*/
+/*************************************************************************
+### Drone.lcdclock() method.
+Constructs a large LCD Clock. The clock will display the current time of day.
+The clock can be stopped by calling the stopLCD() method of the Drone which created the clock.
-exports.LCDClock = function(drone, fgColor,bgColor,border) {
+#### Parameters
+
+ * foregroundBlock (Optional - default is blocks.glowstone)
+ * backgroundBlock (Optional - default is blocks.wool.black)
+ * borderBlock (Optional - a border around the LCD display - default none)
+
+#### Example
+
+At the in-game prompt you can create a LCD clock by looking at a block and typing:
+
+```javascript
+/js var clock = lcdclock()
+/js clock.stopLCD()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the lcdclock() method.
+
+```javascript
+var d = new Drone(player);
+d.lcdclock();
+d.stopLCD();
+```
+
+***/
+var blocks = require('blocks'),
+ utils = require('utils'),
+ Drone = require('drone');
+
+function lcdclock(fgColor, bgColor, border){
+ var drone = this;
var lastSecs = [0,0,0,0],
- world = drone.world,
- intervalId = -1;
+ world = drone.world,
+ intervalId = -1;
+ function update(secs) {
+ var digits = [0,0,0,0],
+ s = secs % 60,
+ m = (secs - s) / 60;
+ digits[3] = s%10;
+ digits[2] = (s-digits[3])/10;
+ digits[1] = m%10;
+ digits[0] = (m-digits[1])/10;
+ //
+ // updating all 4 digits each time is expensive
+ // only update digits which have changed (in most cases - just 1)
+ //
+ if (digits[3] != lastSecs[3]){
+ drone
+ .right(14)
+ .blocktype(''+digits[3],fgColor,bgColor, true)
+ .left(14);
+ }
+ if (digits[2] != lastSecs[2]){
+ drone
+ .right(10)
+ .blocktype(''+digits[2],fgColor,bgColor, true)
+ .left(10);
+ }
+ if (digits[1] != lastSecs[1]){
+ drone
+ .right(4)
+ .blocktype(''+digits[1], fgColor, bgColor, true)
+ .left(4);
+ }
+ if (digits[0] != lastSecs[0]){
+ drone
+ .blocktype(''+digits[0], fgColor, bgColor, true);
+ }
+ lastSecs[0] = digits[0];
+ lastSecs[1] = digits[1];
+ lastSecs[2] = digits[2];
+ lastSecs[3] = digits[3];
+
+ }
if ( typeof bgColor == 'undefined' ) {
- bgColor = '35:15'; // black wool
+ bgColor = blocks.wool.black;
}
if ( typeof fgColor == 'undefined' ) {
- fgColor = 35 ; // white wool
+ fgColor = blocks.glowstone ;
}
if ( border ) {
drone.box(border,21,9,1);
drone.up().right();
}
- drone.blocktype('00:00',fgColor,bgColor);
- return {
- start24: function( ) {
- var clock = this;
- function tick() {
- var rolloverMins = 24*60;
- var timeOfDayInMins = Math.floor(((world.time + 6000) % 24000) / 16.6667);
- timeOfDayInMins = timeOfDayInMins % rolloverMins;
- console.log('Minecraft time: ' + world.time + ' timeOfDayInMins: ' + timeOfDayInMins);
- clock.update(timeOfDayInMins);
- };
- intervalId = setInterval(tick, 800);
- },
- stop24: function() {
- clearInterval( intervalId );
- },
- update: function(secs) {
- var digits = [0,0,0,0],
- s = secs % 60;
- m = (secs - s) / 60;
- digits[3] = s%10;
- digits[2] = (s-digits[3])/10;
- digits[1] = m%10;
- digits[0] = (m-digits[1])/10;
- //
- // updating all 4 digits each time is expensive
- // only update digits which have changed (in most cases - just 1)
- //
- if (digits[3] != lastSecs[3])
- drone.right(14).blocktype(''+digits[3],fgColor,bgColor).left(14);
- if (digits[2] != lastSecs[2])
- drone.right(10).blocktype(''+digits[2],fgColor,bgColor).left(10);
- if (digits[1] != lastSecs[1])
- drone.right(4).blocktype(''+digits[1], fgColor, bgColor).left(4);
- if (digits[0] != lastSecs[0])
- drone.blocktype(''+digits[0], fgColor, bgColor);
+ drone.blocktype('00:00', fgColor, bgColor, true);
- lastSecs[0] = digits[0];
- lastSecs[1] = digits[1];
- lastSecs[2] = digits[2];
- lastSecs[3] = digits[3];
-
- }
+ function tick() {
+ var timeOfDayInMins = utils.time24(world);
+ update( timeOfDayInMins );
+ }
+ intervalId = setInterval(tick, 800);
+ this.stopLCD = function(){
+ clearInterval(intervalId);
};
-};
+}
+Drone.extend(lcdclock);
diff --git a/src/main/js/plugins/drone/contrib/logo.js b/src/main/js/plugins/drone/contrib/logo.js
index 77631eb20..c633472fa 100644
--- a/src/main/js/plugins/drone/contrib/logo.js
+++ b/src/main/js/plugins/drone/contrib/logo.js
@@ -1,69 +1,90 @@
-var Drone = require('../drone').Drone;
+'use strict';
+/*global require*/
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.logojs() method
-//
-// Constructs the JS logo
-// https://raw.github.com/voodootikigod/logo.js/master/js.png
-//
-// fg
-// the material that the letters JS will be made of
-// bg
-// the material that the square will be made of
-//
-Drone.extend('logojs', function(fg, bg) {
+Constructs a large Javascript Logo (black JS on Yellow background)
+See: https://raw.github.com/voodootikigod/logo.js/master/js.png
+
+#### Parameters
+
+ * foregroundBlock (Optional - default is blocks.wool.gray)
+ * backgroundBlock (Optional - default is blocks.gold)
+
+***/
+function logojs(fg, bg) {
// foreground defaults to gray wool
if (typeof fg == "undefined")
- fg = '35:7';
+ fg = blocks.wool.gray;
// background defaults to gold blocks
if (typeof bg == "undefined")
- bg = 41;
+ bg = blocks.gold;
// Draw the sqaure
- this.chkpt('logojs-start')
- .up()
- .box(bg, 100, 100, 1);
+ this
+ .chkpt('logojs-start')
+ .up()
+ .box(bg, 100, 100, 1);
// Draw the J, starting with the hook
- this.right(30).up(13)
- .box(fg)
- .right().down()
- .box(fg, 1, 3, 1)
- .right().down()
- .box(fg, 1, 5, 1)
- .right().down()
- .box(fg, 1, 7, 1)
- .right()
- .box(fg, 1, 8, 1)
- .right().down()
- .box(fg, 1, 10, 1)
- .right()
- .box(fg, 1, 9, 1)
- .right()
- .box(fg, 1, 8, 1)
- .right().down()
- .box(fg, 2, 8, 1)
- .right(2)
- .box(fg, 4, 7, 1)
- .right(4)
- .box(fg, 1, 8, 1)
- .right()
- .box(fg, 1, 9, 1)
- .right().up()
- .box(fg, 3, 10, 1)
- .right(3).up()
- .box(fg, 2, 9, 1)
- .right(2).up()
- .box(fg, 2, 8, 1)
- .right(2).up()
- .box(fg, 1, 7, 1)
- .right().up()
- .box(fg, 1, 6, 1)
- .right().up()
- .box(fg, 1, 5, 1)
- .right().up(2)
- .box(fg, 1, 3, 1)
- .left(9).up(3)
- .box(fg, 10, 31, 1)
+ this
+ .right(30)
+ .up(13)
+ .box(fg)
+ .right()
+ .down()
+ .box(fg, 1, 3, 1)
+ .right()
+ .down()
+ .box(fg, 1, 5, 1)
+ .right()
+ .down()
+ .box(fg, 1, 7, 1)
+ .right()
+ .box(fg, 1, 8, 1)
+ .right()
+ .down()
+ .box(fg, 1, 10, 1)
+ .right()
+ .box(fg, 1, 9, 1)
+ .right()
+ .box(fg, 1, 8, 1)
+ .right()
+ .down()
+ .box(fg, 2, 8, 1)
+ .right(2)
+ .box(fg, 4, 7, 1)
+ .right(4)
+ .box(fg, 1, 8, 1)
+ .right()
+ .box(fg, 1, 9, 1)
+ .right()
+ .up()
+ .box(fg, 3, 10, 1)
+ .right(3)
+ .up()
+ .box(fg, 2, 9, 1)
+ .right(2)
+ .up()
+ .box(fg, 2, 8, 1)
+ .right(2)
+ .up()
+ .box(fg, 1, 7, 1)
+ .right()
+ .up()
+ .box(fg, 1, 6, 1)
+ .right()
+ .up()
+ .box(fg, 1, 5, 1)
+ .right()
+ .up(2)
+ .box(fg, 1, 3, 1)
+ .left(9)
+ .up(3)
+ .box(fg, 10, 31, 1)
// Draw the S
// It's drawn in three strokes from bottom to top. Look for when
@@ -181,7 +202,7 @@ Drone.extend('logojs', function(fg, bg) {
this.move('logojs-start');
return this;
-});
+}
//
// Makes a cube of JS logos!
// This is a wrapper for logojs() so look at its docs
@@ -189,7 +210,7 @@ Drone.extend('logojs', function(fg, bg) {
// Until the drone can rotate on its Z axis we can't
// use logojs() to create top/bottom sides of cube.
//
-Drone.extend('logojscube', function(fg, bg) {
+function logojscube(fg, bg) {
this.chkpt('jscube-start')
.logojs(fg, bg);
@@ -216,4 +237,6 @@ Drone.extend('logojscube', function(fg, bg) {
.logojs(fg, bg);
return this;
-});
+}
+Drone.extend( logojs );
+Drone.extend( logojscube );
diff --git a/src/main/js/plugins/drone/contrib/mazegen.js b/src/main/js/plugins/drone/contrib/mazegen.js
index b02c22cde..1ff4a1fbb 100644
--- a/src/main/js/plugins/drone/contrib/mazegen.js
+++ b/src/main/js/plugins/drone/contrib/mazegen.js
@@ -1,19 +1,67 @@
-// Maze generation based on http://rosettacode.org/wiki/Maze_generation#JavaScript
+'use strict';
+/*global require*/
+/************************************************************************
+### Drone.maze() method
-var Drone = require('../drone').Drone;
+Maze generation based on http://rosettacode.org/wiki/Maze_generation#JavaScript
+#### Parameters
+
+ * width (optional - default 10)
+ * length (optional - default 10)
+
+#### Example
+
+At the in-game prompt you can create a maze by looking at a block and typing:
+
+```javascript
+/js maze()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the maze() method.
+
+```javascript
+var d = new Drone(player);
+d.maze();
+```
+
+
+***/
+var Drone = require('drone'),
+ blocks = require('blocks');
+
+// User-facing code starts here
+// Example: Try /js maze(5,7)
+Drone.extend( function maze( width, length ) {
+ if (typeof width === 'undefined'){
+ width = 10;
+ }
+ if (typeof length === 'undefined'){
+ length = 10;
+ }
+ var m = maze_make(width, length);
+ if (m.x > 0 && m.y > 0) {
+ maze_draw(maze_display(m), this);
+ }
+} );
+//
+// Implementation
+//
function maze_make(x,y) {
var n=x*y-1;
if (n<0) {
- echo("illegal maze dimensions");
+ console.log ("illegal maze dimensions");
return ({x: 0, y: 0});
}
- var horiz=[]; for (var j= 0; j0 && j0 && (j != here[0]+1 || k != here[1]+1));
@@ -22,12 +70,12 @@ function maze_make(x,y) {
var potential= [[here[0]+1, here[1]], [here[0],here[1]+1],
[here[0]-1, here[1]], [here[0],here[1]-1]];
var neighbors= [];
- for (var j= 0; j < 4; j++)
+ for (j= 0; j < 4; j++)
if (unvisited[potential[j][0]+1][potential[j][1]+1])
neighbors.push(potential[j]);
if (neighbors.length) {
n= n-1;
- next= neighbors[Math.floor(Math.random()*neighbors.length)];
+ var next= neighbors[Math.floor(Math.random()*neighbors.length)];
unvisited[next[0]+1][next[1]+1]= false;
if (next[0] == here[0])
horiz[next[0]][(next[1]+here[1]-1)/2]= true;
@@ -96,12 +144,3 @@ function maze_draw(maze_string, d) {
}
}
}
-
-// User-facing code starts here
-// Example: Try /js amazing(5,7)
-Drone.extend('amazing', function(size_x, size_y) {
- m = maze_make(size_x, size_y);
- if (m.x > 0 && m.y > 0) {
- maze_draw(maze_display(m), this);
- }
-});
diff --git a/src/main/js/plugins/drone/contrib/rainbow.js b/src/main/js/plugins/drone/contrib/rainbow.js
index 2575a62be..25a64451e 100644
--- a/src/main/js/plugins/drone/contrib/rainbow.js
+++ b/src/main/js/plugins/drone/contrib/rainbow.js
@@ -1,5 +1,7 @@
-var Drone = require('../drone').Drone;
-var blocks = require('blocks');
+'use strict';
+/*global require*/
+var Drone = require('drone'),
+ blocks = require('blocks');
/************************************************************************
### Drone.rainbow() method
@@ -11,14 +13,23 @@ Creates a Rainbow.
* radius (optional - default:18) - The radius of the rainbow
#### Example
-
- var d = new Drone();
- d.rainbow(30);
+
+At the in-game prompt you can create a rainbow by looking at a block and typing:
+```javascript
+/js rainbow()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the rainbow() method.
+
+```javascript
+var d = new Drone(player);
+d.rainbow(30);
+```

***/
-Drone.extend('rainbow', function(radius){
+function rainbow( radius ) {
var i,
colors,
bm;
@@ -33,7 +44,7 @@ Drone.extend('rainbow', function(radius){
colors = blocks.rainbow.slice(0);
colors.push(blocks.air);
for ( i = 0; i < colors.length; i++ ) {
- bm = this._getBlockIdAndMeta( colors[i] );
+ bm = this.getBlockIdAndMeta( colors[i] );
this.arc({
blockType: bm[0],
meta: bm[1],
@@ -44,4 +55,5 @@ Drone.extend('rainbow', function(radius){
orientation: 'vertical'}).right().up();
}
return this.move('rainbow');
-});
+}
+Drone.extend(rainbow);
diff --git a/src/main/js/plugins/drone/contrib/rboxcall.js b/src/main/js/plugins/drone/contrib/rboxcall.js
deleted file mode 100644
index 222f101f4..000000000
--- a/src/main/js/plugins/drone/contrib/rboxcall.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var Drone = require('../drone').Drone;
-
-/**
-* Iterates over each cube in a cubic region. For each cube has a chance to callback your
-* function and provide a new drone to it.
-*
-* Parameters:
-* callback - any function that accepts a drone as its first argument
-* probability - chance to invoke your callback on each iteration
-* width - width of the region
-* height - (Optional) height of the region, defaults to width
-* depth - (Optional) depth of the cube, defaults to width
-*/
-
-Drone.extend("rboxcall", function( callback, probability, width, height, depth ) {
- this.chkpt('rboxcall-start');
-
- for(var i = 0; i < width; ++i) {
- this.move('rboxcall-start').right(i);
- for(var j = 0; j < depth; ++j) {
- this.move('rboxcall-start').right(i).fwd(j);
- for(var k = 0; k < height; ++k) {
- if(Math.random()*100 < probability) {
- callback.call(null, new Drone(this.x, this.y, this.z));
- }
- this.up();
- }
- }
- }
-
- this.move('rboxcall-start');
-
- return this;
-});
diff --git a/src/main/js/plugins/drone/contrib/redstonewire.js b/src/main/js/plugins/drone/contrib/redstonewire.js
index 19d256ac3..fa74a622a 100644
--- a/src/main/js/plugins/drone/contrib/redstonewire.js
+++ b/src/main/js/plugins/drone/contrib/redstonewire.js
@@ -1,5 +1,7 @@
-var Drone = require('../drone').Drone;
-var blocks = require('blocks');
+'use strict';
+/*global require*/
+var Drone = require('drone'),
+ blocks = require('blocks');
//
// usage:
diff --git a/src/main/js/plugins/drone/contrib/skyscraper-example.js b/src/main/js/plugins/drone/contrib/skyscraper-example.js
deleted file mode 100644
index 465aad5ad..000000000
--- a/src/main/js/plugins/drone/contrib/skyscraper-example.js
+++ /dev/null
@@ -1,18 +0,0 @@
-var Drone = require('../drone').Drone;
-var blocks = require('blocks');
-
-Drone.extend('skyscraper', function( floors ) {
- var i = 0;
- if ( typeof floors == 'undefined' ) {
- floors = 10;
- }
- this.chkpt('skyscraper');
- for ( i = 0; i < floors; i++ ) {
- this // w h d
- .box( blocks.iron, 20, 1, 20) // iron floor
- .up() // w h d
- .box0(blocks.glass_pane, 20, 3, 20) // glass walls
- .up(3);
- }
- return this.move('skyscraper');
-});
diff --git a/src/main/js/plugins/drone/contrib/spawn.js b/src/main/js/plugins/drone/contrib/spawn.js
new file mode 100644
index 000000000..24f86c071
--- /dev/null
+++ b/src/main/js/plugins/drone/contrib/spawn.js
@@ -0,0 +1,8 @@
+'use strict';
+var spawnFn = require('spawn'),
+ Drone = require('drone')
+ ;
+function spawn(entityType){
+ spawnFn(entityType, this.getBlock().location);
+}
+Drone.extend(spawn);
diff --git a/src/main/js/plugins/drone/contrib/spiral_stairs.js b/src/main/js/plugins/drone/contrib/spiral_stairs.js
index ad51633c9..4f9b35674 100644
--- a/src/main/js/plugins/drone/contrib/spiral_stairs.js
+++ b/src/main/js/plugins/drone/contrib/spiral_stairs.js
@@ -1,5 +1,7 @@
-var Drone = require('../drone').Drone;
-var blocks = require('blocks');
+'use strict';
+/*global require*/
+var Drone = require('drone'),
+ blocks = require('blocks');
/************************************************************************
### Drone.spiral_stairs() method
@@ -30,17 +32,21 @@ To construct a spiral staircase 5 floors high made of oak...
spiral_stairs('oak', 5);
***/
-Drone.extend("spiral_stairs",function(stairBlock, flights){
- this.chkpt('spiral_stairs');
+function spiral_stairs(stairBlock, flights){
+ this.chkpt('spiral_stairs');
- for (var i = 0; i < flights; i++){
- this
- .box(blocks.stairs[stairBlock] + ':' + Drone.PLAYER_STAIRS_FACING[this.dir])
- .up().fwd()
- .box(blocks.stairs[stairBlock] + ':' + Drone.PLAYER_STAIRS_FACING[this.dir])
- .up().fwd()
- .box(blocks.slab[stairBlock])
- .turn().fwd();
- }
- return this.move('spiral_stairs');
-});
+ for (var i = 0; i < flights; i++){
+ this
+ .box(blocks.stairs[stairBlock] )
+ .up()
+ .fwd()
+ .box(blocks.stairs[stairBlock] )
+ .up()
+ .fwd()
+ .box(blocks.slab[stairBlock])
+ .turn()
+ .fwd();
+ }
+ this.move('spiral_stairs');
+}
+Drone.extend(spiral_stairs);
diff --git a/src/main/js/plugins/drone/contrib/streamer.js b/src/main/js/plugins/drone/contrib/streamer.js
deleted file mode 100644
index 28c8d759b..000000000
--- a/src/main/js/plugins/drone/contrib/streamer.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var Drone = require('../drone').Drone;
-/**
-* Creates a stream of blocks in a given direction until it hits something other than air
-*
-* Parameters:
-* block - blockId
-* dir - "up", "down", "left", "right", "fwd", "back
-* maxIterations - (Optional) maximum number of cubes to generate, defaults to 1000
-*/
-Drone.extend('streamer', function(block, dir, maxIterations) {
- if (typeof maxIterations == 'undefined')
- maxIterations = 1000;
-
- var usage = "Usage: streamer({block-type}, {direction: 'up', 'down', 'fwd', 'back', 'left', 'right'}, {maximum-iterations: default 1000})\nE.g.\n" +
- "streamer(5, 'up', 200)";
- if (typeof dir == 'undefined'){
- throw new Error(usage);
- }
- if (typeof block == 'undefined') {
- throw new Error(usage);
- }
- for ( var i = 0; i < maxIterations || 1000; ++i ) {
- this.box(block);
- this[dir].call(this);
- var block = this.world.getBlockAt(this.x, this.y, this.z);
- if ( block.typeId != 0 && block.data != 0) {
- break;
- }
- }
- return this;
-});
diff --git a/src/main/js/plugins/drone/contrib/temple.js b/src/main/js/plugins/drone/contrib/temple.js
index 82d52c7af..d841b0fe9 100644
--- a/src/main/js/plugins/drone/contrib/temple.js
+++ b/src/main/js/plugins/drone/contrib/temple.js
@@ -1,24 +1,55 @@
-var Drone = require('../drone').Drone;
-//
-// constructs a mayan temple
-//
-Drone.extend('temple', function(side) {
+'use strict';
+/*global require*/
+var Drone = require('drone'),
+ blocks = require('blocks');
+/************************************************************************
+### Drone.temple() method
+
+Constructs a mayan temple.
+
+#### Parameters
+
+ * side - How many blocks wide and long the temple will be (default: 20)
+
+#### Example
+
+At the in-game prompt you can create a temple by looking at a block and typing:
+
+```javascript
+/js temple()
+```
+
+Alternatively you can create a new Drone object from a Player or Location object and call the temple() method.
+
+```javascript
+var d = new Drone(player);
+d.temple();
+```
+
+
+***/
+function temple( side ) {
if ( !side ) {
side = 20;
}
- var stone = '98:1';
- var stair = '109:' + Drone.PLAYER_STAIRS_FACING[ this.dir ];
-
this.chkpt('temple');
while ( side > 4 ) {
var middle = Math.round( (side-2) / 2 );
- this.chkpt('corner')
- .box( stone, side, 1, side )
- .right( middle ).box( stair ).right().box( stair )
- .move('corner').up().fwd().right();
+ this
+ .chkpt('temple-corner')
+ .box( blocks.brick.mossy, side, 1, side )
+ .right( middle )
+ .box( blocks.stairs.stone )
+ .right()
+ .box( blocks.stairs.stone )
+ .move('temple-corner')
+ .up()
+ .fwd()
+ .right();
side = side - 2;
}
- return this.move('temple');
-});
+ this.move('temple');
+}
+Drone.extend( temple );
diff --git a/src/main/js/plugins/drone/drone-firework.js b/src/main/js/plugins/drone/drone-firework.js
deleted file mode 100644
index 8ffddbc87..000000000
--- a/src/main/js/plugins/drone/drone-firework.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var fireworks = require('fireworks');
-var Drone = require('./drone').Drone;
-Drone.extend( 'firework', function( ) {
- fireworks.firework( this.getLocation() );
-});
-
diff --git a/src/main/js/plugins/drone/drone.js b/src/main/js/plugins/drone/drone.js
index b478cb281..12c032dba 100644
--- a/src/main/js/plugins/drone/drone.js
+++ b/src/main/js/plugins/drone/drone.js
@@ -1,1909 +1,4 @@
-var utils = require('utils'),
- blocks = require('blocks'),
- bkLocation = org.bukkit.Location,
- bkPlayer = org.bukkit.entity.Player,
- bkSign = org.bukkit.block.Sign,
- bkTreeType = org.bukkit.TreeType,
- bkMaterial = org.bukkit.Material;
-/*********************************************************************
-## Drone Plugin
-
-The Drone is a convenience class for building. It can be used for...
-
- 1. Building
- 2. Copying and Pasting
-
-It uses a fluent interface which means all of the Drone's methods return `this` and can
-be chained together like so...
-
- var theDrone = new Drone();
- theDrone.up().left().box(blocks.oak).down().fwd(3).cylinder0(blocks.lava,8);
-
-### TLDNR; (Just read this if you're impatient)
-
-At the in-game command prompt type...
-
- /js box( blocks.oak )
-
-... creates a single wooden block at the cross-hairs or player location
-
- /js box( blocks.oak ).right(2).box( blocks.wool.black, 4, 9, 1)
-
-... creates a single wooden block and a 2001 black obelisk that is 4
-wide x 9 tall x 1 long in size. If you want to see what else
-ScriptCraft's Drone can do, read on...
-
-### Constructing a Drone Object
-
-Drones can be created in any of the following ways...
-
- 1. Calling any one of the methods listed below will return a Drone object. For example...
-
- var d = box( blocks.oak )
-
- ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone
- object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all
- of the Drone class's methods are also global functions that return new Drone objects.
- This is short-hand for creating drones and is useful for playing around with Drones at the in-game
- command prompt. It's shorter than typing ...
-
- var d = new Drone().box( blocks.oak )
-
- ... All of the Drone's methods return `this` so you can chain operations together like this...
-
- var d = box( blocks.oak )
- .up()
- .box( blocks.oak ,3,1,3)
- .down()
- .fwd(2)
- .box( blocks.oak )
- .turn()
- .fwd(2)
- .box( blocks.oak )
- .turn()
- .fwd(2)
- .box( blocks.oak );
-
- 2. Using the following form...
-
- d = new Drone()
-
- ...will create a new Drone. If the cross-hairs are pointing at a
- block at the time then, that block's location becomes the drone's
- starting point. If the cross-hairs are _not_ pointing at a block,
- then the drone's starting location will be 2 blocks directly in
- front of the player. TIP: Building always happens right and front
- of the drone's position...
-
- Plan View:
-
- ^
- |
- |
- D---->
-
- For convenience you can use a _corner stone_ to begin building.
- The corner stone should be located just above ground level. If
- the cross-hair is point at or into ground level when you create a
- new Drone(), then building begins at that point. You can get
- around this by pointing at a 'corner stone' just above ground
- level or alternatively use the following statement...
-
- d = new Drone().up();
-
- ... which will move the drone up one block as soon as it's created.
-
- 
-
- 3. Or by using the following form...
-
- d = new Drone(x,y,z,direction,world);
-
- This will create a new Drone at the location you specified using
- x, y, z In minecraft, the X axis runs west to east and the Z axis runs
- north to south. The direction parameter says what direction you want
- the drone to face: 0 = east, 1 = south, 2 = west, 3 = north. If the
- direction parameter is omitted, the player's direction is used
- instead.
-
- Both the `direction` and `world` parameters are optional.
-
- 4. Create a new Drone based on a Bukkit Location object...
-
- d = new Drone(location);
-
- This is useful when you want to create a drone at a given
- `org.bukkit.Location` . The `Location` class is used throughout
- the bukkit API. For example, if you want to create a drone when a
- block is broken at the block's location you would do so like
- this...
-
- events.blockBreak( function( event) {
- var location = event.block.location;
- var drone = new Drone(location);
- // do more stuff with the drone here...
- });
-
-#### Parameters
-
- * location (optional) : *NB* If an `org.bukkit.Location` object is provided as a parameter, then it should be the only parameter.
- * x (optional) : The x coordinate of the Drone
- * y (optional) : The y coordinate of the Drone
- * z (optional) : The z coordinate of the Drone
- * direction (optional) : The direction in which the Drone is
- facing. Possible values are 0 (east), 1 (south), 2 (west) or 3 (north)
- * world (optional) : The world in which the drone is created.
-
-### Drone.box() method
-
-the box() method is a convenience method for building things. (For the more performance-oriented method - see cuboid)
-
-#### parameters
-
- * b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * w (optional - default 1) - the width of the structure
- * h (optional - default 1) - the height of the structure
- * d (optional - default 1) - the depth of the structure - NB this is
- not how deep underground the structure lies - this is how far
- away (depth of field) from the drone the structure will extend.
-
-#### Example
-
-To create a black structure 4 blocks wide, 9 blocks tall and 1 block long...
-
- box(blocks.wool.black, 4, 9, 1);
-
-... or the following code does the same but creates a variable that can be used for further methods...
-
- var drone = new Drone();
- drone.box(blocks.wool.black, 4, 9, 1);
-
-
-
-### Drone.box0() method
-
-Another convenience method - this one creates 4 walls with no floor or ceiling.
-
-#### Parameters
-
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * width (optional - default 1) - the width of the structure
- * height (optional - default 1) - the height of the structure
- * length (optional - default 1) - the length of the structure - how far
- away (depth of field) from the drone the structure will extend.
-
-#### Example
-
-To create a stone building with the insided hollowed out 7 wide by 3 tall by 6 long...
-
- box0( blocks.stone, 7, 3, 6);
-
-
-
-### Drone.boxa() method
-
-Construct a cuboid using an array of blocks. As the drone moves first along the width axis,
-then the height (y axis) then the length, each block is picked from the array and placed.
-
-#### Parameters
-
- * blocks - An array of blocks - each block in the array will be placed in turn.
- * width
- * height
- * length
-
-#### Example
-
-Construct a rainbow-colored road 100 blocks long...
-
- var rainbowColors = [blocks.wool.red, blocks.wool.orange, blocks.wool.yellow, blocks.wool.lime,
- blocks.wool.lightblue, blocks.wool.blue, blocks.wool.purple];
-
- boxa(rainbowColors,7,1,30);
-
-
-
-### Drone Movement
-
-Drones can move freely in minecraft's 3-D world. You control the
-Drone's movement using any of the following methods..
-
- * up()
- * down()
- * left()
- * right()
- * fwd()
- * back()
- * turn()
-
-... Each of these methods takes a single optional parameter
-`numBlocks` - the number of blocks to move in the given direction. If
-no parameter is given, the default is 1.
-
-to change direction use the `turn()` method which also takes a single
-optional parameter (numTurns) - the number of 90 degree turns to make.
-Turns are always clock-wise. If the drone is facing north, then
-drone.turn() will make the turn face east. If the drone is facing east
-then drone.turn(2) will make the drone turn twice so that it is facing
-west.
-
-### Drone Positional Info
-
- * getLocation() - Returns a Bukkit Location object for the drone
-
-### Drone Markers
-
-Markers are useful when your Drone has to do a lot of work. You can
-set a check-point and return to the check-point using the move()
-method. If your drone is about to undertake a lot of work -
-e.g. building a road, skyscraper or forest you should set a
-check-point before doing so if you want your drone to return to its
-current location.
-
-A 'start' checkpoint is automatically created when the Drone is first created.
-
-Markers are created and returned to using the followng two methods...
-
- * chkpt - Saves the drone's current location so it can be returned to later.
- * move - moves the drone to a saved location. Alternatively you can provide an
- org.bukkit.Location object or x,y,z and direction parameters.
-
-#### Parameters
-
- * name - the name of the checkpoint to save or return to.
-
-#### Example
-
- drone.chkpt('town-square');
- //
- // the drone can now go off on a long excursion
- //
- for ( i = 0; i< 100; i++) {
- drone.fwd(12).box(6);
- }
- //
- // return to the point before the excursion
- //
- drone.move('town-square');
-
-### Drone.prism() method
-
-Creates a prism. This is useful for roofs on houses.
-
-#### Parameters
-
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * width - the width of the prism
- * length - the length of the prism (will be 2 time its height)
-
-#### Example
-
- prism(blocks.oak,3,12);
-
-
-
-### Drone.prism0() method
-
-A variation on `prism` which hollows out the inside of the prism. It uses the same parameters as `prism`.
-
-### Drone.cylinder() method
-
-A convenience method for building cylinders. Building begins radius blocks to the right and forward.
-
-#### Parameters
-
- * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
- Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch`
- * radius
- * height
-
-#### Example
-
-To create a cylinder of Iron 7 blocks in radius and 1 block high...
-
- cylinder(blocks.iron, 7 , 1);
-
-
-
-### Drone.cylinder0() method
-
-A version of cylinder that hollows out the middle.
-
-#### Example
-
-To create a hollow cylinder of Iron 7 blocks in radius and 1 block high...
-
- cylinder0(blocks.iron, 7, 1);
-
-
-
-### Drone.arc() method
-
-The arc() method can be used to create 1 or more 90 degree arcs in the horizontal or vertical planes.
-This method is called by cylinder() and cylinder0() and the sphere() and sphere0() methods.
-
-#### Parameters
-
-arc() takes a single parameter - an object with the following named properties...
-
- * radius - The radius of the arc.
- * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv].
- * meta - The metadata value. See [Data Values][dv].
- * orientation (default: 'horizontal' ) - the orientation of the arc - can be 'vertical' or 'horizontal'.
- * stack (default: 1 ) - the height or length of the arc (depending on
- the orientation - if orientation is horizontal then this parameter
- refers to the height, if vertical then it refers to the length ).
- * strokeWidth (default: 1 ) - the width of the stroke (how many
- blocks) - if drawing nested arcs it's usually a good idea to set
- strokeWidth to at least 2 so that there are no gaps between each
- arc. The arc method uses a [bresenham algorithm][bres] to plot
- points along the circumference.
- * fill - If true (or present) then the arc will be filled in.
- * quadrants (default:
- `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An
- object with 4 properties indicating which of the 4 quadrants of a
- circle to draw. If the quadrants property is absent then all 4
- quadrants are drawn.
-
-#### Examples
-
-To draw a 1/4 circle (top right quadrant only) with a radius of 10 and stroke width of 2 blocks ...
-
- arc({blockType: blocks.iron,
- meta: 0,
- radius: 10,
- strokeWidth: 2,
- quadrants: { topright: true },
- orientation: 'vertical',
- stack: 1,
- fill: false
- } );
-
-
-
-[bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
-[dv]: http://www.minecraftwiki.net/wiki/Data_values
-
-### Drone.door() method
-
-create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created.
-
-#### Parameters
-
- * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
-
-#### Example
-
-To create a wooden door at the crosshairs/drone's location...
-
- var drone = new Drone();
- drone.door();
-
-To create an iron door...
-
- drone.door( blocks.door_iron );
-
-
-
-### Drone.door_iron() method
-
-create an Iron door.
-
-### Drone.door2() method
-
-Create double doors (left and right side)
-
-#### Parameters
-
- * doorType (optional - default wood) - If a parameter is provided then the door is Iron.
-
-#### Example
-
-To create double-doors at the cross-hairs/drone's location...
-
- drone.door2();
-
-
-
-### Drone.door2_iron() method
-
-Create double iron doors
-
-
-### Drone.sign() method
-
-Signs must use block 63 (stand-alone signs) or 68 (signs on walls)
-
-#### Parameters
-
- * message - can be a string or an array of strings.
- * block - can be 63 or 68
-
-#### Example
-
-To create a free-standing sign...
-
- drone.sign(["Hello","World"],63);
-
-
-
-... to create a wall mounted sign...
-
- drone.sign(["Welcome","to","Scriptopia"], 68 );
-
-
-
-### Drone Trees methods
-
- * oak()
- * spruce()
- * birch()
- * jungle()
-
-#### Example
-
-To create 4 trees in a row, point the cross-hairs at the ground then type `/js ` and ...
-
- up( ).oak( ).right(8 ).spruce( ).right(8 ).birch( ).right(8 ).jungle( );
-
-Trees won't always generate unless the conditions are right. You
-should use the tree methods when the drone is directly above the
-ground. Trees will usually grow if the drone's current location is
-occupied by Air and is directly above an area of grass (That is why
-the `up( )` method is called first).
-
-
-
-None of the tree methods require parameters. Tree methods will only be successful
-if the tree is placed on grass in a setting where trees can grow.
-
-### Drone.garden() method
-
-places random flowers and long grass (similar to the effect of placing bonemeal on grass)
-
-#### Parameters
-
- * width - the width of the garden
- * length - how far from the drone the garden extends
-
-#### Example
-
-To create a garden 10 blocks wide by 5 blocks long...
-
- garden(10,5);
-
-
-
-### Drone.rand() method
-
-rand takes either an array (if each blockid has the same chance of occurring)
-or an object where each property is a blockid and the value is it's weight (an integer)
-
-#### Example
-
-place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked)
-
- rand( [blocks.brick.stone, blocks.brick.mossy, blocks.brick.cracked ],w,d,h)
-
-to place random blocks stone has a 50% chance of being picked,
-
- var distribution = {};
- distribution[ blocks.brick.stone ] = 5;
- distribution[ blocks.brick.mossy ] = 3;
- distribution[ blocks.brick.cracked ] = 2;
-
- rand( distribution, width, height, depth)
-
-regular stone has a 50% chance, mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked.
-
-### Copy & Paste using Drone
-
-A drone can be used to copy and paste areas of the game world.
-
-### Drone.copy() method
-
-Copies an area so it can be pasted elsewhere. The name can be used for
-pasting the copied area elsewhere...
-
-#### Parameters
-
- * name - the name to be given to the copied area (used by `paste`)
- * width - the width of the area to copy
- * height - the height of the area to copy
- * length - the length of the area (extending away from the drone) to copy
-
-#### Example
-
- drone.copy('somethingCool',10,5,10 ).right(12 ).paste('somethingCool' );
-
-### Drone.paste() method
-
-Pastes a copied area to the current location.
-
-#### Example
-
-To copy a 10x5x10 area (using the drone's coordinates as the starting
-point) into memory. the copied area can be referenced using the name
-'somethingCool'. The drone moves 12 blocks right then pastes the copy.
-
- drone.copy('somethingCool',10,5,10 )
- .right(12 )
- .paste('somethingCool' );
-
-### Chaining
-
-All of the Drone methods return a Drone object, which means methods
-can be 'chained' together so instead of writing this...
-
- drone = new Drone();
- drone.fwd(3);
- drone.left(2);
- drone.box(2); // create a grass block
- drone.up();
- drone.box(2); // create another grass block
- drone.down();
-
-...you could simply write ...
-
- var drone = new Drone().fwd(3).left(2).box(2).up().box(2).down();
-
-... since each Drone method is also a global function that constructs
-a drone if none is supplied, you can shorten even further to just...
-
- fwd(3).left(2).box(2).up().box(2).down()
-
-The Drone object uses a [Fluent Interface][fl] to make ScriptCraft
-scripts more concise and easier to write and read. Minecraft's
-in-game command prompt is limited to about 80 characters so chaining
-drone commands together means more can be done before hitting the
-command prompt limit. For complex building you should save your
-commands in a new script file and load it using /js load()
-
-[fl]: http://en.wikipedia.org/wiki/Fluent_interface
-
-### Drone Properties
-
- * x - The Drone's position along the west-east axis (x increases as you move east)
- * y - The Drone's position along the vertical axis (y increses as you move up)
- * z - The Drone's position along the north-south axis (z increases as you move south)
- * dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north.
-
-### Extending Drone
-
-The Drone object can be easily extended - new buidling recipes/blueprints can be added and can
-become part of a Drone's chain using the *static* method `Drone.extend`.
-
-### Drone.extend() static method
-
-Use this method to add new methods (which also become chainable global functions) to the Drone object.
-
-#### Parameters
-
- * name - The name of the new method e.g. 'pyramid'
- * function - The method body.
-
-#### Example
-
- // submitted by [edonaldson][edonaldson]
- Drone.extend('pyramid', function( block,height) {
- this.chkpt('pyramid');
- for ( var i = height; i > 0; i -= 2) {
- this.box(block, i, 1, i).up().right().fwd();
- }
- return this.move('pyramid');
- });
-
-Once the method is defined (it can be defined in a new pyramid.js file) it can be used like so...
-
- var d = new Drone();
- d.pyramid(blocks.brick.stone, 12);
-
-... or simply ...
-
- pyramid(blocks.brick.stone, 12);
-
-[edonaldson]: https://github.com/edonaldson
-
-### Drone Constants
-
-#### Drone.PLAYER_STAIRS_FACING
-
-An array which can be used when constructing stairs facing in the Drone's direction...
-
- var d = new Drone();
- d.box(blocks.stairs.oak + ':' + Drone.PLAYER_STAIRS_FACING[d.dir]);
-
-... will construct a single oak stair block facing the drone.
-
-#### Drone.PLAYER_SIGN_FACING
-
-An array which can be used when placing signs so they face in a given direction.
-This is used internally by the Drone.sign() method. It should also be used for placing
-any of the following blocks...
-
- * chest
- * ladder
- * furnace
- * dispenser
-
-To place a chest facing the Drone ...
-
- drone.box( blocks.chest + ':' + Drone.PLAYER_SIGN_FACING[drone.dir]);
-
-#### Drone.PLAYER_TORCH_FACING
-
-Used when placing torches so that they face towards the drone.
-
- drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[drone.dir]);
-
-***/
-
-//
-// Implementation
-// ==============
-//
-// There is no need to read any further unless you want to understand how the Drone object works.
-//
-
-var putBlock = function( x, y, z, blockId, metadata, world ) {
- if ( typeof metadata == 'undefined' ) {
- metadata = 0;
- }
- var block = world.getBlockAt( x, y, z );
- if ( block.typeId != blockId || block.data != metadata ) {
- block.setTypeIdAndData( blockId, metadata, false );
- }
-};
-
-var putSign = function( drone, x, y, z, world, texts, blockId, meta, immediate ) {
- var i,
- block,
- state;
-
- if ( !immediate ) {
- getQueue(drone).push(function(){ putSign( drone, x, y, z, world, texts, blockId, meta, true); });
- return;
- }
- if ( blockId != 63 && blockId != 68 ) {
- throw new Error( 'Invalid Parameter: blockId must be 63 or 68' );
- }
- putBlock( x, y, z, blockId, meta, world );
- block = world.getBlockAt( x, y, z );
- state = block.state;
- if ( state instanceof bkSign ) {
- for ( i = 0; i < texts.length; i++ ) {
- state.setLine( i % 4, texts[ i ] );
- }
- state.update( true );
- }
-};
-
-var Drone = function( x, y, z, dir, world ) {
- this.record = false;
- var usePlayerCoords = false;
- var player = (typeof self !== 'undefined' ? self : null);
- if ( x instanceof bkPlayer ) {
- player = x;
- }
- var playerPos = utils.getPlayerPos( player );
- var that = this;
- var populateFromLocation = function( loc ) {
- that.x = loc.x;
- that.y = loc.y;
- that.z = loc.z;
- that.dir = _getDirFromRotation(loc.yaw);
- that.world = loc.world;
- };
- var mp = utils.getMousePos( player );
- if ( typeof x == 'undefined' || x instanceof bkPlayer ) {
- if ( mp ) {
- populateFromLocation( mp );
- if ( playerPos ) {
- this.dir = _getDirFromRotation(playerPos.yaw);
- }
- } else {
- // base it on the player's current location
- usePlayerCoords = true;
- //
- // it's possible that drone.js could be loaded by a non-playing op
- // (from the server console)
- //
- if ( !playerPos ) {
- return null;
- }
- populateFromLocation( playerPos );
- }
- } else {
- if ( arguments[0] instanceof bkLocation ) {
- populateFromLocation( arguments[ 0 ] );
- } else {
- this.x = x;
- this.y = y;
- this.z = z;
- if ( typeof dir == 'undefined' ) {
- this.dir = _getDirFromRotation( playerPos.yaw );
- } else {
- this.dir = dir%4;
- }
- if ( typeof world == 'undefined' ) {
- this.world = playerPos.world;
- } else {
- this.world = world;
- }
- }
- }
-
- if ( usePlayerCoords ) {
- this.fwd( 3 );
- }
- this.chkpt( 'start' );
- this.record = true;
- this.history = [];
- this.player = player;
- return this;
-};
-
-exports.Drone = Drone;
-/*
- because this is a plugin, any of its exports will be exported globally.
- Since 'blocks' is a module not a plugin it is convenient to export it via
- the Drone module.
- */
+/*global require, exports*/
+var blocks = require('blocks');
+exports.Drone = require('drone');
exports.blocks = blocks;
-
-Drone.queue = [];
-
-Drone.opsPerSec = 10;
-Drone.processQueue = function(){
- var process,
- i = 0,
- queues = getAllQueues();
-
- for ( ; i < queues.length; i++ ) {
- process = queues[i].shift();
- if (process){
- try {
- process();
- } catch( e ) {
- console.log('Drone build error: %s', e);
- }
- }
- }
- setTimeout( Drone.processQueue, 1000 / Drone.opsPerSec );
-};
-setTimeout( Drone.processQueue, 1000 / Drone.opsPerSec );
-
-addUnloadHandler( function() {
- var pendingBuildOps = 0;
- var allQueues = getAllQueues();
- for (var i = 0; i < allQueues.length; i++){
- pendingBuildOps += allQueues[i].length;
- }
- if (pendingBuildOps > 0){
- console.warn('There were ' + pendingBuildOps + ' pending build operations which were cancelled');
- }
-});
-//
-// add custom methods to the Drone object using this function
-//
-Drone.extend = function( name, func ) {
- Drone.prototype[ '_' + name ] = func;
- Drone.prototype[ name ] = function( ) {
- if ( this.record ) {
- this.history.push( [ name, arguments ] );
- }
- var oldVal = this.record;
- this.record = false;
- this[ '_' + name ].apply( this, arguments );
- this.record = oldVal;
- return this;
- };
-
- global[name] = function( ) {
- var result = new Drone( self );
- result[name].apply( result, arguments );
- return result;
- };
-};
-
-/**************************************************************************
-### Drone.times() Method
-
-The times() method makes building multiple copies of buildings easy. It's possible to create rows or grids of buildings without resorting to `for` or `while` loops.
-
-#### Parameters
-
- * numTimes (optional - default 2) : The number of times you want to repeat the preceding statements.
-
-#### Example
-
-Say you want to do the same thing over and over. You have a couple of options...
-
- * You can use a for loop...
-
- d = new Drone(); for ( var i =0;i < 4; i++) { d.cottage().right(8); }
-
-While this will fit on the in-game prompt, it's awkward. You need to
-declare a new Drone object first, then write a for loop to create the
-4 cottages. It's also error prone, even the `for` loop is too much
-syntax for what should really be simple.
-
- * You can use a while loop...
-
- d = new Drone(); var i=4; while (i--) { d.cottage().right(8); }
-
-... which is slightly shorter but still too much syntax. Each of the
-above statements is fine for creating a 1-dimensional array of
-structures. But what if you want to create a 2-dimensional or
-3-dimensional array of structures? Enter the `times()` method.
-
-The `times()` method lets you repeat commands in a chain any number of
-times. So to create 4 cottages in a row you would use the following
-statement...
-
- cottage().right(8).times(4);
-
-...which will build a cottage, then move right 8 blocks, then do it
-again 4 times over so that at the end you will have 4 cottages in a
-row. What's more the `times()` method can be called more than once in
-a chain. So if you wanted to create a *grid* of 20 houses ( 4 x 5 ),
-you would do so using the following statement...
-
- cottage().right(8).times(4).fwd(8).left(32).times(5);
-
-... breaking it down...
-
- 1. The first 3 calls in the chain ( `cottage()`, `right(8)`,
- `times(4)` ) build a single row of 4 cottages.
-
- 2. The last 3 calls in the chain ( `fwd(8)`, `left(32)`, `times(5)` )
- move the drone forward 8 then left 32 blocks (4 x 8) to return to
- the original x coordinate, then everything in the chain is
- repeated again 5 times so that in the end, we have a grid of 20
- cottages, 4 x 5. Normally this would require a nested loop but
- the `times()` method does away with the need for loops when
- repeating builds.
-
-Another example: This statement creates a row of trees 2 by 3 ...
-
- oak().right(10).times(2).left(20).fwd(10).times(3)
-
-... You can see the results below.
-
-
-
-***/
-Drone.prototype.times = function( numTimes, commands ) {
- if ( typeof numTimes == 'undefined' ) {
- numTimes = 2;
- }
- if ( typeof commands == 'undefined' ) {
- commands = this.history.concat();
- }
-
- this.history = [ [ 'times', [ numTimes + 1, commands ] ] ];
- var oldVal = this.record;
- this.record = false;
- for ( var j = 1; j < numTimes; j++ ) {
- for ( var i = 0; i < commands.length; i++) {
- var command = commands[i];
- var methodName = command[0];
- var args = command[1];
- this[ methodName ].apply( this, args );
- }
- }
- this.record = oldVal;
- return this;
-};
-
-Drone.prototype._checkpoints = {};
-
-Drone.extend( 'chkpt', function( name ) {
- this._checkpoints[ name ] = { x:this.x, y:this.y, z:this.z, dir:this.dir };
-} );
-
-Drone.extend( 'move', function( ) {
- if ( arguments[0] instanceof bkLocation ) {
- this.x = arguments[0].x;
- this.y = arguments[0].y;
- this.z = arguments[0].z;
- this.dir = _getDirFromRotation(arguments[0].yaw );
- this.world = arguments[0].world;
- } else if ( typeof arguments[0] === 'string' ) {
- var coords = this._checkpoints[arguments[0]];
- if ( coords ) {
- this.x = coords.x;
- this.y = coords.y;
- this.z = coords.z;
- this.dir = coords.dir%4;
- }
- } else {
- // expect x,y,z,dir
- switch( arguments.length ) {
- case 4:
- this.dir = arguments[3];
- case 3:
- this.z = arguments[2];
- case 2:
- this.y = arguments[1];
- case 1:
- this.x = arguments[0];
- }
- }
-} );
-
-Drone.extend( 'turn', function ( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- this.dir += n;
- this.dir %=4;
-} );
-
-Drone.extend( 'right', function( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- _movements[ this.dir ].right( this, n );
-});
-
-Drone.extend( 'left', function( n ) {
- if ( typeof n == 'undefined') {
- n = 1;
- }
- _movements[ this.dir ].left( this, n );
-});
-
-Drone.extend( 'fwd', function( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- _movements[ this.dir ].fwd( this, n );
-});
-
-Drone.extend( 'back', function( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- _movements[ this.dir ].back( this, n );
-});
-
-Drone.extend( 'up', function( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- this.y+= n;
-});
-
-Drone.extend( 'down', function( n ) {
- if ( typeof n == 'undefined' ) {
- n = 1;
- }
- this.y-= n;
-});
-//
-// position
-//
-Drone.prototype.getLocation = function( ) {
- return new bkLocation( this.world, this.x, this.y, this.z );
-};
-//
-// building
-//
-Drone.extend( 'sign', function( message, block ) {
- if ( message.constructor != Array ) {
- message = [message];
- }
- var bm = this._getBlockIdAndMeta( block );
- block = bm[0];
- var meta = bm[1];
- if ( block != 63 && block != 68 ) {
- var usage = 'Usage: sign("message", "63:1") or sign("message","68:1")';
- if ( this.player ) {
- this.player.sendMessage(usage);
- }
- console.error(usage);
- return;
- }
- if ( block == 68 ) {
- meta = Drone.PLAYER_SIGN_FACING[ this.dir % 4 ];
- this.back();
- }
- if ( block == 63 ) {
- meta = ( 12 + ( ( this.dir + 2 ) * 4 ) ) % 16;
- }
- putSign( this, this.x, this.y, this.z, this.world, message, block, meta);
- if ( block == 68 ) {
- this.fwd();
- }
-});
-
-var playerQueues = {};
-/*
- if the drone has an associated player, then use that player's queue otherwise
- use the global queue.
-*/
-function getQueue( drone ){
- if ( drone.player ) {
- var playerName = ''+drone.player.name;
- var result = playerQueues[playerName];
- if (result === undefined){
- playerQueues[playerName] = [];
- return playerQueues[playerName];
- }
- return result;
- } else {
- return Drone.queue;
- }
-}
-function getAllQueues() {
- var result = [ Drone.queue ];
- for (var pq in playerQueues) {
- result.push(playerQueues[pq]) ;
- }
- return result;
-}
-Drone.prototype.cuboida = function(/* Array */ blocks, w, h, d, overwrite, immediate ) {
-
- var len = blocks.length,
- i = 0;
-
- if ( !immediate ) {
- for ( ; i < len; i++ ) {
- blocks[i] = this._getBlockIdAndMeta( blocks[ i ] );
- }
- var clone = Drone.clone(this);
- getQueue(this).push(this.cuboida.bind(clone, blocks, w, h, d, overwrite, true) );
- return this;
- }
- if ( typeof overwrite == 'undefined' ) {
- overwrite = true;
- }
- if ( typeof h == 'undefined' ) {
- h = 1;
- }
- if ( typeof d == 'undefined' ) {
- d = 1;
- }
- if ( typeof w == 'undefined' ) {
- w = 1;
- }
- var that = this;
- var dir = this.dir;
- var bi = 0;
- _traverse[dir].depth( that, d, function( ) {
- _traverseHeight( that, h, function( ) {
- _traverse[dir].width( that, w, function( ) {
- var block = that.world.getBlockAt( that.x, that.y, that.z );
- var properBlock = blocks[ bi % len ];
- if (overwrite || block.type.equals(bkMaterial.AIR) ) {
- block.setTypeIdAndData( properBlock[0], properBlock[1], false );
- }
- bi++;
- });
- });
- });
- return this;
-
-};
-Drone.MAX_VOLUME = 1000000;
-Drone.MAX_SIDE = 1000;
-
-var tooBig = function(w, h, d ) {
- return ( w * h * d ) >= Drone.MAX_VOLUME ||
- ( w >= Drone.MAX_SIDE ) ||
- ( h >= Drone.MAX_SIDE ) ||
- ( d >= Drone.MAX_SIDE );
-};
-/*
- faster cuboid because blockid, meta and world must be provided
- use this method when you need to repeatedly place blocks
- */
-Drone.prototype.cuboidX = function( blockType, meta, w, h, d, immediate ) {
-
- if ( typeof h == 'undefined' ) {
- h = 1;
- }
- if ( typeof d == 'undefined' ) {
- d = 1;
- }
- if ( typeof w == 'undefined' ) {
- w = 1;
- }
- if ( tooBig( w, h, d ) ) {
- this.sign([
- 'Build too Big!',
- 'width:' + w,
- 'height:' + h,
- 'depth:' + d
- ], 68);
- console.warn('Build too big! ' + w + ' X ' + h + ' X ' + d);
- return this;
- }
- var that = this;
- var dir = this.dir;
-
- if ( !immediate ) {
- var clone = Drone.clone(this);
- getQueue(this).push(this.cuboidX.bind(clone, blockType, meta, w, h, d, true));
- return this;
- }
- var depthFunc = function( ) {
-
- var block = that.world.getBlockAt( that.x, that.y, that.z );
- block.setTypeIdAndData( blockType, meta, false );
- // wph 20130210 - dont' know if this is a bug in bukkit but for chests,
- // the metadata is ignored (defaults to 2 - south facing)
- // only way to change data is to set it using property/bean.
- block.data = meta;
- };
- var heightFunc = function( ) {
- _traverse[dir].depth( that, d, depthFunc );
- };
- var widthFunc = function( ) {
- _traverseHeight( that, h, heightFunc );
- };
- _traverse[dir].width( that, w, widthFunc );
- return this;
-
-};
-
-Drone.prototype.cuboid = function( block, w, h, d ) {
- var bm = this._getBlockIdAndMeta( block );
- return this.cuboidX( bm[0], bm[1], w, h, d );
-};
-
-Drone.prototype.cuboid0 = function( block, w, h, d ) {
- this.chkpt( 'start_point' );
-
- // Front wall
- this.cuboid( block, w, h, 1 );
- // Left wall
- this.cuboid( block, 1, h, d );
- // Right wall
- this.right( w - 1 ).cuboid( block, 1, h, d ).left( w - 1 );
- // Back wall
- this.fwd( d - 1 ).cuboid( block, w, h, 1 );
-
- return this.move( 'start_point' );
-};
-
-
-Drone.extend( 'door', function( doorMaterial ) {
- if ( typeof doorMaterial == 'undefined' ) {
- doorMaterial = 64; // wood
- } else {
- doorMaterial = 71; // iron
- }
- this.cuboidX( doorMaterial, this.dir )
- .up( )
- .cuboidX( doorMaterial, 8 )
- .down( );
-} );
-
-Drone.extend( 'door_iron', function( ) {
- this.cuboidX( 71, this.dir )
- .up( )
- .cuboidX( 71, 8 )
- .down( );
-} );
-
-Drone.extend( 'door2' , function( doorMaterial ) {
- if ( typeof doorMaterial == 'undefined' ) {
- doorMaterial = 64;
- } else {
- doorMaterial = 71;
- }
- this
- .cuboidX( doorMaterial, this.dir ).up( )
- .cuboidX( doorMaterial, 8 ).right( )
- .cuboidX( doorMaterial, 9 ).down( )
- .cuboidX( doorMaterial, this.dir ).left( );
-} );
-Drone.extend( 'door2_iron' , function( ) {
- this
- .cuboidX( 71, this.dir ).up( )
- .cuboidX( 71, 8 ).right( )
- .cuboidX( 71, 9 ).down( )
- .cuboidX( 71, this.dir ).left( );
-} );
-
-// player dirs: 0 = east, 1 = south, 2 = west, 3 = north
-// block dirs: 0 = east, 1 = west, 2 = south , 3 = north
-// sign dirs: 5 = east, 3 = south, 4 = west, 2 = north
-Drone.PLAYER_STAIRS_FACING = [ 0, 2, 1, 3 ];
-// for blocks 68 (wall signs) 65 (ladders) 61,62 (furnaces) 23 (dispenser) and 54 (chest)
-Drone.PLAYER_SIGN_FACING = [ 4, 2, 5, 3 ];
-Drone.PLAYER_TORCH_FACING = [ 2, 4, 1, 3 ];
-
-var _STAIRBLOCKS = {
- 53: '5:0' // oak wood
- ,67: 4 // cobblestone
- ,108: 45 // brick
- ,109: 98 // stone brick
- ,114: 112 // nether brick
- ,128: 24 // sandstone
- ,134: '5:1' // spruce wood
- ,135: '5:2' // birch wood
- ,136: '5:3' // jungle wood
-};
-//
-// prism private implementation
-//
-var _prism = function( block, w, d ) {
- var stairEquiv = _STAIRBLOCKS[block];
- if ( stairEquiv ) {
- this.fwd( ).prism(stairEquiv,w,d-2 ).back( );
- var d2 = 0;
- var middle = Math.floor( d/2 );
- var uc = 0,dc = 0;
- while ( d2 < d ) {
- var di = (d2 < middle?this.dir:(this.dir+2 )%4 );
- var bd = block + ':' + Drone.PLAYER_STAIRS_FACING[di];
- var putStep = true;
- if ( d2 == middle ) {
- if ( d % 2 == 1 ) {
- putStep = false;
- }
- }
- if ( putStep ) {
- this.cuboid(bd,w );
- }
- if ( d2 < middle-1 ) {
- this.up( );
- uc++;
- }
- var modulo = d % 2;
- if ( modulo == 1 ) {
- if ( d2 > middle && d2= middle && d2= 1 ) {
- this.cuboid(block,w,1,d2 );
- d2 -= 2;
- this.fwd( ).up( );
- c++;
- }
- this.down(c ).back(c );
- }
- return this;
-};
-//
-// prism0 private implementation
-//
-var _prism0 = function( block,w,d ) {
- this.prism(block,w,d )
- .fwd( ).right( )
- .prism(0,w-2,d-2 )
- .left( ).back( );
- var se = _STAIRBLOCKS[block];
- if ( d % 2 == 1 && se ) {
- // top of roof will be open - need repair
- var f = Math.floor(d/2 );
- this.fwd(f ).up(f ).cuboid(se,w ).down(f ).back(f );
- }
-};
-Drone.extend('prism0',_prism0 );
-Drone.extend('prism',_prism );
-Drone.extend('box',Drone.prototype.cuboid );
-Drone.extend('box0',Drone.prototype.cuboid0 );
-Drone.extend('boxa',Drone.prototype.cuboida );
-//
-// show the Drone's position and direction
-//
-Drone.prototype.toString = function( ) {
- var dirs = ['east','south','west','north'];
- return 'x: ' + this.x + ' y: '+this.y + ' z: ' + this.z + ' dir: ' + this.dir + ' '+dirs[this.dir];
-};
-Drone.prototype.debug = function( ) {
- console.log(this.toString( ) );
- return this;
-};
-/*
- do the bresenham thing
- */
-var _bresenham = function( x0,y0,radius, setPixel, quadrants ) {
- //
- // credit: Following code is copied almost verbatim from
- // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
- // Bresenham's circle algorithm
- //
- var f = 1 - radius;
- var ddF_x = 1;
- var ddF_y = -2 * radius;
- var x = 0;
- var y = radius;
- var defaultQuadrants = {topleft: true, topright: true, bottomleft: true, bottomright: true};
- quadrants = quadrants?quadrants:defaultQuadrants;
- /*
- II | I
- ------------
- III | IV
- */
- if ( quadrants.topleft || quadrants.topright )
- setPixel(x0, y0 + radius ); // quadrant I/II topmost
- if ( quadrants.bottomleft || quadrants.bottomright )
- setPixel(x0, y0 - radius ); // quadrant III/IV bottommost
- if ( quadrants.topright || quadrants.bottomright )
- setPixel(x0 + radius, y0 ); // quadrant I/IV rightmost
- if ( quadrants.topleft || quadrants.bottomleft )
- setPixel(x0 - radius, y0 ); // quadrant II/III leftmost
-
- while ( x < y ) {
- if(f >= 0 ) {
- y--;
- ddF_y += 2;
- f += ddF_y;
- }
- x++;
- ddF_x += 2;
- f += ddF_x;
- if ( quadrants.topright ) {
- setPixel(x0 + x, y0 + y ); // quadrant I
- setPixel(x0 + y, y0 + x ); // quadrant I
- }
- if ( quadrants.topleft ) {
- setPixel(x0 - x, y0 + y ); // quadrant II
- setPixel(x0 - y, y0 + x ); // quadrant II
- }
- if ( quadrants.bottomleft ) {
- setPixel(x0 - x, y0 - y ); // quadrant III
- setPixel(x0 - y, y0 - x ); // quadrant III
- }
- if ( quadrants.bottomright ) {
- setPixel(x0 + x, y0 - y ); // quadrant IV
- setPixel(x0 + y, y0 - x ); // quadrant IV
- }
- }
-};
-var _getStrokeDir = function( x,y ) {
- var absY = Math.abs(y );
- var absX = Math.abs(x );
- var strokeDir = 0;
- if ( y > 0 && absY >= absX )
- strokeDir = 0 ; //down
- else if ( y < 0 && absY >= absX )
- strokeDir = 1 ; // up
- else if ( x > 0 && absX >= absY )
- strokeDir = 2 ; // left
- else if ( x < 0 && absX >= absY )
- strokeDir = 3 ; // right
- return strokeDir;
-};
-/*
- The daddy of all arc-related API calls -
- if you're drawing anything that bends it ends up here.
- */
-var _arc2 = function( params ) {
- var drone = params.drone;
- var orientation = params.orientation?params.orientation:'horizontal';
- var quadrants = params.quadrants?params.quadrants:{
- topright:1,
- topleft:2,
- bottomleft:3,
- bottomright:4
- };
- var stack = params.stack?params.stack:1;
- var radius = params.radius;
- var strokeWidth = params.strokeWidth?params.strokeWidth:1;
- drone.chkpt('arc2' );
- var x0, y0, gotoxy,setPixel;
-
- if ( orientation == 'horizontal' ) {
- gotoxy = function( x,y ) { return drone.right(x ).fwd(y );};
- drone.right(radius ).fwd(radius ).chkpt('center' );
- switch ( drone.dir ) {
- case 0: // east
- case 2: // west
- x0 = drone.z;
- y0 = drone.x;
- break;
- case 1: // south
- case 3: // north
- x0 = drone.x;
- y0 = drone.z;
- }
- setPixel = function( x, y ) {
- x = ( x-x0 );
- y = ( y-y0 );
- if ( params.fill ) {
- // wph 20130114 more efficient esp. for large cylinders/spheres
- if ( y < 0 ) {
- drone
- .fwd( y ).right( x )
- .cuboidX( params.blockType, params.meta, 1, stack, Math.abs( y * 2 ) + 1 )
- .back( y ).left( x );
- }
- }else{
- if ( strokeWidth == 1 ) {
- gotoxy(x,y )
- .cuboidX( params.blockType, params.meta,
- 1, // width
- stack, // height
- strokeWidth // depth
- )
- .move('center' );
- } else {
- var strokeDir = _getStrokeDir( x, y );
- var width = 1, depth = 1;
- switch ( strokeDir ) {
- case 0: // down
- y = y-( strokeWidth - 1 );
- depth = strokeWidth;
- break;
- case 1: // up
- depth = strokeWidth;
- break;
- case 2: // left
- width = strokeWidth;
- x = x-(strokeWidth-1 );
- break;
- case 3: // right
- width = strokeWidth;
- break;
- }
- gotoxy( x, y )
- .cuboidX( params.blockType, params.meta, width, stack, depth )
- .move( 'center' );
-
- }
- }
- };
- }else{
- // vertical
- gotoxy = function( x,y ) { return drone.right(x ).up(y );};
- drone.right(radius ).up(radius ).chkpt('center' );
- switch ( drone.dir ) {
- case 0: // east
- case 2: // west
- x0 = drone.z;
- y0 = drone.y;
- break;
- case 1: // south
- case 3: // north
- x0 = drone.x;
- y0 = drone.y;
- }
- setPixel = function( x, y ) {
- x = ( x - x0 );
- y = ( y - y0 );
- if ( params.fill ) {
- // wph 20130114 more efficient esp. for large cylinders/spheres
- if ( y < 0 ) {
- drone
- .up( y ).right( x )
- .cuboidX( params.blockType, params.meta, 1, Math.abs( y * 2 ) + 1, stack )
- .down( y ).left( x );
- }
- }else{
- if ( strokeWidth == 1 ) {
- gotoxy( x, y )
- .cuboidX( params.blockType, params.meta, strokeWidth, 1, stack )
- .move( 'center' );
- }else{
- var strokeDir = _getStrokeDir( x,y );
- var width = 1, height = 1;
- switch ( strokeDir ) {
- case 0: // down
- y = y - ( strokeWidth - 1 );
- height = strokeWidth;
- break;
- case 1: // up
- height = strokeWidth;
- break;
- case 2: // left
- width = strokeWidth;
- x = x - ( strokeWidth - 1 );
- break;
- case 3: // right
- width = strokeWidth;
- break;
- }
- gotoxy(x,y )
- .cuboidX(params.blockType, params.meta, width, height, stack )
- .move('center' );
-
- }
- }
- };
- }
- /*
- setPixel assumes a 2D plane - need to put a block along appropriate plane
- */
- _bresenham(x0,y0,radius,setPixel,quadrants );
-
- params.drone.move('arc2' );
-};
-
-
-Drone.extend('arc',function( params ) {
- params.drone = this;
- _arc2(params );
-} );
-
-var _cylinder0 = function( block,radius,height,exactParams ) {
- var arcParams = {
- radius: radius,
- fill: false,
- orientation: 'horizontal',
- stack: height
- };
-
- if ( exactParams ) {
- for ( var p in exactParams ) {
- arcParams[p] = exactParams[p];
- }
- }else{
- var md = this._getBlockIdAndMeta(block );
- arcParams.blockType = md[0];
- arcParams.meta = md[1];
- }
- return this.arc(arcParams );
-};
-var _cylinder1 = function( block,radius,height,exactParams ) {
- var arcParams = {
- radius: radius,
- fill: true,
- orientation: 'horizontal',
- stack: height,
- };
-
- if ( exactParams ) {
- arcParams.blockType = exactParams.blockType;
- arcParams.meta = exactParams.meta;
- }else{
- var md = this._getBlockIdAndMeta(block );
- arcParams.blockType = md[0];
- arcParams.meta = md[1];
- }
- return this.arc(arcParams );
-};
-var _paste = function( name, immediate )
-{
-
- if ( !immediate ) {
- getQueue(this).push(function(){ _paste(name, true);});
- return;
- }
- var ccContent = Drone.clipBoard[name];
- var srcBlocks = ccContent.blocks;
- var srcDir = ccContent.dir; // direction player was facing when copied.
- var dirOffset = (4 + (this.dir - srcDir ) ) %4;
- var that = this;
-
- _traverse[this.dir].width(that,srcBlocks.length,function( ww ) {
- var h = srcBlocks[ww].length;
- _traverseHeight(that,h,function( hh ) {
- var d = srcBlocks[ww][hh].length;
- _traverse[that.dir].depth(that,d,function( dd ) {
- var b = srcBlocks[ww][hh][dd];
- var bm = that._getBlockIdAndMeta(b );
- var cb = bm[0];
- var md = bm[1];
- //
- // need to adjust blocks which face a direction
- //
- switch ( cb ) {
- //
- // doors
- //
- case 64: // wood
- case 71: // iron
- // top half of door doesn't need to change
- if ( md < 8 ) {
- md = (md + dirOffset ) % 4;
- }
- break;
- //
- // stairs
- //
- case 53: // oak
- case 67: // cobblestone
- case 108: // red brick
- case 109: // stone brick
- case 114: // nether brick
- case 128: // sandstone
- case 134: // spruce
- case 135: // birch
- case 136: // junglewood
- var dir = md & 0x3;
- var a = Drone.PLAYER_STAIRS_FACING;
- var len = a.length;
- for ( var c=0;c < len;c++ ) {
- if ( a[c] == dir ) {
- break;
- }
- }
- c = (c + dirOffset ) %4;
- var newDir = a[c];
- md = (md >>2<<2 ) + newDir;
- break;
- //
- // signs , ladders etc
- //
- case 23: // dispenser
- case 54: // chest
- case 61: // furnace
- case 62: // burning furnace
- case 65: // ladder
- case 68: // wall sign
- var a = Drone.PLAYER_SIGN_FACING;
- var len = a.length;
- for ( var c=0;c < len;c++ ) {
- if ( a[c] == md ) {
- break;
- }
- }
- c = (c + dirOffset ) %4;
- var newDir = a[c];
- md = newDir;
- break;
- }
- putBlock(that.x,that.y,that.z,cb,md,that.world );
- } );
- } );
- } );
-};
-var _getDirFromRotation = function( r ) {
- // 0 = east, 1 = south, 2 = west, 3 = north
- // 46 to 135 = west
- // 136 to 225 = north
- // 226 to 315 = east
- // 316 to 45 = south
-
- r = (r + 360 ) % 360; // east could be 270 or -90
-
- if ( r > 45 && r <= 135 )
- return 2; // west
- if ( r > 135 && r <= 225 )
- return 3; // north
- if ( r > 225 && r <= 315 )
- return 0; // east
- if ( r > 315 || r < 45 )
- return 1; // south
-};
-var _getBlockIdAndMeta = function( b ) {
- var defaultMeta = 0,
- i = 0,
- bs,
- md,
- sp;
- if ( typeof b == 'string' ) {
- bs = b;
- sp = bs.indexOf(':' );
- if ( sp == -1 ) {
- b = parseInt(bs );
- // wph 20130414 - use sensible defaults for certain blocks e.g. stairs
- // should face the drone.
- for ( i in blocks.stairs ) {
- if ( blocks.stairs[i] === b ) {
- defaultMeta = Drone.PLAYER_STAIRS_FACING[this.dir];
- break;
- }
- }
- return [ b, defaultMeta ];
- }
- b = parseInt(bs.substring(0,sp ) );
- md = parseInt(bs.substring(sp+1,bs.length ) );
- return [b,md];
- }else{
- // wph 20130414 - use sensible defaults for certain blocks e.g. stairs
- // should face the drone.
- for ( i in blocks.stairs ) {
- if ( blocks.stairs[i] === b ) {
- defaultMeta = Drone.PLAYER_STAIRS_FACING[this.dir];
- break;
- }
- }
- return [ b, defaultMeta ];
- }
-};
-//
-// movement
-//
-var _movements = [{},{},{},{}];
-// east
-_movements[0].right = function( that,n ) { that.z +=n; return that;};
-_movements[0].left = function( that,n ) { that.z -=n; return that;};
-_movements[0].fwd = function( that,n ) { that.x +=n; return that;};
-_movements[0].back = function( that,n ) { that.x -= n; return that;};
-// south
-_movements[1].right = _movements[0].back;
-_movements[1].left = _movements[0].fwd;
-_movements[1].fwd = _movements[0].right;
-_movements[1].back = _movements[0].left;
-// west
-_movements[2].right = _movements[0].left;
-_movements[2].left = _movements[0].right;
-_movements[2].fwd = _movements[0].back;
-_movements[2].back = _movements[0].fwd;
-// north
-_movements[3].right = _movements[0].fwd;
-_movements[3].left = _movements[0].back;
-_movements[3].fwd = _movements[0].left;
-_movements[3].back = _movements[0].right;
-var _traverse = [{},{},{},{}];
-// east
-_traverse[0].width = function( that,n,callback ) {
- var s = that.z, e = s + n;
- for ( ; that.z < e; that.z++ ) {
- callback(that.z-s );
- }
- that.z = s;
-};
-_traverse[0].depth = function( that,n,callback ) {
- var s = that.x, e = s+n;
- for ( ;that.x < e;that.x++ ) {
- callback(that.x-s );
- }
- that.x = s;
-};
-// south
-_traverse[1].width = function( that,n,callback ) {
- var s = that.x, e = s-n;
- for ( ;that.x > e;that.x-- ) {
- callback(s-that.x );
- }
- that.x = s;
-};
-_traverse[1].depth = _traverse[0].width;
-// west
-_traverse[2].width = function( that,n,callback ) {
- var s = that.z, e = s-n;
- for ( ;that.z > e;that.z-- ) {
- callback(s-that.z );
- }
- that.z = s;
-};
-_traverse[2].depth = _traverse[1].width;
-// north
-_traverse[3].width = _traverse[0].depth;
-_traverse[3].depth = _traverse[2].width;
-var _traverseHeight = function( that,n,callback ) {
- var s = that.y, e = s + n;
- for ( ; that.y < e; that.y++ ) {
- callback(that.y-s );
- }
- that.y = s;
-};
-//
-// standard fisher-yates shuffle algorithm
-//
-var _fisherYates = function( myArray ) {
- var i = myArray.length;
- if ( i == 0 ) return false;
- while ( --i ) {
- var j = Math.floor( Math.random( ) * ( i + 1 ) );
- var tempi = myArray[i];
- var tempj = myArray[j];
- myArray[i] = tempj;
- myArray[j] = tempi;
- }
-};
-var _copy = function( name, w, h, d ) {
- var that = this;
- var ccContent = [];
- _traverse[this.dir].width(that,w,function( ww ) {
- ccContent.push([] );
- _traverseHeight(that,h,function( hh ) {
- ccContent[ww].push([] );
- _traverse[that.dir].depth(that,d,function( dd ) {
- var b = that.world.getBlockAt(that.x,that.y,that.z );
- ccContent[ww][hh][dd] = b;
- } );
- } );
- } );
- Drone.clipBoard[name] = {dir: this.dir, blocks: ccContent};
-};
-var _garden = function( width, depth ) {
- if ( typeof width == 'undefined' ) {
- width = 10;
- }
- if ( typeof depth == 'undefined' ) {
- depth = width;
- }
- var grass = 2,
- red = 37,
- yellow = 38,
- longgrass = '31:1',
- air = 0;
-
- // make sure grass is present first
- this.down()
- .box( grass, width, 1, depth )
- .up( );
-
- // make flowers more common than long grass
- var dist = { };
- dist[red] = 3;
- dist[yellow] = 3;
- dist[longgrass] = 2;
- dist[air] = 1;
-
- return this.rand( dist, width, 1, depth, false /* don't overwrite */ );
-};
-
-var _rand = function( blockDistribution ) {
- if ( !(blockDistribution.constructor == Array ) ) {
- var a = [];
- for ( var p in blockDistribution ) {
- var n = blockDistribution[p];
- for ( var i = 0;i < n;i++ ) {
- a.push(p );
- }
- }
- blockDistribution = a;
- }
- while ( blockDistribution.length < 1000 ) {
- // make array bigger so that it's more random
- blockDistribution = blockDistribution.concat(blockDistribution );
- }
- _fisherYates(blockDistribution );
- return blockDistribution;
-};
-
-Drone.extend( 'rand', function( dist, width, height, depth, overwrite ) {
- if ( typeof overwrite == 'undefined' ) {
- overwrite = true;
- }
- var randomized = _rand( dist );
- this.boxa( randomized, width, height, depth, overwrite);
-} );
-
-var _trees = {
- oak: bkTreeType.BIG_TREE ,
- birch: bkTreeType.BIRCH ,
- jungle: bkTreeType.JUNGLE,
- spruce: bkTreeType.REDWOOD
-};
-for ( var p in _trees ) {
- Drone.extend(p, function( v ) {
- return function( ) {
- var block = this.world.getBlockAt(this.x,this.y,this.z );
- if ( block.typeId == 2 ) {
- this.up( );
- }
- var treeLoc = new bkLocation(this.world,this.x,this.y,this.z );
- var successful = treeLoc.world.generateTree(treeLoc,v );
- if ( block.typeId == 2 ) {
- this.down( );
- }
- };
- }(_trees[p] ) );
-}
-Drone.clone = function(origin) {
- var result = {x: origin.x, y: origin.y, z: origin.z, world: origin.world, dir: origin.dir};
- return result;
-};
-//
-// Drone's clipboard
-//
-Drone.clipBoard = {};
-Drone.extend('garden',_garden );
-Drone.extend('copy', _copy );
-Drone.extend('paste',_paste );
-Drone.extend('cylinder0',_cylinder0 );
-Drone.extend('cylinder', _cylinder1 );
-//
-// wph 20130130 - make this a method - extensions can use it.
-//
-Drone.prototype._getBlockIdAndMeta = _getBlockIdAndMeta;
-
-
diff --git a/src/main/js/plugins/entities.js b/src/main/js/plugins/entities.js
new file mode 100644
index 000000000..48446df32
--- /dev/null
+++ b/src/main/js/plugins/entities.js
@@ -0,0 +1,8 @@
+'use strict';
+/*global require, exports*/
+/*
+ make entities a global variable for use at in-game prompt
+ Tab completion is a useful way to discover what entity types are available.
+*/
+var entities = require('entities');
+exports.entities = entities;
diff --git a/src/main/js/plugins/examples/example-1-hello-module.js b/src/main/js/plugins/examples/example-1-hello-module.js
index cb13ed6cb..330670414 100644
--- a/src/main/js/plugins/examples/example-1-hello-module.js
+++ b/src/main/js/plugins/examples/example-1-hello-module.js
@@ -21,10 +21,10 @@ The `hello` function below is only usable by players with the scriptcraft.evalua
permission since it relies on the `/js` command to execute.
exports.hello = function(player){
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
};
***/
exports.hello = function( player ) {
- player.sendMessage( 'Hello ' + player.name );
+ echo( player, 'Hello ' + player.name );
};
diff --git a/src/main/js/plugins/examples/example-2-hello-command.js b/src/main/js/plugins/examples/example-2-hello-command.js
index 4470003a3..1ef6b2065 100644
--- a/src/main/js/plugins/examples/example-2-hello-command.js
+++ b/src/main/js/plugins/examples/example-2-hello-command.js
@@ -22,11 +22,11 @@ can use the new extension. Unlike the previous example, the `jsp hello`
command does not evaluate javascript code so this command is much more secure.
command('hello', function (parameters, player) {
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
});
***/
command( 'hello', function( parameters, player ) {
- player.sendMessage( 'Hello ' + player.name );
+ echo( player, 'Hello ' + player.name );
});
diff --git a/src/main/js/plugins/examples/example-3-hello-ops-only.js b/src/main/js/plugins/examples/example-3-hello-ops-only.js
index 84b2a14fd..5c7893a6d 100644
--- a/src/main/js/plugins/examples/example-3-hello-ops-only.js
+++ b/src/main/js/plugins/examples/example-3-hello-ops-only.js
@@ -22,11 +22,11 @@ This differs from example 2 in that the function will only print a
message for operators.
command('op-hello', function (parameters, player) {
- if (!player.op){
- player.sendMessage('Only operators can do this.');
- return;
+ if ( !isOp(player) ){
+ echo( player, 'Only operators can do this.');
+ return;
}
- player.sendMessage('Hello ' + player.name);
+ echo( player, 'Hello ' + player.name);
});
***/
@@ -34,9 +34,9 @@ command( 'op-hello', function( parameters, player ) {
/*
this is how you limit based on player privileges
*/
- if ( !player.op ) {
- player.sendMessage( 'Only operators can do this.' );
+ if ( !isOp(player) ) {
+ echo( player, 'Only operators can do this.' );
return;
}
- player.sendMessage( 'Hello ' + player.name );
+ echo( player, 'Hello ' + player.name );
});
diff --git a/src/main/js/plugins/examples/example-4-hello-parameters.js b/src/main/js/plugins/examples/example-4-hello-parameters.js
index 76def9f8c..ba01adca0 100644
--- a/src/main/js/plugins/examples/example-4-hello-parameters.js
+++ b/src/main/js/plugins/examples/example-4-hello-parameters.js
@@ -21,7 +21,7 @@ a fixed 'Hello ' to anything you like by passing a parameter.
command( 'hello-params', function ( parameters, player ) {
var salutation = parameters[0] ;
- player.sendMessage( salutation + ' ' + player.name );
+ echo( player, salutation + ' ' + player.name );
});
***/
@@ -36,5 +36,5 @@ command('hello-params', function( parameters, player ) {
which appears after `jsp hello-params `.
*/
var salutation = parameters[0] ;
- player.sendMessage( salutation + ' ' + player.name );
+ echo( player, salutation + ' ' + player.name );
});
diff --git a/src/main/js/plugins/examples/example-6-hello-player.js b/src/main/js/plugins/examples/example-6-hello-player.js
index b53e0bdd3..fd928549c 100644
--- a/src/main/js/plugins/examples/example-6-hello-player.js
+++ b/src/main/js/plugins/examples/example-6-hello-player.js
@@ -38,7 +38,7 @@ Source Code ...
if ( recipient ) {
greetings.hello( recipient );
} else {
- sender.sendMessage( 'Player ' + playerName + ' not found.' );
+ echo( sender, 'Player ' + playerName + ' not found.' );
}
});
@@ -53,6 +53,6 @@ command( 'hello-byname', function( parameters, sender ) {
if ( recipient ) {
greetings.hello( recipient );
} else {
- sender.sendMessage( 'Player ' + playerName + ' not found.' );
+ echo( sender, 'Player ' + playerName + ' not found.' );
}
});
diff --git a/src/main/js/plugins/examples/example-7-hello-events.js b/src/main/js/plugins/examples/example-7-hello-events.js
index 58b62b574..72ba171af 100644
--- a/src/main/js/plugins/examples/example-7-hello-events.js
+++ b/src/main/js/plugins/examples/example-7-hello-events.js
@@ -1,3 +1,5 @@
+'use strict';
+/*global events, echo, isOp, __plugin*/
/*************************************************************************
## Example Plugin #7 - Listening for events, Greet players when they join the game.
@@ -14,87 +16,58 @@ following reasons...
`/scriptcraft/plugins` directory, it will be loaded automatically
when the server starts up.
- 2. It uses ScriptCraft's `events.on()` function to add a new *Event
- Handler*. An *Event Handler* is a just a function which gets
+ 2. It uses ScriptCraft's `events` module to add a new *Event
+ Handler*. An *Event Handler* is a function that gets
called whenever a particular *event* happens in the game. The
function defined below will only be executed whenever a player
joins the game. This style of program is sometimes refered to as
*Event-Driven Programming*.
-Adding new *Event Handlers* in ScriptCraft is relatively easy. Use the
-`events.on()` function to add a new event handler. It takes 2
-parameters...
+Adding new *Event Handlers* in ScriptCraft is relatively easy. Use one
+of the `events` module's functions to add a new event handler. The
+events module has many functions - one for each type of event. Each
+function takes a single parameter:
- 1. The Event Name, in this case `'player.PlayerJoinEvent'`. You can
- browse [all possible Bukkit events][bkevts] (click the 'Next
- Package' and 'Previous Package' links to browse).
-
- 2. The event handling function (also sometimes refered to as a
- 'callback'). In ScriptCraft, this function takes a single
- parameter, an event object. All of the information about the event
- is in the event object.
+ * The event handling function (also sometimes refered to as a
+ 'callback'). In ScriptCraft, this function takes a single
+ parameter, an event object. All of the information about the event
+ is in the event object.
In the example below, if a player joins the server and is an operator,
then the ScriptCraft plugin information will be displayed to that
player.
-What's also notable about this example is how it uses the [Bukkit
-API][bkapi]. The code...
-
- if (event.player.op)
-
-... is a succinct way of accessing object properties which in Java
-would have to be written as ...
-
- if (event.getPlayer().isOp())
-
-... ScriptCraft uses a special version of JavaScript which comes
-bundled with Java (Minecraft is written in Java) and JavaScript in
-Java can access properties of Java objects more succinctly than in
-Java itself. What this means in practice is that when you're perusing
-the [Bukkit API Reference][bkapi] and come across a method like
-[Player.getAllowFlight()][bkgaf], you can write code like this...
-
- var allowFlight = player.getAllowFlight(); // java style
-
-... or the more succinct ...
-
- var allowFlight = player.allowFlight; // javascript style
-
-... Which style you choose is up to you but `player.allowFlight` is
-cleaner and more readable. Similarly where you see a method like
-[Player.setAllowFlight()][bksaf], you can write ...
-
- player.setAllowFlight(true); // java style
-
-... or the more readable...
-
- player.allowFlight = true; // javascript style
-
-... Which style you choose is up to you.
-
-[bkevts]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/package-summary.html
-[bkgaf]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html#getAllowFlight()
-[bksaf]: http://jd.bukkit.org/dev/apidocs/org/bukkit/entity/Player.html#setAllowFlight()
-[bkapi]: http://jd.bukkit.org/dev/apidocs/
-
- events.on( 'player.PlayerJoinEvent', function( event ) {
- if ( event.player.op ) {
- event.player.sendMessage('Welcome to ' + __plugin);
- }
- });
-
-Update: Since version 2.0.8 the above code can be replaced by the more succinct:
-
- events.playerJoin( function( event ) {
- if ( event.player.op ) {
- event.player.sendMessage('Welcome to ' + __plugin);
- }
- });
-
+```javascript
+function onJoin( event ){
+ if ( isOp(event.player) ) {
+ echo( event.player, 'Welcome to ' + __plugin );
+ }
+}
+events.connection( onJoin );
+```
+First the onJoin() function is defined, this is our event handler -
+the function we wish to be called every time some new player joins the
+game. Then we hook up - or register - that function using the
+events.connection() function. The events.connection function is the
+function responsible for adding new *connection* event handlers - that
+is - functions which should be invoked when there's a new *connection*
+event in the game. A new *connection* event is fired whenever a player
+joins the game. There are many other types of events you can handle in
+Minecraft. You can see [a full list of events here][cmEvtList].
+
+[cmEvtList]: #events-helper-module-canary-version
***/
-events.on( 'player.PlayerJoinEvent', function( event ) {
- if ( event.player.op ) {
- event.player.sendMessage( 'Welcome to ' + __plugin );
+
+// wph 20140927 - event handler registration differs depending on framework.
+function onJoin( event ) {
+ if ( isOp(event.player) ) {
+ echo( event.player, 'Welcome to ' + __plugin );
}
-});
+}
+if (__plugin.canary){
+ // canarymod
+ events.connection( onJoin );
+} else {
+ // bukkit
+ events.playerJoin( onJoin );
+}
diff --git a/src/main/js/plugins/homes/homes.js b/src/main/js/plugins/homes/homes.js
index a4b0af73f..f6af3eca8 100644
--- a/src/main/js/plugins/homes/homes.js
+++ b/src/main/js/plugins/homes/homes.js
@@ -106,11 +106,11 @@ var homes = plugin( 'homes', {
host = utils.player( host );
loc = _store.houses[ host.name ];
if ( !loc ) {
- guest.sendMessage( host.name + ' has no home' );
+ echo( guest, host.name + ' has no home' );
return;
}
if ( !this._canVisit( guest, host ) ) {
- guest.sendMessage( 'You can not visit ' + host.name + "'s home yet" );
+ echo( guest, 'You can not visit ' + host.name + "'s home yet" );
return;
}
homeLoc = utils.locationFromJSON( loc );
@@ -213,8 +213,8 @@ var homes = plugin( 'homes', {
}
invitations.push( guest.name );
_store.invites[host.name] = invitations;
- guest.sendMessage( host.name + ' has invited you to their home.' );
- guest.sendMessage( 'type "/jsp home ' + host.name + '" to accept' );
+ echo( guest, host.name + ' has invited you to their home.' );
+ echo( guest, 'type "/jsp home ' + host.name + '" to accept' );
},
/*
Uninvite someone to the home
@@ -290,16 +290,16 @@ var options = {
},
'help': function( params, sender ) {
- sender.sendMessage( homes.help() );
+ echo( sender, homes.help() );
},
'list': function( params, sender ) {
var visitable = homes.list();
if ( visitable.length == 0 ) {
- sender.sendMessage( 'There are no homes to visit' );
+ echo( sender, 'There are no homes to visit' );
return;
} else {
- sender.sendMessage([
+ echo( sender, [
'You can visit any of these ' + visitable.length + ' homes'
,visitable.join(', ')
]);
@@ -309,9 +309,9 @@ var options = {
'ilist': function( params, sender ) {
var potentialVisitors = homes.ilist();
if ( potentialVisitors.length == 0 ) {
- sender.sendMessage('No one can visit your home');
+ echo( sender, 'No one can visit your home');
} else {
- sender.sendMessage([
+ echo( sender, [
'These ' + potentialVisitors.length + 'players can visit your home',
potentialVisitors.join(', ')]);
}
@@ -319,13 +319,13 @@ var options = {
'invite': function( params, sender ) {
if ( params.length == 1 ) {
- sender.sendMessage( 'You must provide a player name' );
+ echo( sender, 'You must provide a player name' );
return;
}
var playerName = params[1];
var guest = utils.player( playerName );
if ( !guest ) {
- sender.sendMessage( playerName + ' is not here' );
+ echo( sender, playerName + ' is not here' );
} else {
homes.invite( sender, guest );
}
@@ -333,13 +333,13 @@ var options = {
'uninvite': function( params, sender ) {
if ( params.length == 1 ) {
- sender.sendMessage( 'You must provide a player name' );
+ echo( sender, 'You must provide a player name' );
return;
}
var playerName = params[1];
var guest = utils.player( playerName );
if ( !guest ) {
- sender.sendMessage( playerName + ' is not here' );
+ echo( sender, playerName + ' is not here' );
} else {
homes.uninvite( sender, guest );
}
@@ -347,25 +347,25 @@ var options = {
'public': function( params, sender ) {
homes.open( sender, params.slice( 1 ).join(' ') );
- sender.sendMessage( 'Your home is open to the public' );
+ echo( sender, 'Your home is open to the public' );
},
'private': function( params, sender ) {
homes.close( sender );
- sender.sendMessage( 'Your home is closed to the public' );
+ echo( sender, 'Your home is closed to the public' );
},
'listall': function( params, sender ) {
if ( !sender.isOp() ) {
- sender.sendMessage( 'Only operators can do this' );
+ echo( sender, 'Only operators can do this' );
} else {
- sender.sendMessage( homes.listall().join(', ') );
+ echo( sender, homes.listall().join(', ') );
}
},
'clear': function( params, sender ) {
if ( !sender.isOp() ) {
- sender.sendMessage( 'Only operators can do this' );
+ echo( sender, 'Only operators can do this' );
} else {
homes.clear( params[1], sender );
}
@@ -393,7 +393,7 @@ command( 'home', function ( params , sender) {
} else {
host = utils.player( params[0] );
if ( !host ) {
- sender.sendMessage( params[0] + ' is not here' );
+ echo( sender, params[0] + ' is not here' );
} else {
homes.go( sender, host );
}
diff --git a/src/main/js/plugins/minigames/SnowballFight.js b/src/main/js/plugins/minigames/SnowballFight.js
deleted file mode 100644
index 8d1dfbd6c..000000000
--- a/src/main/js/plugins/minigames/SnowballFight.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/*************************************************************************
-## SnowballFight mini-game
-
-### Description
-
-This is a rough and ready prototype of a simple multi-player
-shoot-em-up. To start a game with all players playing against one another...
-
- /js new Game_SnowballFight(60).start();
-
-... this obviously works best if all of the players are in close
-proximity within the same game world. Alternatively you can have team
-matches...
-
-
- /js var redTeam = ['','',...etc]
- /js var blueTeam = ['',',...etc]
- /js var greenTeam = ['',',...etc]
- /js new Game_SnowballFight(60, {red: redTeam,blue: blueTeam,green: greenTeam}).start();
-
-Or you can just have specific players play against each other...
-
- /js new Game_SnowballFight(60, ['player1','player2','player3']).start();
-
-(where 'player1' etc are the names of actual players)
-
-You specify the teams in the game as an object where each property's
-name is a team name and each property's value is the list of players
-on that team. You specify the duration of the game (in seconds) You
-kick off the game with the start() method. I need to work on a
-better in-game mechanism for players to choose teams and start the
-game but this will do for now.
-
-When the game starts, each player is put in survival mode and given
-snowballs. The aim of the game is to hit players on opposing teams. If
-you hit a player on your own team, you lose a point.
-
-At the end of the game the scores for each team are broadcast and each
-player returns to their previous mode of play (creative or
-survival). Create a small arena with a couple of small buildings for
-cover to make the game more fun.
-
-***/
-
-var bkGameMode = org.bukkit.GameMode,
- bkItemStack = org.bukkit.inventory.ItemStack,
- bkMaterial = org.bukkit.Material,
- bkSnowball = org.bukkit.entity.Snowball;
-
-var _startGame = function( gameState ) {
- var i,
- teamName,
- team,
- player;
-
- // don't let game start if already in progress (wait for game to finish)
- if ( gameState.inProgress ) {
- return;
- }
- gameState.inProgress = true;
- // reset timer
- gameState.duration = gameState.originalDuration;
- // put all players in survival mode and give them each 200 snowballs
- // 64 snowballs for every 30 seconds should be more than enough
- for ( i = 10; i < gameState.duration; i += 10 ) {
- gameState.ammo.push( gameState.ammo[ 0 ] );
- }
-
- for ( teamName in gameState.teams ) {
- gameState.teamScores[teamName] = 0;
- team = gameState.teams[ teamName ];
- for ( i = 0; i < team.length; i++ ) {
- player = server.getPlayer( team[i] );
- gameState.savedModes[ player.name ] = player.gameMode;
- player.gameMode = bkGameMode.SURVIVAL;
- player.inventory.addItem( gameState.ammo );
- }
- }
-};
-/*
- end the game
- */
-var _endGame = function( gameState ) {
- var scores = [],
- leaderBoard = [],
- tn,
- i,
- teamName,
- team,
- player,
- handlerList;
-
- leaderBoard = [];
- for ( tn in gameState.teamScores){
- leaderBoard.push([tn,gameState.teamScores[tn]]);
- }
- leaderBoard.sort(function(a,b){ return b[1] - a[1];});
-
- for ( i = 0; i < leaderBoard.length; i++ ) {
- scores.push( 'Team ' + leaderBoard[i][0] + ' scored ' + leaderBoard[i][1] );
- }
-
- for ( teamName in gameState.teams ) {
- team = gameState.teams[teamName];
- for ( i = 0; i < team.length; i++ ) {
- // restore player's previous game mode and take back snowballs
- player = server.getPlayer( team[i] );
- player.gameMode = gameState.savedModes[ player.name ];
- player.inventory.removeItem( gameState.ammo );
- player.sendMessage( 'GAME OVER.' );
- player.sendMessage( scores );
- }
- }
- gameState.listener.unregister();
- gameState.inProgress = false;
-};
-/*
- get the team the player belongs to
- */
-var _getTeam = function( player, pteams ) {
- var teamName,
- team,
- i;
- for ( teamName in pteams ) {
- team = pteams[ teamName ];
- for ( i = 0; i < team.length; i++ ) {
- if ( team[i] == player.name ) {
- return teamName;
- }
- }
- }
- return null;
-};
-/*
- construct a new game
- */
-var createGame = function( duration, teams ) {
- var players,
- i,
- _snowBalls = new bkItemStack( bkMaterial.SNOW_BALL, 64 );
-
- var _gameState = {
- teams: teams,
- duration: duration,
- originalDuration: duration,
- inProgress: false,
- teamScores: {},
- listener: null,
- savedModes: {},
- ammo: [ _snowBalls ]
- };
- if ( typeof duration == 'undefined' ) {
- duration = 60;
- }
- if ( typeof teams == 'undefined' ) {
- /*
- wph 20130511 use all players
- */
- teams = [];
- players = server.onlinePlayers;
- for ( i = 0; i < players.length; i++ ) {
- teams.push( players[i].name );
- }
- }
- //
- // allow for teams param to be either {red:['player1','player2'],blue:['player3']} or
- // ['player1','player2','player3'] if all players are against each other (no teams)
- //
- if ( teams instanceof Array ) {
- _gameState.teams = {};
- for ( i = 0;i < teams.length; i++ ) {
- _gameState.teams[ teams[i] ] = [ teams[i] ];
- }
- }
- /*
- this function is called every time a player is damaged by another entity/player
- */
- var _onSnowballHit = function( event ) {
- var snowball = event.damager;
- if ( !snowball || !( snowball instanceof bkSnowball ) ) {
- return;
- }
- var throwersTeam = _getTeam( snowball.shooter, _gameState.teams );
- var damageeTeam = _getTeam( event.entity, _gameState.teams);
- if ( !throwersTeam || !damageeTeam ) {
- return; // thrower/damagee wasn't in game
- }
- if ( throwersTeam != damageeTeam ) {
- _gameState.teamScores[ throwersTeam ]++;
- } else {
- _gameState.teamScores[ throwersTeam ]--;
- }
- };
-
- return {
- start: function( ) {
- _startGame( _gameState );
- _gameState.listener = events.entityDamageByEntity( _onSnowballHit );
- new java.lang.Thread( function( ) {
- while ( _gameState.duration-- ) {
- java.lang.Thread.sleep( 1000 ); // sleep 1,000 millisecs (1 second)
- }
- _endGame(_gameState);
- } ).start( );
- }
- };
-};
-exports.Game_SnowballFight = createGame;
-
-
diff --git a/src/main/js/plugins/minigames/cow-clicker.js b/src/main/js/plugins/minigames/cow-clicker.js
index 6ed42503a..9b6836ed1 100644
--- a/src/main/js/plugins/minigames/cow-clicker.js
+++ b/src/main/js/plugins/minigames/cow-clicker.js
@@ -41,10 +41,13 @@ your own mini-game...
***/
+if (__plugin.canary || __plugin.bukkit){
+ console.warn('cow-clicker minigame is not yet supported in CanaryMod and Craftbukkit');
+ return;
+}
var store = {},
bkBukkit = org.bukkit.Bukkit,
bkCow = org.bukkit.entity.Cow,
- bkSound = org.bukkit.Sound,
bkOfflinePlayer = org.bukkit.OfflinePlayer,
scoreboardConfig = {
cowclicker: {
@@ -71,9 +74,9 @@ var _onPlayerInteract = function( event ) {
scoreboard.update( 'cowclicker', player, store[ player.name ].score );
bkBukkit.dispatchCommand( player, 'me clicked a cow!' );
- sound( bkSound.CLICK, 1, 1 );
+ sound( bukkit.sound.CLICK, 1, 1 );
setTimeout( function( ) {
- sound( bkSound.COW_HURT, 10, 0.85 ) ;
+ sound( bukkit.sound.COW_HURT, 10, 0.85 ) ;
}, 200 );
}
};
@@ -123,7 +126,7 @@ var _addPlayer = function( player, score ) {
store[ player.name ] = { score: score };
scoreboard.update( 'cowclicker', player, store[ player.name ].score);
- player.sendMessage( 'Go forth and click some cows!' );
+ echo( player, 'Go forth and click some cows!' );
};
var _removePlayer = function( player, notify ) {
@@ -145,8 +148,8 @@ var _removePlayer = function( player, notify ) {
delete store[ player.name ];
if ( notify && player ) {
- player.sendMessage( 'You clicked ' + playerScore + ' cows! ' +
- 'You must be tired after all that clicking.' );
+ echo( player, 'You clicked ' + playerScore + ' cows! ' +
+ 'You must be tired after all that clicking.' );
}
};
diff --git a/src/main/js/plugins/signs/examples.js b/src/main/js/plugins/signs/examples.js
index 179ee3f8d..4bb39badf 100644
--- a/src/main/js/plugins/signs/examples.js
+++ b/src/main/js/plugins/signs/examples.js
@@ -12,38 +12,40 @@ var signs = require('signs');
//
var onDinnerChoice = function(event){
- event.player.sendMessage("You chose " + event.text);
+ echo( event.player, 'You chose ' + event.text);
};
-var convertToDinnerMenu = signs.menu("Dinner", ["Lamb","Pork","Chicken","Duck","Beef"], onDinnerChoice);
+var convertToDinnerMenu = signs.menu('Dinner',
+ ['Lamb','Pork','Chicken','Duck','Beef'],
+ onDinnerChoice);
var onTimeChoice = function(event){
- event.player.location.world.setTime( event.number * 6000 );
+ event.player.location.world.setTime( event.number * 6000 );
};
-var convertToTimeMenu = signs.menu("Time", ["Dawn","Midday","Dusk","Midnight"], onTimeChoice);
+var convertToTimeMenu = signs.menu('Time', ['Dawn','Midday','Dusk','Midnight'], onTimeChoice);
exports.signs = {
- menu_food: function(cmdSender){
- var sign = signs.getTargetedBy(cmdSender);
- if (!sign){
- throw new Error('You must look at an existing sign');
- }
- convertToDinnerMenu(sign);
- },
-//
-// This is an example sign that displays a menu of times of day
-// interacting with the sign will change the time of day accordingly.
-//
-// In game, create a sign , target it and type ...
-//
-// /js var signExamples = require('./signs/examples');
-// /js signExamples.timeOfDay()
-//
- menu_time: function(cmdSender){
- var sign = signs.getTargetedBy(cmdSender);
- if (!sign){
- throw new Error('You must look at an existing sign');
- }
- convertToTimeMenu(sign);
+ menu_food: function(cmdSender){
+ var sign = signs.getTargetedBy(cmdSender);
+ if (!sign){
+ throw new Error('You must look at an existing sign');
+ }
+ convertToDinnerMenu(sign);
+ },
+ //
+ // This is an example sign that displays a menu of times of day
+ // interacting with the sign will change the time of day accordingly.
+ //
+ // In game, create a sign , target it and type ...
+ //
+ // /js var signExamples = require('./signs/examples');
+ // /js signExamples.timeOfDay()
+ //
+ menu_time: function(cmdSender){
+ var sign = signs.getTargetedBy(cmdSender);
+ if (!sign){
+ throw new Error('You must look at an existing sign');
}
-}
+ convertToTimeMenu(sign);
+ }
+};
diff --git a/src/main/js/plugins/spawn.js b/src/main/js/plugins/spawn.js
index 1cfd56690..2ff425ae7 100644
--- a/src/main/js/plugins/spawn.js
+++ b/src/main/js/plugins/spawn.js
@@ -1,3 +1,6 @@
+'use strict';
+/*global Packages, __plugin, command, echo, isOp, org */
+/*jslint nomen: true, indent: 2 */
/*************************************************************************
## Spawn Plugin
@@ -12,30 +15,28 @@ Allows in-game operators to easily spawn creatures at current location.
This command supports TAB completion so to see a list of possible
entitities, type `/jsp spawn ' at the in-game command prompt, then
press TAB. Visit
-
+ (Bukkit/SpigotMC)
+or (CanaryMod)
+
for a list of possible entities (creatures) which can be spawned.
***/
-var entities = [],
- bkEntityType = org.bukkit.entity.EntityType;
-
-var entitytypes = bkEntityType.values();
-for ( var t in entitytypes ) {
- if ( entitytypes[t] && entitytypes[t].ordinal ) {
- entities.push(entitytypes[t].name());
- }
+var entities = require('entities'),
+ spawn = require('spawn');
+var entityNames = [];
+for (var name in entities){
+ entityNames.push(name);
}
-command( 'spawn', function( parameters, sender ) {
- if ( !sender.op ) {
- sender.sendMessage( 'Only operators can perform this command' );
+command('spawn', function (parameters, sender) {
+ if (!isOp(sender)) {
+ echo(sender, 'Only operators can perform this command');
return;
}
var location = sender.location;
- if ( !location ) {
- sender.sendMessage( 'You have no location. This command only works in-game.' );
+ if (!location) {
+ echo(sender, 'You have no location. This command only works in-game.');
return;
}
- var world = location.world;
- var type = ('' + parameters[0]).toUpperCase();
- world.spawnEntity( location, bkEntityType[type] );
-}, entities );
+ var name = ('' + parameters[0]).toUpperCase();
+ spawn( name, sender.location);
+}, entityNames);
diff --git a/src/main/resources/Canary.inf b/src/main/resources/Canary.inf
new file mode 100644
index 000000000..47f24d4e3
--- /dev/null
+++ b/src/main/resources/Canary.inf
@@ -0,0 +1,5 @@
+main-class = org.scriptcraftjs.canarymod.ScriptCraftPlugin
+isLibrary = false
+name = ScriptCraft
+author = Walter Higgins
+version = [[version]]
\ No newline at end of file
diff --git a/src/main/resources/boot.js b/src/main/resources/boot.js
index aed84126a..d0f4d543b 100644
--- a/src/main/resources/boot.js
+++ b/src/main/resources/boot.js
@@ -7,12 +7,12 @@ var __scboot = null;
FileReader = java.io.FileReader,
FileOutputStream = java.io.FileOutputStream,
ZipInputStream = java.util.zip.ZipInputStream,
- jsPlugins = new File('plugins/scriptcraft'),
+ //jsPlugins = new File('plugins/scriptcraft'),
+ jsPlugins = new File('scriptcraft'),
initScript = 'lib/scriptcraft.js';
- var unzip = function(path, logger, plugin) {
- var zis = new ZipInputStream(plugin.getResource(path)),
- entry,
+ var unzip = function(zis, logger) {
+ var entry,
reason = null,
unzipFile = false,
zTime = 0,
@@ -57,14 +57,13 @@ var __scboot = null;
/*
Called from Java plugin
*/
- __scboot = function ( plugin, engine )
+ __scboot = function ( plugin, engine, classLoader )
{
- var logger = plugin.logger,
- cfg = plugin.config,
- cfgName,
+ var logger = plugin.canary ? plugin.logman : plugin.logger,
initScriptFile = new File(jsPlugins,initScript),
zips = ['lib','plugins','modules'],
i = 0,
+ zis,
len = zips.length;
if (!jsPlugins.exists()){
@@ -74,14 +73,26 @@ var __scboot = null;
}
for (i = 0; i < len;i++){
- cfgName = 'extract-js.' + zips[i];
- if (cfg.getBoolean(cfgName)){
- unzip( zips[i] + '.zip',logger,plugin);
+ if ( plugin.canary ) {
+ zis = new ZipInputStream(classLoader.getResourceAsStream(zips[i] + '.zip'));
+ unzip( zis, logger );
+ } else {
+ if ( plugin.config.getBoolean('extract-js.' + zips[i]) ) {
+ zis = new ZipInputStream(plugin.getResource(zips[i] + '.zip'));
+ unzip( zis, logger );
+ }
}
}
- plugin.saveDefaultConfig();
-
- engine.eval(new FileReader(initScriptFile));
- __onEnable(engine, plugin, initScriptFile);
+ if (plugin.bukkit) {
+ plugin.saveDefaultConfig();
+ }
+ try {
+ engine.eval(new FileReader(initScriptFile));
+ __onEnable(engine, plugin, initScriptFile);
+ }catch ( e ){
+ var msg = 'Error evaluating ' + initScriptFile + ': ' + e;
+ plugin.canary ? logger.error(msg) : logger.severe(msg);
+ throw e;
+ }
};
})();
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 55e6fd275..a61cf09ec 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,5 +1,5 @@
name: scriptcraft
-main: net.walterhiggins.scriptcraft.ScriptCraftPlugin
+main: org.scriptcraftjs.bukkit.ScriptCraftPlugin
version: [[version]]
commands:
js: