Merge branch 'walterhiggins:master' into master
11
.classpath
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src/docs/java"/>
|
||||||
|
<classpathentry kind="src" path="src/main/java/bukkit"/>
|
||||||
|
<classpathentry kind="src" path="src/main/java/canary"/>
|
||||||
|
<classpathentry kind="src" path="src/main/java/webserver"/>
|
||||||
|
<classpathentry kind="lib" path="lib/bukkit-1.7.10-R0.1-SNAPSHOT.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/canarymod.jar"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="output" path="target"/>
|
||||||
|
</classpath>
|
22
.gitignore
vendored
|
@ -2,7 +2,29 @@ target
|
||||||
*.class
|
*.class
|
||||||
*.js~
|
*.js~
|
||||||
*.js#
|
*.js#
|
||||||
|
*.md#
|
||||||
|
*.md~
|
||||||
|
*.mdt~
|
||||||
|
*.mdt#
|
||||||
*.iml
|
*.iml
|
||||||
|
*.#*
|
||||||
*.idea
|
*.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build.local.properties
|
build.local.properties
|
||||||
|
/src/main/javascript/lib/.#tabcomplete.js
|
||||||
|
/src/main/javascript/plugins/.#example-1.js
|
||||||
|
/nbproject/private/private.xml
|
||||||
|
/nbproject/project.xml
|
||||||
|
/src/docs/nbproject/private/
|
||||||
|
/src/docs/build//#build.xml#
|
||||||
|
/#build.xml#
|
||||||
|
/src/docs/build/classes/generateApiDocs.js
|
||||||
|
/src/docs/build/classes/generateTOC.js
|
||||||
|
/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
|
17
.project
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ScriptCraft</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
5
.travis.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
language: java
|
||||||
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
|
- oraclejdk7
|
||||||
|
- openjdk7
|
331
README.md
|
@ -1,125 +1,266 @@
|
||||||
ScriptCraft
|
# ScriptCraft - Modding Minecraft with Javascript
|
||||||
===========
|
|
||||||
A Minecraft mod that lets you create mods using Javascript.
|
|
||||||
|
|
||||||
Description
|
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/walterhiggins/ScriptCraft?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
===========
|
|
||||||
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. 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.
|
|
||||||
|
|
||||||
ScriptCraft also includes many objects and functions to make building and modding easier using Javascript.
|
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.
|
||||||
|
|
||||||
* echo( message ) - displays a message on the player's screen. e.g. `/js echo( 1 + 3 )` or `/js echo ("Hello World")`
|
I created ScriptCraft to make it easier for younger programmers to
|
||||||
* getMousePos() - A function which returns the current position of the cross-hairs (if a block is targeted)
|
create their own Minecraft Mods. Mods are written using the
|
||||||
* getPlayerPos() - A function which returns the current position of the player.
|
JavaScript programming language. Once the ScriptCraft mod is
|
||||||
* putBlock( x, y, z, blockId, metaData ) - A function which lets you place a block anywhere (if no coordinates are given the block the player is currently looking at is replaced).
|
installed, you can add your own new Mods by adding JavaScript (.js)
|
||||||
* getBlock( x, y, z ) - returns the blockId and metadata at the given location (if no coordinates are given the cross-hair location is used)
|
files in a directory.
|
||||||
* putSign( String[] texts, x, y, z, blockId, metaData ) - A function which lets you place a sign.
|
|
||||||
|
|
||||||
The above primitives can be used to create buildings which would
|
* If you're new to programming and want to start modding Minecraft, then [Start Here][yp].
|
||||||
otherwise be time-consuming to create manually. It is highly
|
* If you've already used [Scratch][scr], have attended a few
|
||||||
recommended using the attached [drone][drone] javascript plugin which
|
[CoderDojo][cd] sessions, or have already dabbled with JavaScript,
|
||||||
provides a fluent API for building. The Javascript `Drone` class
|
then [Start Here][cda].
|
||||||
provides a much richer API which can be used to construct
|
* Watch some [demos][ytpl] of what you can do with ScriptCraft.
|
||||||
buildings. See the attached [cottage.js][cottage] file for an example
|
|
||||||
of you can use the sample Drone plugin to create new buildings in
|
This is a simple mod in a file called greet.js in the scriptcraft/plugins directory:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function greet( player ) {
|
||||||
|
echo( player, 'Hello ' + player.name );
|
||||||
|
}
|
||||||
|
exports.greet = greet;
|
||||||
|
```
|
||||||
|
|
||||||
|
At the in-game prompt, type:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/js greet(self)
|
||||||
|
```
|
||||||
|
|
||||||
|
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 [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
|
||||||
|
bundled with ScriptCraft provides an easy way to build at-scale in
|
||||||
|
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.
|
Minecraft.
|
||||||
|
|
||||||
[drone]: https://github.com/walterhiggins/ScriptCraft/tree/master/src/main/javascript/drone/drone.js
|
[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
|
# 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]
|
|
||||||
|
|
||||||
Installation
|
ScriptCraft is a Minecraft Server Mod which only works with Minecraft
|
||||||
============
|
for Personal computers (Windows, Mac and Linux). It does not work with
|
||||||
If you don't want to compile from source, you can [download the
|
X-BOX, Playstation or WiiU versions of the game. You will need to have
|
||||||
compiled plugin here][dl] and copy it the craftbukkit's plugins
|
Java version 7 or later installed. Check the version by typing `java
|
||||||
directory.
|
-version` at a command prompt.
|
||||||
|
|
||||||
Post Install
|
# Installation
|
||||||
============
|
|
||||||
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 <username>' at the server console prompt or
|
|
||||||
by adding the player's username to the ops.txt file in your
|
|
||||||
craftbukkit directory.
|
|
||||||
|
|
||||||
Launch CraftBukkit, then launch the Minecraft client and create a new
|
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 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 <username>' (replacing <username> 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 the server, then launch the Minecraft client and create a new
|
||||||
server connection. The IP address will be `localhost` . Once you've
|
server connection. The IP address will be `localhost` . Once you've
|
||||||
connected to your bukkit server and have entered the game, look at a
|
connected to your server and have entered the game, look at a
|
||||||
ground-level block and type ...
|
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
|
blocks high by 1 block long. Take a look at the
|
||||||
src/main/javascript/drone/drone.js file to see what ScriptCraft's
|
src/main/javascript/drone/drone.js file to see what ScriptCraft's
|
||||||
drone can do. If you're interested in customizing minecraft beyond
|
drone can do.
|
||||||
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/homes/homes.js
|
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.
|
||||||
[ch]: blob/master/src/main/javascript/chat/color.js
|
|
||||||
[ar]: blob/master/src/main/javascript/arrows/arrows.js
|
|
||||||
[si]: blob/master/src/main/javascript/signs/menu.js
|
|
||||||
|
|
||||||
A Javascript mod for minecraft is just a javascript source file (.js)
|
## Your first mod - Howling blocks
|
||||||
located in the craftbukkit/js-plugins directory. All .js files in this
|
Listed below is a simple mod that will make blocks 'Howl' when they're broken.
|
||||||
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.
|
|
||||||
|
|
||||||
Additional information
|
``` javascript
|
||||||
======================
|
// copy and paste this code to a new file named 'scriptcraft/plugins/howling-blocks.js'
|
||||||
Because the Bukkit API is open, all of the Bukkit API is accessible
|
var sounds = require('sounds');
|
||||||
via javascript once the ScriptCraft plugin is loaded. For example, in
|
function howl(event){
|
||||||
addition to the functions provided in the MCP version of ScriptCraft,
|
sounds.entityWolfHowl( event.block );
|
||||||
there are a couple of useful Java objects exposed via javascript in
|
}
|
||||||
the Bukkit ScriptCraft plugin...
|
events.blockBreak( howl );
|
||||||
|
```
|
||||||
|
|
||||||
* `__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() ).
|
If you're using CanaryMod instead of SpigotMC you can [download the equivalent code](https://gist.github.com/walterhiggins/69cddd15160d803fb096).
|
||||||
* `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.
|
|
||||||
* `server` - The top-level org.bukkit.Server object. See the [Bukkit API docs][bukapi] for reference.
|
|
||||||
|
|
||||||
[dl]: http://scriptcraftjs.org/download
|
A JavaScript mod for minecraft is just a JavaScript source file (.js)
|
||||||
[api]: http://jd.bukkit.org/apidocs/org/bukkit/plugin/java/JavaPlugin.html
|
located in the scriptcraft/plugins directory. All .js files in this
|
||||||
[ib]: http://wiki.bukkit.org/Setting_up_a_server
|
directory will be automatically loaded when the server starts.
|
||||||
[cbdl]: http://dl.bukkit.org/downloads/craftbukkit/
|
|
||||||
[bukapi]: http://jd.bukkit.org/apidocs/
|
|
||||||
|
|
||||||
Further Reading
|
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 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
|
||||||
|
ScriptCraft plugin:
|
||||||
|
|
||||||
|
* `__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 [SpigotMC API docs][spigotapi] for reference.
|
||||||
|
|
||||||
|
* `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 SpigotMC API.
|
||||||
|
|
||||||
|
[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
|
||||||
|
|
||||||
|
If you would like to contribute source code and/or documentation changes please [read contributing.md][contrib]
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
[![Travis Build Status](https://api.travis-ci.org/walterhiggins/ScriptCraft.png)](http://travis-ci.org/walterhiggins/ScriptCraft)
|
||||||
|
|
||||||
|
# Bukkit Configuration
|
||||||
|
## (You can ignore this if using CanaryMod)
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
extract-js:
|
||||||
|
plugins: true
|
||||||
|
modules: true
|
||||||
|
lib: true
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Further Reading
|
||||||
|
|
||||||
ScriptCraft has [its own website][website] with further information.
|
ScriptCraft has [its own website][website] with further information.
|
||||||
|
|
||||||
* To get started using ScriptCraft to Learn Javascript I recommend [reading this][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].
|
* The ScriptCraft [API documentation][api].
|
||||||
* To delve deeper into creating your own minecraft mod, I recommend [reading this][mm].
|
* 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].
|
||||||
|
|
||||||
|
<a href="http://www.amazon.co.uk/gp/product/0133930149/ref=as_li_tl?ie=UTF8&camp=1634&creative=6738&creativeASIN=0133930149&linkCode=as2&tag=walthigg-21&linkId=P3LLGB3WTATW57AZ"><img border="0" src="http://ws-eu.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=0133930149&Format=_SL250_&ID=AsinImage&MarketPlace=GB&ServiceVersion=20070822&WS=1&tag=walthigg-21" ></a><img src="http://ir-uk.amazon-adsystem.com/e/ir?t=walthigg-21&l=as2&o=2&a=0133930149" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
|
||||||
|
|
||||||
You can find more information about [ScriptCraft on my blog][blog].
|
You can find more information about [ScriptCraft on my blog][blog].
|
||||||
|
|
||||||
[blog]: http://walterhiggins.net/blog/cat-index-scriptcraft.html
|
# Additional Resources
|
||||||
[buk]: https://github.com/walterhiggins/ScriptCraft/blob/master/bukkit.md
|
|
||||||
[yp]: http://walterhiggins.net/blog/YoungPersonProgrammingMinecraft
|
|
||||||
[mm]: http://walterhiggins.net/blog/ScriptCraft-1-Month-later
|
|
||||||
[api]: https://github.com/walterhiggins/ScriptCraft/blob/master/docs/api.md
|
|
||||||
[website]: http://scriptcraftjs.org/
|
|
||||||
|
|
||||||
|
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
|
||||||
|
[yp]: docs/YoungPersonsGuideToProgrammingMinecraft.md
|
||||||
|
[mm]: docs/Anatomy-of-a-Plugin.md
|
||||||
|
[api]: docs/API-Reference.md
|
||||||
|
[website]: http://scriptcraftjs.org/
|
||||||
|
[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]: 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
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
bukkit-version=1.4.7
|
scriptcraft-version=3.2.1
|
||||||
|
|
280
build.xml
|
@ -1,11 +1,42 @@
|
||||||
<project name="scriptcraft" default="package" basedir=".">
|
<project name="scriptcraft" default="package" basedir=".">
|
||||||
<property file="build.properties"/>
|
<property file="build.properties"/>
|
||||||
<description>Builds the scriptcraft.jar file - a plugin for bukkit
|
<description>Builds the scriptcraft.jar file - a plugin for bukkit</description>
|
||||||
</description>
|
<!-- ScriptCraft works with:
|
||||||
<property name="src" location="src/main/java"/>
|
* CanaryMod
|
||||||
|
* Bukkit
|
||||||
|
* Glowstone (which is bukkit-compatible)
|
||||||
|
* Spigot (which is bukkit-compatible)
|
||||||
|
-->
|
||||||
|
<property name="src.canary" location="src/main/java/canary"/>
|
||||||
|
<property name="src.bukkit" location="src/main/java/bukkit"/>
|
||||||
|
|
||||||
|
<!-- compiles against these libraries -->
|
||||||
|
<property name="lib.canary" location="lib/canarymod-1.8.0.jar"/>
|
||||||
|
<property name="lib.bukkit" location="lib/spigot-1.11.2.jar"/>
|
||||||
|
|
||||||
<property name="build" location="target/classes"/>
|
<property name="build" location="target/classes"/>
|
||||||
<property name="dist" location="target/" />
|
<property name="dist" location="target/" />
|
||||||
<property name="minecraft.dir" location="${dist}/minecraft" />
|
<property name="minecraft.dir" location="${dist}/minecraft" />
|
||||||
|
<property name="js-plugins-dir" value="scriptcraft"/>
|
||||||
|
<property name="http.agent" value="'Walter'" />
|
||||||
|
|
||||||
|
<macrodef name="jscript">
|
||||||
|
<attribute name="src"/>
|
||||||
|
<attribute name="out"/>
|
||||||
|
<attribute name="err"/>
|
||||||
|
<element name="js-args" implicit="yes" optional="true"/>
|
||||||
|
<sequential>
|
||||||
|
<java classname="jscript" failonerror="true" fork="true" output="@{out}" error="@{err}">
|
||||||
|
<classpath>
|
||||||
|
<pathelement path="${build}"/>
|
||||||
|
<pathelement path="${lib.canary}"/>
|
||||||
|
<pathelement path="${lib.bukkit}"/>
|
||||||
|
</classpath>
|
||||||
|
<arg value="@{src}"/>
|
||||||
|
<js-args/>
|
||||||
|
</java>
|
||||||
|
</sequential>
|
||||||
|
</macrodef>
|
||||||
|
|
||||||
<target name="init">
|
<target name="init">
|
||||||
<property file="build.local.properties"/>
|
<property file="build.local.properties"/>
|
||||||
|
@ -16,101 +47,216 @@
|
||||||
</tstamp>
|
</tstamp>
|
||||||
<mkdir dir="${build}"/>
|
<mkdir dir="${build}"/>
|
||||||
|
|
||||||
<available file="${minecraft.dir}" property="minecraft.present"/>
|
<available file="${lib.canary}" property="canary.present"/>
|
||||||
|
<available file="${dist}/js/modules/underscore.js" property="underscore.present"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="server-setup" depends="init" description="Downloads the latest bukkit dev jar"
|
<target name="test" depends="package" description="Perform unit tests">
|
||||||
unless="minecraft.present">
|
|
||||||
<mkdir dir="${minecraft.dir}" />
|
|
||||||
|
|
||||||
<echo>Retrieving CraftBukkit artifact info
|
|
||||||
</echo>
|
|
||||||
<get src="http://dl.bukkit.org/api/1.0/downloads/projects/CraftBukkit?_accept=application/xml"
|
|
||||||
dest="${minecraft.dir}/bukkit.xml" />
|
|
||||||
|
|
||||||
<xslt in="${minecraft.dir}/bukkit.xml"
|
|
||||||
out="${minecraft.dir}/ant.properties.xml"
|
|
||||||
style="build/bukkit-to-url.xsl"/>
|
|
||||||
<xmlproperty file="${minecraft.dir}/ant.properties.xml" keeproot="true"/>
|
|
||||||
|
|
||||||
<echo>Retrieving CraftBukkit jar
|
|
||||||
</echo>
|
|
||||||
<get src="${bukkit.url}"
|
|
||||||
dest="${minecraft.dir}/craftbukkit.jar"
|
|
||||||
verbose="true"/>
|
|
||||||
|
|
||||||
<echo>Creating default ops.txt for your user
|
|
||||||
</echo>
|
|
||||||
<echo message="${op.name}" file="${minecraft.dir}/ops.txt" />
|
|
||||||
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
<target name="run" depends="server-setup, package, update-live-cb" description="Starts Bukkit with ScriptCraft">
|
<target name="compile-plugins" depends="init" description="compile canary plugin source">
|
||||||
<echo>Starting Bukkit with ScriptCraft
|
<javac includeantruntime="false"
|
||||||
</echo>
|
source="1.6"
|
||||||
<java jar="${minecraft.dir}/craftbukkit.jar"
|
target="1.6"
|
||||||
maxmemory="1024m"
|
destdir="${build}"
|
||||||
fork="true"
|
debug="true">
|
||||||
dir="${minecraft.dir}"
|
<src path="${src.canary}"/>
|
||||||
/>
|
<src path="${src.bukkit}"/>
|
||||||
|
<classpath>
|
||||||
|
<pathelement path="${lib.canary}" />
|
||||||
|
<pathelement path="${lib.bukkit}" />
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="gendocs" depends="construct-ypgpm, construct-api-ref" description="Generate API documentation">
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile-docs" depends="init">
|
||||||
|
<javac includeantruntime="false" srcdir="src/docs/java" destdir="${build}">
|
||||||
|
<classpath>
|
||||||
|
<pathelement path="${lib.canary}"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="generate-api-ref-entries" depends="copy-js,compile-docs,init">
|
||||||
|
|
||||||
|
<jscript src="src/docs/js/generateApiDocs.js"
|
||||||
|
out="${dist}/apiref.md"
|
||||||
|
err="${dist}/gen-api-error.log">
|
||||||
|
<arg value="${dist}/js"/>
|
||||||
|
</jscript>
|
||||||
|
|
||||||
|
<jscript src="src/docs/js/generateItemsDoc.js"
|
||||||
|
out="${dist}/items.md"
|
||||||
|
err="${dist}/gen-items-error.log" />
|
||||||
|
|
||||||
|
<jscript src="src/docs/js/generateEntitiesDoc.js"
|
||||||
|
out="${dist}/entities.md"
|
||||||
|
err="${dist}/gen-entities-error.log" />
|
||||||
|
|
||||||
|
<concat destfile="${dist}/apiref-con.md">
|
||||||
|
<fileset file="${dist}/apiref.md" />
|
||||||
|
<fileset file="${dist}/items.md" />
|
||||||
|
<fileset file="${dist}/entities.md" />
|
||||||
|
</concat>
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
<target name="gen-events-helper-canary" depends="compile-docs,init">
|
||||||
|
<mkdir dir="${dist}/js/lib"/>
|
||||||
|
<jscript src="src/docs/js/generateEventsHelper.js"
|
||||||
|
out="${dist}/js/lib/events-helper-canary.js"
|
||||||
|
err="${dist}/gen-events-canary-error.log">
|
||||||
|
<arg value="CanaryMod"/>
|
||||||
|
<arg value="${lib.canary}"/>
|
||||||
|
<arg value="blockDestroy"/>
|
||||||
|
<arg value="net.canarymod.hook.player.BlockDestroyHook"/>
|
||||||
|
</jscript>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="gen-events-helper-bukkit" depends="compile-docs,init">
|
||||||
|
<mkdir dir="${dist}/js/lib"/>
|
||||||
|
<jscript src="src/docs/js/generateEventsHelper.js"
|
||||||
|
out="${dist}/js/lib/events-helper-bukkit.js"
|
||||||
|
err="${dist}/gen-events-bukkit-error.log">
|
||||||
|
<arg value="SpigotMC"/>
|
||||||
|
<arg value="${lib.bukkit}"/>
|
||||||
|
<arg value="blockBreak"/>
|
||||||
|
<arg value="org.bukkit.event.block.BlockBreakEvent"/>
|
||||||
|
</jscript>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="construct-api-ref" depends="gen-toc-apiref,init">
|
||||||
|
|
||||||
|
<concat destfile="docs/API-Reference.md">
|
||||||
|
<header filtering="no" trimleading="yes"><!--
|
||||||
|
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
|
||||||
|
|
||||||
|
[walter.higgins@gmail.com][email]
|
||||||
|
|
||||||
|
[email]: mailto:walter.higgins@gmail.com?subject=ScriptCraft_API_Reference
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<fileset file="${dist}/toc-apiref.md" />
|
||||||
|
<fileset file="${dist}/apiref-con.md" />
|
||||||
|
</concat>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="compile" depends="init, server-setup" description="compile bukkit plugin source">
|
<target name="gen-toc-apiref" depends="compile-docs,generate-api-ref-entries, init" description="Generate Table of Contents for API Reference">
|
||||||
<javac includeantruntime="false" srcdir="${src}" destdir="${build}" classpath="${minecraft.dir}/craftbukkit.jar" />
|
<jscript src="src/docs/js/generateTOC.js"
|
||||||
</target>
|
out="${dist}/toc-apiref.md"
|
||||||
|
err="${dist}/gen-toc-error.log">
|
||||||
<target name="gendocs" depends="init" description="Generate API documentation">
|
<arg value="${dist}/apiref-con.md"/>
|
||||||
<javac includeantruntime="false" srcdir="src/docs/java"/>
|
</jscript>
|
||||||
<java classname="jscript" failonerror="true" fork="true" output="docs/api.md">
|
|
||||||
<classpath>
|
|
||||||
<pathelement path="src/docs/java"/>
|
|
||||||
</classpath>
|
|
||||||
<arg value="src/docs/javascript/generateApiDocs.js"/>
|
|
||||||
<arg value="src/main/javascript"/>
|
|
||||||
</java>
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="coffeescript_setup" depends="init" description="Gets latest coffeescript compiler">
|
<target name="gen-toc-ypgpm" depends="compile-docs,init" description="Generate Table of Contents for Young Programmers Guide">
|
||||||
<echo>Retrieving Coffeescript compiler
|
<jscript src="src/docs/js/generateTOC.js"
|
||||||
</echo>
|
out="${dist}/toc-ypgpm.md"
|
||||||
<mkdir dir="${build}/coffeescript/core" />
|
err="${dist}/gen-ypgpm-error.log">
|
||||||
<get src="https://raw.github.com/jashkenas/coffee-script/master/extras/coffee-script.js"
|
<arg value="src/docs/templates/ypgpm.md"/>
|
||||||
dest="${build}/coffeescript/core/_coffeescript.js"/>
|
</jscript>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="zip_js" depends="coffeescript_setup">
|
<!-- Piece together the Young persons' guide from template and generated table of contents -->
|
||||||
<zip destfile="${build}/js-plugins.zip">
|
<target name="construct-ypgpm" depends="gen-toc-ypgpm,init">
|
||||||
<zipfileset dir="./src/main/javascript" />
|
<concat destfile="docs/YoungPersonsGuideToProgrammingMinecraft.md">
|
||||||
<zipfileset dir="${build}/coffeescript" />
|
<header filtering="no" trimleading="yes"><!--
|
||||||
|
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
|
||||||
|
</header>
|
||||||
|
<fileset file="${dist}/toc-ypgpm.md" />
|
||||||
|
<fileset file="src/docs/templates/ypgpm.md" />
|
||||||
|
</concat>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="zip_js" depends="zip_lib, zip_modules, zip_plugins">
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="copy-js" depends="gen-events-helper-bukkit,gen-events-helper-canary,init">
|
||||||
|
<copy todir="${dist}/js">
|
||||||
|
<fileset dir="src/main/js"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="zip_lib" depends="copy-js">
|
||||||
|
<delete file="${build}/lib.zip"/>
|
||||||
|
<zip destfile="${build}/lib.zip"
|
||||||
|
basedir="${dist}/js"
|
||||||
|
excludes="modules/**, plugins/**,">
|
||||||
</zip>
|
</zip>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="package" depends="gendocs,zip_js,compile"
|
<target name="get_underscore" depends="copy-js" unless="underscore.present">
|
||||||
description="generate the distribution" >
|
<!-- download underscore -->
|
||||||
|
<get src="http://underscorejs.org/underscore.js"
|
||||||
|
verbose="true"
|
||||||
|
dest="${dist}/js/modules/underscore.js"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="zip_modules" depends="copy-js, get_underscore">
|
||||||
|
<delete file="${build}/modules.zip"/>
|
||||||
|
<zip destfile="${build}/modules.zip"
|
||||||
|
basedir="${dist}/js"
|
||||||
|
excludes="lib/**, plugins/**,">
|
||||||
|
</zip>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="zip_plugins" depends="copy-js">
|
||||||
|
<delete file="${build}/plugins.zip"/>
|
||||||
|
<zip destfile="${build}/plugins.zip"
|
||||||
|
basedir="${dist}/js"
|
||||||
|
excludes="lib/**, modules/**">
|
||||||
|
</zip>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="package" depends="gendocs,zip_js,compile-plugins" description="generate the distribution" >
|
||||||
<!-- ensure plugin.yml is always copied -->
|
<!-- ensure plugin.yml is always copied -->
|
||||||
<delete file="${build}/plugin.yml" />
|
<delete file="${build}/plugin.yml" />
|
||||||
<copy file="src/main/resources/plugin.yml" todir="${build}"/>
|
<delete file="${build}/Canary.inf" />
|
||||||
<replace file="${build}/plugin.yml" value="${bukkit-version}-${DSTAMP}">
|
<copy todir="${build}">
|
||||||
<replacetoken>[[version]]
|
<fileset dir="src/main/resources"/>
|
||||||
</replacetoken>
|
</copy>
|
||||||
|
<replace file="${build}/plugin.yml" value="${scriptcraft-version}-${DSTAMP}">
|
||||||
|
<replacetoken>[[version]]</replacetoken>
|
||||||
|
</replace>
|
||||||
|
<replace file="${build}/Canary.inf" value="${scriptcraft-version}-${DSTAMP}">
|
||||||
|
<replacetoken>[[version]]</replacetoken>
|
||||||
</replace>
|
</replace>
|
||||||
|
|
||||||
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
|
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
|
||||||
<mkdir dir="${dist}/${DSTAMP}" />
|
<mkdir dir="${dist}/${DSTAMP}" />
|
||||||
<jar jarfile="${dist}/${DSTAMP}/ScriptCraft.jar" basedir="${build}"/>
|
|
||||||
|
<jar jarfile="${dist}/${DSTAMP}/scriptcraft.jar" basedir="${build}"/>
|
||||||
|
<copy file="${dist}/${DSTAMP}/scriptcraft.jar" tofile="${dist}/scriptcraft.jar"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="clean" description="clean up" >
|
<target name="clean" description="clean up" >
|
||||||
<delete dir="${dist}"/>
|
<delete dir="${dist}"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="update-live-cb" depends="package" description="Copy the built plugin to the live craftbukkit folder for testing.">
|
<target name="update-live-cb" depends="package" description="Copy the built plugin to the live folder for testing.">
|
||||||
<mkdir dir="${minecraft.dir}/plugins" />
|
<mkdir dir="${minecraft.dir}/plugins" />
|
||||||
<delete>
|
<delete>
|
||||||
<fileset dir="${minecraft.dir}/plugins/" includes="scriptcraft*.*"/>
|
<fileset dir="${minecraft.dir}/plugins/" includes="scriptcraft*.*"/>
|
||||||
</delete>
|
</delete>
|
||||||
<mkdir dir="${minecraft.dir}/plugins" />
|
<mkdir dir="${minecraft.dir}/plugins" />
|
||||||
<copy file="${dist}/${DSTAMP}/ScriptCraft.jar" todir="${minecraft.dir}/plugins"/>
|
<copy file="${dist}/${DSTAMP}/scriptcraft.jar" todir="${minecraft.dir}/plugins"/>
|
||||||
</target>
|
</target>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
<xsl:stylesheet
|
|
||||||
version="1.0"
|
|
||||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
||||||
|
|
||||||
<xsl:template match="list-item[name='Development Build']">
|
|
||||||
<bukkit>
|
|
||||||
<url><xsl:text>http://dl.bukkit.org</xsl:text><xsl:value-of select="latest_artifact/file/url" /></url>
|
|
||||||
</bukkit>
|
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="text()" />
|
|
||||||
|
|
||||||
</xsl:stylesheet>
|
|
119
contributing.md
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
# Status
|
||||||
|
|
||||||
|
[![Travis Build Status](https://api.travis-ci.org/walterhiggins/ScriptCraft.png)](http://travis-ci.org/walterhiggins/ScriptCraft)
|
||||||
|
|
||||||
|
# Notes for Contributors
|
||||||
|
|
||||||
|
This project uses a Maven-like directory structure...
|
||||||
|
|
||||||
|
src +
|
||||||
|
main +
|
||||||
|
java +
|
||||||
|
net +
|
||||||
|
walterhiggins +
|
||||||
|
scriptcraft +
|
||||||
|
ScriptCraftPlugin.java
|
||||||
|
|
||||||
|
js +
|
||||||
|
lib +
|
||||||
|
(core javascript code goes here. Modules in this directory
|
||||||
|
should not be 'require'd by plugin or module authors)
|
||||||
|
modules +
|
||||||
|
(this is where module authors should put modules for
|
||||||
|
use by others)
|
||||||
|
plugins +
|
||||||
|
(this is where plugins - scriptcraft extensions for use by
|
||||||
|
operators and players should go)
|
||||||
|
resources +
|
||||||
|
plugin.yml
|
||||||
|
docs +
|
||||||
|
templates +
|
||||||
|
(documentation templates go here. If you want to make
|
||||||
|
changes to the young persons guide should be made to ypgpm.md)
|
||||||
|
ypgpm.md
|
||||||
|
javascript +
|
||||||
|
(javascript source used to build the API reference and
|
||||||
|
table of contents for the API reference and Programming Guide
|
||||||
|
is located here)
|
||||||
|
docs +
|
||||||
|
(the following files should not be edited directly because they are constructed
|
||||||
|
during the build process - yeah I know they strictly shouldn't be under source control but
|
||||||
|
it's nice to have the markdown docs on github for reading by non-contributors)
|
||||||
|
|
||||||
|
API-Reference.md
|
||||||
|
Young-Persons-Guide.md
|
||||||
|
|
||||||
|
(this project is build using Ant, type `ant` at the command line to build.)
|
||||||
|
|
||||||
|
build.xml
|
||||||
|
build.properties
|
||||||
|
|
||||||
|
## Core javascript modules
|
||||||
|
|
||||||
|
ScriptCraft's deployed core consists of a single Java source file (the
|
||||||
|
Bukkit Plugin) and a tiny set of javascript source files located in
|
||||||
|
the src/main/javascript/lib directory. All other javascript files are
|
||||||
|
optional modules and plugins. `scriptcraft.js` is the first file
|
||||||
|
loaded by the Java plugin. scriptcraft.js in turn loads the require.js
|
||||||
|
file which initializes the commonJS `require()` function and all other
|
||||||
|
files in the lib directory are then loaded. Finally all of the modules
|
||||||
|
in the plugins directory are automatically loaded and any exports are
|
||||||
|
automatically exported to the global namespace. For example a file
|
||||||
|
called `greet.js` located in the plugins folder...
|
||||||
|
|
||||||
|
// plugins/greet.js contents
|
||||||
|
|
||||||
|
exports.greet = function(sender){
|
||||||
|
sender.sendMessage('hello')
|
||||||
|
}
|
||||||
|
|
||||||
|
... will be loaded at startup and the `greet` function will be
|
||||||
|
global. Anyone with operator privileges can type `/js greet(self)` at
|
||||||
|
the in-game command prompt to execute the function.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
See <https://github.com/rwaldron/idiomatic.js> for a recommended
|
||||||
|
approach to writing javascript code for ScriptCraft. ScriptCraft is
|
||||||
|
aimed at younger programmers so readability is important - moreso than
|
||||||
|
cleverness. If submitting new code for inclusion in ScriptCraft please
|
||||||
|
ensure it is documented using the guidelines below...
|
||||||
|
|
||||||
|
## Documentation contributions
|
||||||
|
|
||||||
|
The Young persons guide to programming source file is located at
|
||||||
|
/src/docs/templates/ypgpm.md . *Do not make changes to
|
||||||
|
/docs/YoungPersonsGuide.md*
|
||||||
|
|
||||||
|
The API Reference is generated by the build from markdown comments
|
||||||
|
embedded in the javascript source. If you would like comments for
|
||||||
|
contributed code to be included in the API reference then enclose your
|
||||||
|
comment as follows:
|
||||||
|
|
||||||
|
* Start the comment block with a `/**********` (a forward-slash
|
||||||
|
followed by 10 or more asterisk characters). *The start block must
|
||||||
|
be at the start of the line*.
|
||||||
|
|
||||||
|
* End the comment block with a `***/` (3 asterisk followed by a
|
||||||
|
forward slash) at the start of a new line.
|
||||||
|
|
||||||
|
This is an example of a comment which will be included in the API reference...
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
## foo() function
|
||||||
|
|
||||||
|
The foo() function performs foo-type operatations on all bars.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* name : Name of the foo
|
||||||
|
* count: Number of foos to perform
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
foo() returns a list of foos that were changed.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
Top level comments for a module should be a H2 heading `##`. Please
|
||||||
|
don't use a H1 heading ( `#` ) as this is used for the top-level API
|
||||||
|
document title.
|
7458
docs/API-Reference.md
Normal file
175
docs/Anatomy-of-a-Plugin.md
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
# 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
|
||||||
|
demonstrate this, I've recreated a commonly-used mod (homes) in
|
||||||
|
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 …
|
||||||
|
|
||||||
|
* Persistence
|
||||||
|
* Adding Player (non-operator) commands
|
||||||
|
|
||||||
|
[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 …
|
||||||
|
|
||||||
|
```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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
want players to be able to use that function, you expose it using the
|
||||||
|
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 …
|
||||||
|
|
||||||
|
/jsp chat_color yellow
|
||||||
|
|
||||||
|
… 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
|
||||||
|
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 …
|
||||||
|
|
||||||
|
```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
|
||||||
|
modders a feel for just how easy it can be to change the game to suit
|
||||||
|
their needs.
|
||||||
|
|
||||||
|
[1]: img/scriptcraft-chat-color.png
|
||||||
|
|
46
docs/Frequently-Asked-Questions.md
Normal file
|
@ -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');
|
||||||
|
```
|
126
docs/Using-Java-APIs-In-Javascript.md
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
# Using Java APIs in Javascript
|
||||||
|
|
||||||
|
ScriptCraft uses the Javascript Engine bundled with Java 6 and later
|
||||||
|
versions. This means that all of the core Java classes can be used
|
||||||
|
from within ScriptCraft. In addition, all of the Bukkit API can be
|
||||||
|
used from Javascript too. There are some things to consider when using
|
||||||
|
Java classes in Javascript...
|
||||||
|
|
||||||
|
## Using Java Beans
|
||||||
|
|
||||||
|
The Javascript Engine bundled with Java comes with a handy notation
|
||||||
|
for accessing and modifying Java Beans. A Java Bean is any Java class
|
||||||
|
which uses a `get{Property}()` method to retrieve an object's property
|
||||||
|
and a `set{Property}()` method to set the object's property. One
|
||||||
|
example of a Java Bean in the [Bukkit API][bukapi] is the
|
||||||
|
[org.bukkit.entity.Player][bukpl] Class which has many methods which
|
||||||
|
conform to the JavaBean specification.
|
||||||
|
|
||||||
|
For example the [Player.getWalkSpeed()][bukplws] can be used to get a
|
||||||
|
player's walking speed. In Java you would have to write code like this
|
||||||
|
to obtain the walking speed...
|
||||||
|
|
||||||
|
float walkingSpeed = player.getWalkSpeed();
|
||||||
|
|
||||||
|
... however, in Javascript you can access the walking-speed property
|
||||||
|
using the more succinct...
|
||||||
|
|
||||||
|
var walkingspeed = player.walkSpeed;
|
||||||
|
|
||||||
|
... or if you prefer to use Java-style access...
|
||||||
|
|
||||||
|
var walkingspeed = player.getWalkSpeed();
|
||||||
|
|
||||||
|
... I personally prefer to use the simpler `player.walkSpeed` because
|
||||||
|
it is easier to read. The important thing to remember when using the
|
||||||
|
Bukkit (or any Java API) from Javascript is that for any Java Bean, a
|
||||||
|
property called `propertyName` will have a getter called
|
||||||
|
`getPropertyName()` and a setter called `setPropertyName()`. From this
|
||||||
|
rule you can infer what any Bukkit class properties are. For example,
|
||||||
|
the [Bukkit Player][bukpl] object has the following methods...
|
||||||
|
|
||||||
|
* float getWalkSpeed()
|
||||||
|
* void setWalkSpeed(float speed)
|
||||||
|
|
||||||
|
... so from this you can infer that every Player object has a
|
||||||
|
`walkSpeed` property which can be read and changed. For example you
|
||||||
|
can triple your own walking speed (from the default 0.2) at the
|
||||||
|
in-game prompt using the following command...
|
||||||
|
|
||||||
|
/js self.walkSpeed = self.walkSpeed * 3;
|
||||||
|
|
||||||
|
... If we were limited to using Java's notation we would have had to
|
||||||
|
write `/js self.setWalkSpeed( self.getWalkSpeed() * 3 )` . Since
|
||||||
|
almost every class in the Bukkit API is also a JavaBean you can access
|
||||||
|
properties of properties and so on. For example, to get the name of
|
||||||
|
the world in which a player is located...
|
||||||
|
|
||||||
|
/js self.location.world.name
|
||||||
|
|
||||||
|
... is more concise than `/js self.getLocation().getWorld().getName()`.
|
||||||
|
If you're new to Java and the [Bukkit API][bukapi] is the first time
|
||||||
|
you've browsed Java documentation, you may be wondering where the
|
||||||
|
`location` property came from - the `location` property is "inherited"
|
||||||
|
from one of the Player class's super-classes (it's ancestors). You'll see the
|
||||||
|
`getLocation()` method listed under a section titled **Methods
|
||||||
|
inherited from interface org.bukkit.entity.Entity** in the
|
||||||
|
[Player][bukpl] javadoc page.
|
||||||
|
|
||||||
|
## Using java.lang package classes
|
||||||
|
|
||||||
|
In Java the following code will print out the `user.dir` and
|
||||||
|
`user.timezone` properties...
|
||||||
|
|
||||||
|
System.out.println( System.getProperty( "user.dir" ) );
|
||||||
|
System.out.println( System.getProperty( "user.timezone" ) );
|
||||||
|
|
||||||
|
... In Java, any classes in the `java.lang` package don't need to be
|
||||||
|
prefixed with the package so the `java.lang.System` class can simply
|
||||||
|
be written as `System`. However, in Javascript classes in the
|
||||||
|
`java.lang` package need to be fully qualified so you need to write...
|
||||||
|
|
||||||
|
println( java.lang.System.getProperty( "user.dir" ) );
|
||||||
|
println( java.lang.System.getProperty( "user.timezone" ) );
|
||||||
|
|
||||||
|
... the `println()` function is one of the default functions provided
|
||||||
|
by the JS Engine in Java so there is no need to add the class name
|
||||||
|
prefix, but for other System class methods you need to explicitly
|
||||||
|
include the package name e.g. `java.lang.`. If you are using the
|
||||||
|
System class in a number of statements you can save yourself some
|
||||||
|
typing by declaring a System variable and using that instead of the
|
||||||
|
fully-qualified package and class name...
|
||||||
|
|
||||||
|
var System = java.lang.System;
|
||||||
|
println( System.getProperty( "user.dir" ) );
|
||||||
|
println( System.getProperty( "user.timezone" ) );
|
||||||
|
|
||||||
|
The JS Engine provides an `importPackage()` function which can be used
|
||||||
|
to import packages. This also saves you having to type full package
|
||||||
|
names before classes. For example...
|
||||||
|
|
||||||
|
importPackage(java.util);
|
||||||
|
var hMap = new HashMap();
|
||||||
|
hMap.put('name','Walter');
|
||||||
|
|
||||||
|
... makes all of the classes in the Java Library's `java.util` package
|
||||||
|
available for use without having to use the `java.util`
|
||||||
|
prefix. However, importing the `java.lang` package is not recommended
|
||||||
|
as some of the java.lang classes (e.g. String, Object) conflict with
|
||||||
|
Javascript Object types.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
When writing modules or plugins in ScriptCraft, you can access and
|
||||||
|
change JavaBean properties using a simple .{propertyName} notation
|
||||||
|
instead of using the Java .get{PropertyName}() and .set{PropertyName()
|
||||||
|
methods. This results in more concise code. This simpler notation is
|
||||||
|
provided by the Javascript Engine embedded in Java 6 and later
|
||||||
|
versions. Javascript does not have access to private members, the
|
||||||
|
.{propertyName} notation is automagically converted to the appropriate
|
||||||
|
.get{PropertyName}() or .set{PropertyName}() method by Java.
|
||||||
|
|
||||||
|
[bukapi]: http://jd.bukkit.org/beta/apidocs/
|
||||||
|
[bukpl]: http://jd.bukkit.org/beta/apidocs/org/bukkit/entity/Player.html
|
||||||
|
[bukplws]: http://jd.bukkit.org/beta/apidocs/org/bukkit/entity/Player.html#getWalkSpeed()
|
||||||
|
[buksrv]: http://jd.bukkit.org/beta/apidocs/org/bukkit/Server.html
|
||||||
|
|
1466
docs/api.md
BIN
docs/img/castleex1.png
Normal file
After Width: | Height: | Size: 387 KiB |
BIN
docs/img/chessboardex1.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
docs/img/cottageex1.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
docs/img/cottageroadex1.png
Normal file
After Width: | Height: | Size: 159 KiB |
BIN
docs/img/cowclicker.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
docs/img/dancefloorex1.png
Normal file
After Width: | Height: | Size: 163 KiB |
BIN
docs/img/fortex1.png
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
docs/img/lcdclockex1.png
Normal file
After Width: | Height: | Size: 165 KiB |
BIN
docs/img/mazeex1.png
Normal file
After Width: | Height: | Size: 324 KiB |
BIN
docs/img/scriptcraft-chat-color.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
docs/img/templeex1.png
Normal file
After Width: | Height: | Size: 306 KiB |
1
lib/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/canarymod.jar
|
BIN
lib/canarymod-1.8.0.jar
Normal file
BIN
lib/spigot-1.11.2.jar
Normal file
21
license.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
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
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -1,43 +0,0 @@
|
||||||
package net.minecraft.src;
|
|
||||||
/**
|
|
||||||
* This mod lets you load and run javascript to build structures which
|
|
||||||
* would otherwise be tedious. Build road networks, rows of houses,
|
|
||||||
* factories and sky-scrapers. ScriptCraft takes building to a whole
|
|
||||||
* new level by making it easy to create javascript scripts that do
|
|
||||||
* the building for you. The following code creates a simple cottage
|
|
||||||
* at the crosshair location or the player's current location...
|
|
||||||
*
|
|
||||||
* load("./drone.js");
|
|
||||||
* var drone = new Drone().chkpt('cornerstone');
|
|
||||||
* drone.box0(48,7,2,6) // 4 walls
|
|
||||||
* .right(3).door() // a door front-center
|
|
||||||
* .left(2).box(102) // windows left and right of door
|
|
||||||
* .right(4).box(102) //
|
|
||||||
* .move('cornerstone').up(2).prism0(53,7,6); // a gable roof
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class CommandScript extends CommandBase
|
|
||||||
{
|
|
||||||
ScriptCraftEvaluator evaluator = null;
|
|
||||||
|
|
||||||
public String getCommandName() { return "js"; }
|
|
||||||
|
|
||||||
public int getRequiredPermissionLevel() { return 0; }
|
|
||||||
|
|
||||||
public void processCommand(ICommandSender par1ICommandSender, String[] args)
|
|
||||||
{
|
|
||||||
if (this.evaluator == null)
|
|
||||||
this.evaluator = new ScriptCraftEvaluator(new ScriptCraftMCP(this));
|
|
||||||
|
|
||||||
// Collect the arguments into a single string.
|
|
||||||
String s = "";
|
|
||||||
for (int i=0; i < args.length; i++) {
|
|
||||||
s += args[i] + " ";
|
|
||||||
}
|
|
||||||
// Now evaluate the string we've colected.
|
|
||||||
this.evaluator.eval(s,par1ICommandSender);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package net.minecraft.src;
|
|
||||||
|
|
||||||
public interface IScriptCraft
|
|
||||||
{
|
|
||||||
public double[] getPlayerPos();
|
|
||||||
public double[] getMousePos();
|
|
||||||
public void putSign(String[] texts,int x, int y, int z, int block, int meta);
|
|
||||||
public void putBlock(int x, int y, int z, int blockId, int meta);
|
|
||||||
public String getBlock(int x, int y, int z);
|
|
||||||
public void notifyAdministrators(String message);
|
|
||||||
public void setInvoker(Object invoker);
|
|
||||||
}
|
|
|
@ -1,265 +0,0 @@
|
||||||
package net.minecraft.src;
|
|
||||||
|
|
||||||
import org.mozilla.javascript.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.io.*;
|
|
||||||
import javax.swing.JFileChooser;
|
|
||||||
|
|
||||||
public class ScriptCraftEvaluator
|
|
||||||
{
|
|
||||||
protected static IScriptCraft sc = null;
|
|
||||||
protected Context ctx = null;
|
|
||||||
protected Scriptable scope = null;
|
|
||||||
|
|
||||||
public static class MCScope extends ImporterTopLevel{
|
|
||||||
public MCScope(Context ctx){
|
|
||||||
super(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public ScriptCraftEvaluator(IScriptCraft scImpl)
|
|
||||||
{
|
|
||||||
ScriptCraftEvaluator.sc = scImpl;
|
|
||||||
this.ctx = Context.enter();
|
|
||||||
ScriptableObject importer = new ScriptCraftEvaluator.MCScope(ctx);
|
|
||||||
this.scope = this.ctx.initStandardObjects(importer);
|
|
||||||
//
|
|
||||||
// for mcp debug only
|
|
||||||
//ctx.evaluateString(scope,"importPackage(net.minecraft.src)","<cmd>",1,null);
|
|
||||||
//
|
|
||||||
String[] names = {
|
|
||||||
"print"
|
|
||||||
,"load"
|
|
||||||
,"help"
|
|
||||||
,"getPlayerPos"
|
|
||||||
,"getMousePos"
|
|
||||||
,"putBlock"
|
|
||||||
,"getBlock"
|
|
||||||
,"putSign"
|
|
||||||
};
|
|
||||||
importer.defineFunctionProperties(names,
|
|
||||||
ScriptCraftEvaluator.class,
|
|
||||||
ScriptableObject.DONTENUM);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* So clients can add their own properties ...
|
|
||||||
*
|
|
||||||
* evaluator.getScope().defineProperty("jsVarName",javaObject);
|
|
||||||
*/
|
|
||||||
public ScriptableObject getScope()
|
|
||||||
{
|
|
||||||
return (ScriptableObject)this.scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object eval(String javascript, Object invoker)
|
|
||||||
{
|
|
||||||
ScriptCraftEvaluator.sc.setInvoker(invoker);
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators("js> " + javascript);
|
|
||||||
Object result = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = ctx.evaluateString(this.scope, javascript, "<cmd>", 1, null);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(System.err);
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators("Exception: " + e.getMessage());
|
|
||||||
}
|
|
||||||
if (result != null)
|
|
||||||
{
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators(Context.toString(result));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a javascript source file and evaluate its contents.
|
|
||||||
*/
|
|
||||||
public static Object load(Context cx, Scriptable thisObj, Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
Object result = null;
|
|
||||||
|
|
||||||
File scriptFile = null;
|
|
||||||
String filename = null;
|
|
||||||
|
|
||||||
if (args.length == 0)
|
|
||||||
{
|
|
||||||
JFileChooser fc = new javax.swing.JFileChooser();
|
|
||||||
int rc = fc.showOpenDialog(null);
|
|
||||||
if (rc ==JFileChooser.APPROVE_OPTION){
|
|
||||||
scriptFile = fc.getSelectedFile();
|
|
||||||
}else{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
scriptFile = new File((String)args[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileReader in = null;
|
|
||||||
try {
|
|
||||||
in = new FileReader(scriptFile);
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException ex)
|
|
||||||
{
|
|
||||||
ex.printStackTrace(System.err);
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators( "Error - File not found " + args[0]);
|
|
||||||
Context.reportError("Couldn't open file \"" + scriptFile + "\".");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
filename = scriptFile.getAbsolutePath();
|
|
||||||
System.out.println("ScripCraftEvaluator: filename=" + filename);
|
|
||||||
File parentFile = scriptFile.getParentFile();
|
|
||||||
String filedir = null;
|
|
||||||
if (parentFile !=null){
|
|
||||||
filedir = parentFile.getAbsolutePath();
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// setup the special script-context-only variables
|
|
||||||
//
|
|
||||||
((ScriptableObject)thisObj).defineProperty("$SCRIPT",filename,ScriptableObject.DONTENUM);
|
|
||||||
((ScriptableObject)thisObj).defineProperty("$SCRIPT_DIR",filedir==null?"":filedir,ScriptableObject.DONTENUM);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Here we evalute the entire contents of the file as
|
|
||||||
// a script. Text is printed only if the print() function
|
|
||||||
// is called.
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators( "Loading " + filename);
|
|
||||||
result = cx.evaluateReader(thisObj, in, filename, 1, null);
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators( "Successfully loaded " + filename);
|
|
||||||
}
|
|
||||||
catch (WrappedException we) {
|
|
||||||
we.printStackTrace(System.err);
|
|
||||||
String wes = we.getWrappedException().toString();
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators("WrappedException while loading " + filename + ": " + wes);
|
|
||||||
System.err.println(wes);
|
|
||||||
we.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (EvaluatorException ee) {
|
|
||||||
ee.printStackTrace(System.err);
|
|
||||||
System.err.println("js: " + ee.getMessage());
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators( "EvaluatorException while loading " + filename + ": " + ee.getMessage());
|
|
||||||
}
|
|
||||||
catch (JavaScriptException jse) {
|
|
||||||
jse.printStackTrace(System.err);
|
|
||||||
System.err.println("js: " + jse.getMessage());
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators("JavascriptException while loading " + filename + ": " + jse.getMessage());
|
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
ioe.printStackTrace(System.err);
|
|
||||||
System.err.println(ioe.toString());
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators( "IOException while loading " + filename + ": " + ioe.getMessage());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
System.err.println(ioe.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
public static void help(Context cx, Scriptable thisObj, Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
String cwd = java.lang.System.getProperty("user.dir");
|
|
||||||
String[] helpArgs = {"Current Working Directory: " + cwd,
|
|
||||||
"load('path-to-script.js')",
|
|
||||||
"load() (with no params) lets you choose a script file",
|
|
||||||
"getPlayerPos() returns player coords",
|
|
||||||
"getMousePos() returns mouse/crosshair coords",
|
|
||||||
"getBlock(x,y,z) returns the block and metadata e.g. '98' for a stone block or '98:2' for a mossy stone block",
|
|
||||||
"putBlock(x,y,z,blockId,meta) e.g. putBlock(100,2,50,44,2) puts a sandstone slab (44:2) at position 100,2,50. See http://www.minecraftinfo.com/idlist.htm for block ids"
|
|
||||||
};
|
|
||||||
print(cx,thisObj,helpArgs,funObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void print(Context cx, Scriptable thisObj,Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
for (int i=0; i < args.length; i++) {
|
|
||||||
if (i > 0){
|
|
||||||
System.out.print(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the arbitrary JavaScript value into a string form.
|
|
||||||
String s = Context.toString(args[i]);
|
|
||||||
ScriptCraftEvaluator.sc.notifyAdministrators(s);
|
|
||||||
System.out.print(s);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
public static double[] getPlayerPos(Context cx, Scriptable thisObj,Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
return ScriptCraftEvaluator.sc.getPlayerPos();
|
|
||||||
}
|
|
||||||
public static double[] getMousePos(Context cx, Scriptable thisObj,Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
return ScriptCraftEvaluator.sc.getMousePos();
|
|
||||||
}
|
|
||||||
public static void putBlock(Context cx, Scriptable thisObj,Object[] args, Function funObj)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int z;
|
|
||||||
int b;
|
|
||||||
int m;
|
|
||||||
|
|
||||||
if (args.length == 2){
|
|
||||||
double[] mousePos = ScriptCraftEvaluator.sc.getMousePos();
|
|
||||||
if (mousePos != null){
|
|
||||||
x = (int)mousePos[0];
|
|
||||||
y = (int)mousePos[1];
|
|
||||||
z = (int)mousePos[2];
|
|
||||||
b = new Double(args[0].toString()).intValue();
|
|
||||||
m = new Double(args[1].toString()).intValue();
|
|
||||||
}else{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
x = new Double(args[0].toString()).intValue();
|
|
||||||
y = new Double(args[1].toString()).intValue();
|
|
||||||
z = new Double(args[2].toString()).intValue();
|
|
||||||
b = new Double(args[3].toString()).intValue();
|
|
||||||
m = new Double(args[4].toString()).intValue();
|
|
||||||
}
|
|
||||||
ScriptCraftEvaluator.sc.putBlock(x,y,z,b,m);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// gets the blockId and metadata at the given coords
|
|
||||||
// if no coords are provided then the mouse position is used instead.
|
|
||||||
//
|
|
||||||
public static String getBlock(Context cx, Scriptable thisObj,Object[] args, Function funObj){
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int z;
|
|
||||||
|
|
||||||
if (args.length != 0){
|
|
||||||
x = new Double(args[0].toString()).intValue();
|
|
||||||
y = new Double(args[1].toString()).intValue();
|
|
||||||
z = new Double(args[2].toString()).intValue();
|
|
||||||
}else{
|
|
||||||
double[] mousePos = ScriptCraftEvaluator.sc.getMousePos();
|
|
||||||
if (mousePos != null){
|
|
||||||
x = (int)mousePos[0];
|
|
||||||
y = (int)mousePos[1];
|
|
||||||
z = (int)mousePos[2];
|
|
||||||
}else{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ScriptCraftEvaluator.sc.getBlock(x,y,z);
|
|
||||||
}
|
|
||||||
public static void putSign(Context cx, Scriptable thisObj,Object[] args, Function funObj){
|
|
||||||
List jsArray = (List)args[0];
|
|
||||||
|
|
||||||
String[] texts = new String[4];
|
|
||||||
for (int i = 0; i < jsArray.size() && i <= 3;i++){
|
|
||||||
texts[i] = (String)jsArray.get(i);
|
|
||||||
}
|
|
||||||
int x = new Double(args[1].toString()).intValue();
|
|
||||||
int y = new Double(args[2].toString()).intValue();
|
|
||||||
int z = new Double(args[3].toString()).intValue();
|
|
||||||
int b = new Double(args[4].toString()).intValue();
|
|
||||||
int m = new Double(args[5].toString()).intValue();
|
|
||||||
ScriptCraftEvaluator.sc.putSign(texts,x,y,z,b,m);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package net.minecraft.src;
|
|
||||||
import net.minecraft.client.*;
|
|
||||||
/**
|
|
||||||
* An implementation of the IScriptCraft interface for
|
|
||||||
* Minecraft Coder Pack-style install
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ScriptCraftMCP implements IScriptCraft
|
|
||||||
{
|
|
||||||
protected CommandBase command = null;
|
|
||||||
|
|
||||||
public ScriptCraftMCP(CommandBase mcpCommand){
|
|
||||||
this.command = mcpCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ICommandSender invoker;
|
|
||||||
//
|
|
||||||
// following code depends on MCP
|
|
||||||
//
|
|
||||||
public double[] getPlayerPos()
|
|
||||||
{
|
|
||||||
double[] result = new double[4];
|
|
||||||
if (this.invoker instanceof EntityPlayer){
|
|
||||||
EntityPlayer player = (EntityPlayer)this.invoker;
|
|
||||||
result[0] = player.posX;
|
|
||||||
result[1] = player.posY;
|
|
||||||
result[2] = player.posZ;
|
|
||||||
result[3] = player.rotationYaw;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public double[] getMousePos()
|
|
||||||
{
|
|
||||||
Minecraft mc = net.minecraft.client.Minecraft.getMinecraft();
|
|
||||||
MovingObjectPosition omo = mc.objectMouseOver;
|
|
||||||
if (omo == null){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
double[] result = new double[4];
|
|
||||||
result[0] = omo.blockX;
|
|
||||||
result[1] = omo.blockY;
|
|
||||||
result[2] = omo.blockZ;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
public void putSign(String[] texts, int x, int y, int z, int block, int meta)
|
|
||||||
{
|
|
||||||
this.putBlock(x,y,z,block,meta);
|
|
||||||
EntityPlayer player = (EntityPlayer)this.invoker;
|
|
||||||
World world = player.worldObj;
|
|
||||||
TileEntitySign sign = (TileEntitySign)world.getBlockTileEntity(x,y,z);
|
|
||||||
for (int i=0 ; i < texts.length && i <= 3;i++){
|
|
||||||
String text = texts[i];
|
|
||||||
if (text != null){
|
|
||||||
if (text.length() > 15){
|
|
||||||
text = text.substring(0,15);
|
|
||||||
}
|
|
||||||
sign.signText[i] = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sign.onInventoryChanged();
|
|
||||||
world.markBlockForUpdate(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putBlock(int x, int y, int z, int blockId, int meta)
|
|
||||||
{
|
|
||||||
World world = null;
|
|
||||||
if (this.invoker instanceof EntityPlayer)
|
|
||||||
{
|
|
||||||
world = ((EntityPlayer)(this.invoker)).worldObj;
|
|
||||||
}
|
|
||||||
else if (this.invoker instanceof TileEntity)
|
|
||||||
{
|
|
||||||
world = ((TileEntity)(this.invoker)).getWorldObj();
|
|
||||||
}
|
|
||||||
world.setBlockAndMetadata(x,y,z,blockId,meta);
|
|
||||||
|
|
||||||
switch (blockId)
|
|
||||||
{
|
|
||||||
case 6:
|
|
||||||
((BlockSapling)Block.sapling).growTree(world,x,y,z,world.rand);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// returns the block id and metadata at a given location in the world
|
|
||||||
// e.g. returns "98" for a stone block or "98:2" for a mossy stone block.
|
|
||||||
//
|
|
||||||
public String getBlock(int x, int y, int z)
|
|
||||||
{
|
|
||||||
EntityPlayer player = (EntityPlayer)this.invoker;
|
|
||||||
World world = player.worldObj;
|
|
||||||
int blockId = world.getBlockId(x,y,z);
|
|
||||||
int metadata = world.getBlockMetadata(x,y,z);
|
|
||||||
if (metadata !=0){
|
|
||||||
return "" + blockId + ":" + metadata;
|
|
||||||
}else{
|
|
||||||
return "" + blockId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void notifyAdministrators(String message)
|
|
||||||
{
|
|
||||||
this.command.notifyAdmins(this.invoker,message,(Object[])null);
|
|
||||||
}
|
|
||||||
public void setInvoker(Object invoker)
|
|
||||||
{
|
|
||||||
this.invoker = (ICommandSender)invoker;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
9a10
|
|
||||||
> this.registerCommand(new CommandScript());
|
|
|
@ -1,10 +0,0 @@
|
||||||
466a467,475
|
|
||||||
> //
|
|
||||||
> // wph 20121231 show the block id and metadata for in-focus block
|
|
||||||
> //
|
|
||||||
> MovingObjectPosition omo = this.mc.objectMouseOver;
|
|
||||||
> if (omo != null){
|
|
||||||
> int bi = this.mc.theWorld.getBlockId(omo.blockX,omo.blockY,omo.blockZ);
|
|
||||||
> int md = this.mc.theWorld.getBlockMetadata(omo.blockX,omo.blockY,omo.blockZ);
|
|
||||||
> this.drawString(var8,"Mouse:" + omo.blockX + ", " + omo.blockY + ", " + omo.blockZ + " block data value: " + bi + ":" + md,2,112,14737632);
|
|
||||||
> }
|
|
594
release-notes.md
Normal file
|
@ -0,0 +1,594 @@
|
||||||
|
RELEASE NOTES
|
||||||
|
=============
|
||||||
|
|
||||||
|
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<TAB> 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!');
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
----------
|
||||||
|
Added asynchronous `input()` function module.
|
||||||
|
|
||||||
|
Version 2.0.6 (2014 03 15)
|
||||||
|
--------------------------
|
||||||
|
## Simplified Event handling code.
|
||||||
|
|
||||||
|
The callback function for event handling now only takes 1 single
|
||||||
|
parameter, the event which triggered the callback. The listener object
|
||||||
|
is bound to the callback function so within the callback function
|
||||||
|
`this` refers to the listener object. Unregistering listeners has also
|
||||||
|
been greatly simplified. You can have an event handler which fires
|
||||||
|
only once by unregistering itself within the callback like this...
|
||||||
|
|
||||||
|
events.on('player.PlayerJoinEvent', function( event ) {
|
||||||
|
|
||||||
|
// do something
|
||||||
|
event.player.sendMessage( "You're the first player to join" );
|
||||||
|
|
||||||
|
// unregister so this function is called only once ever.
|
||||||
|
this.unregister();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
The `events.on()` function also returns the same listener object as
|
||||||
|
`this` refered to inside the callback. The listener object has a
|
||||||
|
single method `unregister()` which can be called to stop listening for
|
||||||
|
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.
|
||||||
|
|
||||||
|
# 2014 03 08
|
||||||
|
|
||||||
|
Fixed issues #115 #122 #123
|
||||||
|
|
||||||
|
Improved background processing of Drone build commands.
|
||||||
|
|
||||||
|
# 2014 02 19
|
||||||
|
|
||||||
|
## Version 2.0.5
|
||||||
|
|
||||||
|
Asynchronous building. Drone now builds asynchronously.
|
||||||
|
Fixed Issue #119 (exceptions on reload/stop)
|
||||||
|
|
||||||
|
# 2014 02 11
|
||||||
|
|
||||||
|
## Version 2.0.4
|
||||||
|
|
||||||
|
Various bug fixes, enhanced classroom module and improved error logging on startup.
|
||||||
|
|
||||||
|
# 2014 01 17
|
||||||
|
|
||||||
|
Added support for communication with Arduino and other devices which
|
||||||
|
use the [MQTT protocol][mqtt] via a new `sc-mqtt` module. This module
|
||||||
|
requires a separate sc-mqtt.jar file downloadable from
|
||||||
|
<http://scriptcraftjs.org/download/extras/mqtt> which must be included
|
||||||
|
in the CraftBukkit classpath. If using MQTT in ScriptCraft, then
|
||||||
|
Craftbukkit should be launched like this...
|
||||||
|
|
||||||
|
java -classpath craftbukkit.jar;sc-mqtt.jar org.bukkit.craftbukkit.Main
|
||||||
|
|
||||||
|
You can use the new `sc-mqtt` module like this to send and receive
|
||||||
|
messages between minecraft and an Arduino. For example to send a
|
||||||
|
message to the MQTT broker whenever a player breaks a block...
|
||||||
|
|
||||||
|
var mqtt = require('sc-mqtt');
|
||||||
|
var client = mqtt.client('tcp://localhost:1883','scripcraft');
|
||||||
|
client.connect();
|
||||||
|
|
||||||
|
events.on('block.BlockBreakEvent', function(listener, event){
|
||||||
|
client.publish('minecraft','block-broken');
|
||||||
|
});
|
||||||
|
|
||||||
|
To have minecraft react to inputs from an MQTT broker...
|
||||||
|
|
||||||
|
var mqtt = require('sc-mqtt');
|
||||||
|
var client = mqtt.client('tcp://localhost:1883','scripcraft');
|
||||||
|
client.connect();
|
||||||
|
client.onMessageArrived(function(topic, message){
|
||||||
|
var payload = message.payload;
|
||||||
|
if (topic == 'arduino'){
|
||||||
|
// do something with payload.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[mqtt]: http://mqtt.org/
|
||||||
|
# 2014 01 14
|
||||||
|
|
||||||
|
Added config.yml file for configuration options. 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.
|
||||||
|
The config.yml file looks like this...
|
||||||
|
|
||||||
|
extract-js:
|
||||||
|
plugins: true
|
||||||
|
modules: true
|
||||||
|
lib: true
|
||||||
|
|
||||||
|
# 2014 01 13
|
||||||
|
|
||||||
|
Bug Fix: Make ScriptCraft work with Nashorn javascript engine bundled with Java 8
|
||||||
|
<https://github.com/walterhiggins/ScriptCraft/issues/112>
|
||||||
|
|
||||||
|
# 2014 01 12
|
||||||
|
|
||||||
|
## Important
|
||||||
|
The ScriptCraft.jar file has been renamed scriptcraft.jar (see bug fix
|
||||||
|
details below). This means that you will have to remove the existing
|
||||||
|
`plugins/ScriptCraft.jar` file if present.
|
||||||
|
|
||||||
|
Bug Fix: On Mac OS, the plugins/scriptcraft directory is copied to
|
||||||
|
plugins/ScriptCraftPlugin the 2nd time ScriptCraftPlugin is loaded.
|
||||||
|
This has been fixed by changing the plugin name from ScriptCraftPlugin
|
||||||
|
to scriptcraft. The jar file has also been rename from
|
||||||
|
ScriptCraft.jar to scriptcraft.jar.
|
||||||
|
|
||||||
|
New command: `jsp spawn` lets in-game operators spawn any type of
|
||||||
|
entity. For example `/jsp spawn cow` will spawn a cow at the in-game
|
||||||
|
operator's current location.
|
||||||
|
|
||||||
|
New minigame: Cow Clicker. A simple demonstration of using Bukkit's
|
||||||
|
Scoreboard API. Players click cows to score points. Scores are
|
||||||
|
displayed in a side bar on screen. Players join or leave the game by
|
||||||
|
typing `/jsp cowclicker` at the in-game prompt.
|
||||||
|
|
||||||
|
# 2014 01 05
|
||||||
|
|
||||||
|
Bug Fix: On Mac OS, alias plugin caused Exceptions due to missing
|
||||||
|
.trim() function on String.
|
||||||
|
|
||||||
|
Changed target for compilation to 1.6 so that ScriptCraft will work
|
||||||
|
with both java 1.6 and 1.7.
|
||||||
|
|
||||||
|
Added documentation for the Signs module.
|
||||||
|
|
||||||
|
# 2014 01 02
|
||||||
|
|
||||||
|
Added a warning in console at start-up if legacy directories are detected.
|
||||||
|
Added 'use strict' to core modules.
|
||||||
|
Bug Fix; Signs were not being saved. (introduced with recent change to JSONifying Location)
|
||||||
|
|
||||||
|
# 2014 01 01
|
||||||
|
|
||||||
|
'Buddha' Release - towards a total annihilation of the 'self' variable.
|
||||||
|
The 'self' variable should only be used at the in-game or server
|
||||||
|
console command prompts and should not be used in modules or in
|
||||||
|
multi-threaded code.
|
||||||
|
|
||||||
|
Moved scriptcraft directory from {craftbukkit-root}/scriptcraft to
|
||||||
|
{craftbukkit-root}/plugins/scriptcraft because this seems to be where
|
||||||
|
other plugins create plugin-specific directories on the filesystem.
|
||||||
|
|
||||||
|
Documentation updates. Added new sections to the Young Persons Guide
|
||||||
|
to Modding Minecraft.
|
||||||
|
|
||||||
|
# 2013 12 30
|
||||||
|
|
||||||
|
Removing coffeescript support because coffeescript.js will not
|
||||||
|
compile/evaluate (generated bytecode exceeds 64K limit).
|
||||||
|
|
||||||
|
Updated plugin to remove `self` variable once the `js` command
|
||||||
|
evaluation has completed (limits the scope of the `self` variable to
|
||||||
|
just command-prompt evaluation).
|
||||||
|
|
||||||
|
# 2013 12 29
|
||||||
|
|
||||||
|
Bug Fix: [Can't get Scriptcraft core libraries working][bug103].
|
||||||
|
|
||||||
|
[bug103]: https://github.com/walterhiggins/ScriptCraft/issues/103
|
||||||
|
|
||||||
|
Bug Fix; Server console errors when empty commands submitted.
|
||||||
|
|
||||||
|
Added more example modules.
|
||||||
|
|
||||||
|
# 2013 12 28
|
||||||
|
|
||||||
|
Documented the 'homes' module other tweaks to documentation.
|
||||||
|
|
||||||
|
# 2013 12 27
|
||||||
|
|
||||||
|
## Updated 'jsp alias' command.
|
||||||
|
|
||||||
|
The 'jsp alias' command now lets players define their own shortcuts
|
||||||
|
which don't require the 'jsp ' prefix.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
At the in-game prompt use the following command to create a new alias
|
||||||
|
`cw` (short for change Clock & Weather) which will change the time and
|
||||||
|
weather using a single statement.
|
||||||
|
|
||||||
|
/jsp alias set cw = time set {1} ; weather {2}
|
||||||
|
|
||||||
|
This creates a new cw alias which takes 2 parameters, time and weather
|
||||||
|
and passes them to the 'time set' and 'weather' commands. You use the
|
||||||
|
alias like this...
|
||||||
|
|
||||||
|
/cw 4000 sun
|
||||||
|
|
||||||
|
... which in turn gets converted into these 2 commands...
|
||||||
|
|
||||||
|
/time set 4000
|
||||||
|
/weather sun
|
||||||
|
|
||||||
|
Aliases can be set on a per-player basis or can be set globally (for
|
||||||
|
all players). Aliases are automatically saved and restore on server
|
||||||
|
shutdown/startup.
|
||||||
|
|
||||||
|
## Added console global variable.
|
||||||
|
|
||||||
|
ScriptCraft now has a `console` global variable which can be used for
|
||||||
|
logging (to the server console). The `console` variable uses the
|
||||||
|
ScriptCraft plugin Logger object. You use the console object in your
|
||||||
|
javascript modules like this...
|
||||||
|
|
||||||
|
console.info('Hello %s, %s', 'World', 'Universe');
|
||||||
|
|
||||||
|
... or simply...
|
||||||
|
|
||||||
|
console.warn('Hello World');
|
||||||
|
|
||||||
|
# 2013 12 26
|
||||||
|
|
||||||
|
Made the `events` variable global because it is use by modules and
|
||||||
|
plugins. This means there is no longer any need to explicitly
|
||||||
|
`require('events')` since `events` is now a free variable in the
|
||||||
|
global namespace.
|
||||||
|
|
||||||
|
# 2013 12 25
|
||||||
|
|
||||||
|
Added the 'commando' module.
|
||||||
|
|
||||||
|
# 2013 12 24
|
||||||
|
|
||||||
|
## 'Modules' release
|
||||||
|
|
||||||
|
### Modules in ScriptCraft
|
||||||
|
|
||||||
|
ScriptCraft now has a simple module loading system. ScriptCraft now
|
||||||
|
uses the [CommonJS module contract][cjsmod] - that is - the same
|
||||||
|
module system used by Node.js. All of the javascript code which comes
|
||||||
|
bundled with ScriptCraft has been modified so that it conforms to the
|
||||||
|
CommonJS module system.
|
||||||
|
|
||||||
|
### What this means for plugins you've developed using ScriptCraft
|
||||||
|
|
||||||
|
If you have written plugins using a previous version of ScriptCraft then you have 2 options...
|
||||||
|
|
||||||
|
1. Continue using the previous version of ScriptCraft.
|
||||||
|
2. Update your plugins to work with the ScriptCraft 'Modules' release.
|
||||||
|
|
||||||
|
... Option 2 should be relatively straightforward if you follow these steps...
|
||||||
|
|
||||||
|
1. Copy your own custom plugins from the `js-plugins` directory to the new `scriptcraft/plugins` directory.
|
||||||
|
2. In your javascript code any functions, objects or variables which
|
||||||
|
you want to expose for use by others should be exposed using the
|
||||||
|
special `exports` variable. All other code within your .js files will
|
||||||
|
now be private by default. See below for details on how
|
||||||
|
CommonJS/Node.js modules work.
|
||||||
|
|
||||||
|
If you have any questions or concerns or need help converting your
|
||||||
|
existing javascript plugin, contact please post questions on the
|
||||||
|
[ScriptCraft forum][scforum] or open an issue on the [Github
|
||||||
|
project][github]
|
||||||
|
|
||||||
|
[github]: http://github.com/walterhiggins/ScriptCraft
|
||||||
|
[scforum]: https://groups.google.com/forum/?fromgroups=#!forum/scriptcraft---scripting-minecraft
|
||||||
|
|
||||||
|
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
|
||||||
|
[cjsmod]: http://wiki.commonjs.org/wiki/Modules/1.1.1
|
||||||
|
|
||||||
|
The contents of foo.js:
|
||||||
|
|
||||||
|
var circle = require('./circle.js');
|
||||||
|
echo( 'The area of a circle of radius 4 is '
|
||||||
|
+ circle.area(4));
|
||||||
|
|
||||||
|
The contents of circle.js:
|
||||||
|
|
||||||
|
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
|
||||||
|
subdirectory is created in the craftbukkit directory. If your
|
||||||
|
craftbukkit directory is called 'craftbukkit' then the new
|
||||||
|
subdirectories will be ...
|
||||||
|
|
||||||
|
* craftbukkit/scriptcraft/
|
||||||
|
* craftbukkit/scriptcraft/plugins
|
||||||
|
* craftbukkit/scriptcraft/modules
|
||||||
|
* craftbukkit/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....
|
||||||
|
|
||||||
|
exports.greet = function() {
|
||||||
|
echo('Hello ' + self.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
... then `greet` becomes a global function and can be used at the
|
||||||
|
in-game (or server) command prompt like so...
|
||||||
|
|
||||||
|
/js greet()
|
||||||
|
|
||||||
|
... 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.
|
|
@ -1,5 +1,6 @@
|
||||||
import javax.script.*;
|
import javax.script.*;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
|
import net.canarymod.api.inventory.ItemType;
|
||||||
|
|
||||||
public class jscript
|
public class jscript
|
||||||
{
|
{
|
||||||
|
@ -10,6 +11,7 @@ public class jscript
|
||||||
java.io.File file = new java.io.File(args[0]);
|
java.io.File file = new java.io.File(args[0]);
|
||||||
engine.put("engine",engine);
|
engine.put("engine",engine);
|
||||||
engine.put("args",args);
|
engine.put("args",args);
|
||||||
|
engine.put("cmItemTypeClass",ItemType.class);
|
||||||
FileReader fr = new java.io.FileReader(file);
|
FileReader fr = new java.io.FileReader(file);
|
||||||
engine.eval(fr);
|
engine.eval(fr);
|
||||||
fr.close();
|
fr.close();
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
This script is run at build time to generate api.md - a single Markdown document containing documentation for ScriptCraft's API
|
|
||||||
*/
|
|
||||||
args = args.slice(1);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
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))
|
|
||||||
return -1;
|
|
||||||
if (b.match(re))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if(adir<bdir) return -1;
|
|
||||||
if(adir>bdir) return 1;
|
|
||||||
afile = afile.replace(/\.js$/,"");
|
|
||||||
if (afile == adir)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var sortByModule = function(a,b)
|
|
||||||
{
|
|
||||||
var aparts = (""+a).split(/\//);
|
|
||||||
var bparts = (""+b).split(/\//);
|
|
||||||
var adir = aparts[aparts.length-2];
|
|
||||||
var afile = aparts[aparts.length-1];
|
|
||||||
var bdir = bparts[bparts.length-2];
|
|
||||||
var bfile = bparts[bparts.length-1];
|
|
||||||
if (afile == "_scriptcraft.js")
|
|
||||||
return -1;
|
|
||||||
if (bfile == "_scriptcraft.js")
|
|
||||||
return 1;
|
|
||||||
if(adir<bdir) return -1;
|
|
||||||
if(adir>bdir) return 1;
|
|
||||||
if (afile.indexOf(adir) == 0)
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
var store = [];
|
|
||||||
find(new File(dir),store,/\/[a-zA-Z0-9_\-]+\.js$/);
|
|
||||||
|
|
||||||
store.sort(sorter([
|
|
||||||
/_scriptcraft\.js$/,
|
|
||||||
/core/,
|
|
||||||
/drone\.js/,
|
|
||||||
/drone/
|
|
||||||
]));
|
|
||||||
|
|
||||||
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 len = contents.length;
|
|
||||||
var writeComment = false;
|
|
||||||
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){
|
|
||||||
println(contents[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
136
src/docs/js/generateApiDocs.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
function foreach(array, func){
|
||||||
|
for (var i =0; i < array.length; i++){
|
||||||
|
func(array[i],i,array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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.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(adir<bdir) return -1;
|
||||||
|
if(adir>bdir) 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 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\/events\-helper\-canary/,
|
||||||
|
/lib\/events\-helper\-bukkit/,
|
||||||
|
/lib\//,
|
||||||
|
/modules\/drone\/index\.js/,
|
||||||
|
/modules\/drone\//,
|
||||||
|
/plugins\/drone\//,
|
||||||
|
/modules\//,
|
||||||
|
/examples\//
|
||||||
|
]));
|
||||||
|
|
||||||
|
var contents = [];
|
||||||
|
foreach(store, function(filename){
|
||||||
|
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;
|
||||||
|
var writeComment = false;
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
46
src/docs/js/generateEntitiesDoc.js
Normal file
|
@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
103
src/docs/js/generateEventsHelper.js
Normal file
|
@ -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('};');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
77
src/docs/js/generateItemsDoc.js
Normal file
|
@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
50
src/docs/js/generateTOC.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
args = Array.prototype.slice.call(args,1);
|
||||||
|
|
||||||
|
// wph 20140105 trim not availabe in String on Mac OS.
|
||||||
|
if (typeof String.prototype.trim == 'undefined'){
|
||||||
|
String.prototype.trim = function(){
|
||||||
|
return this.replace(/^\s+|\s+$/g,'');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var template = args[0];
|
||||||
|
|
||||||
|
var BufferedReader = java.io.BufferedReader;
|
||||||
|
var FileReader = java.io.FileReader;
|
||||||
|
|
||||||
|
var contents = [], line = undefined;
|
||||||
|
var br = new BufferedReader( new FileReader(template) );
|
||||||
|
|
||||||
|
while ( (line = br.readLine()) != null){
|
||||||
|
contents.push(line);
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
|
||||||
|
var anchors = {};
|
||||||
|
|
||||||
|
var createLink = function(text){
|
||||||
|
var result = text.replace(/_/g,'_');
|
||||||
|
result = result.replace(/[^a-zA-Z0-9 _\-]/g,'');
|
||||||
|
result = result.replace(/ /g,'-');
|
||||||
|
var result = result.toLowerCase();
|
||||||
|
if (anchors[result]){
|
||||||
|
result = result + '-' + (anchors[result]++);
|
||||||
|
}
|
||||||
|
anchors[result] = 1;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
java.lang.System.out.println('## Table of Contents');
|
||||||
|
|
||||||
|
for (var i = 0; i < contents.length; i++){
|
||||||
|
line = contents[i];
|
||||||
|
if (line.match(/^##\s+/)){
|
||||||
|
var h2 = line.match(/^##\s+(.*)/)[1].trim();
|
||||||
|
var link = createLink(h2);
|
||||||
|
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 + ')');
|
||||||
|
}
|
||||||
|
}
|
1276
src/docs/templates/ypgpm.md
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
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 java.util.ArrayList;
|
||||||
|
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<CommandSender,ScriptCraftEvaluator> playerContexts = new HashMap<CommandSender,ScriptCraftEvaluator>();
|
||||||
|
private String NO_JAVASCRIPT_MESSAGE = "No JavaScript Engine available. ScriptCraft will not work without Javascript.";
|
||||||
|
protected ScriptEngine engine = null;
|
||||||
|
|
||||||
|
@Override public void onEnable()
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
} finally {
|
||||||
|
currentThread.setContextClassLoader(previousClassLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> onTabComplete(CommandSender sender, Command cmd,
|
||||||
|
String alias,
|
||||||
|
String[] args)
|
||||||
|
{
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
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) {
|
||||||
|
sender.sendMessage(e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
|
||||||
|
{
|
||||||
|
boolean result = false;
|
||||||
|
Object jsResult = null;
|
||||||
|
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) {
|
||||||
|
this.getLogger().severe(se.toString());
|
||||||
|
se.printStackTrace();
|
||||||
|
sender.sendMessage(se.getMessage());
|
||||||
|
}
|
||||||
|
if (jsResult != null){
|
||||||
|
return ((Boolean)jsResult).booleanValue();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String> complete(MessageReceiver sender, String[] args, String cmd){
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
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<String> jsComplete(MessageReceiver sender, String[] args){
|
||||||
|
return complete(sender, args, "js");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TabComplete (commands = { "jsp" })
|
||||||
|
public List<String> jspComplete(MessageReceiver sender, String[] args){
|
||||||
|
return complete(sender, args, "jsp");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,185 +0,0 @@
|
||||||
package net.walterhiggins.scriptcraft;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import javax.script.*;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.bukkit.command.*;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
|
|
||||||
public class ScriptCraftPlugin extends JavaPlugin implements Listener
|
|
||||||
{
|
|
||||||
// right now all ops share the same JS context/scope
|
|
||||||
// need to look at possibly having context/scope per operator
|
|
||||||
//protected Map<CommandSender,ScriptCraftEvaluator> playerContexts = new HashMap<CommandSender,ScriptCraftEvaluator>();
|
|
||||||
protected ScriptEngine engine = null;
|
|
||||||
private static final String JS_PLUGINS_DIR = "js-plugins";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unzips bundled javascript code.
|
|
||||||
*/
|
|
||||||
private void unzipJS()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// does the js-plugins directory exist?
|
|
||||||
//
|
|
||||||
File jsPlugins = new File(JS_PLUGINS_DIR);
|
|
||||||
if (!jsPlugins.exists())
|
|
||||||
{
|
|
||||||
getLogger().finest("Directory " + JS_PLUGINS_DIR + " does not exist.");
|
|
||||||
getLogger().finest("Initializing " + JS_PLUGINS_DIR + " directory with contents from plugin archive.");
|
|
||||||
jsPlugins.mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
ZipInputStream zis = new ZipInputStream(getResource(JS_PLUGINS_DIR + ".zip"));
|
|
||||||
ZipEntry entry;
|
|
||||||
try {
|
|
||||||
while ( ( entry = zis.getNextEntry() ) != null)
|
|
||||||
{
|
|
||||||
String filename = entry.getName();
|
|
||||||
File newFile = new File(jsPlugins.getName() + File.separator + filename);
|
|
||||||
|
|
||||||
//create all non exists folders
|
|
||||||
//else you will hit FileNotFoundException for compressed folder
|
|
||||||
if (entry.isDirectory()){
|
|
||||||
newFile.mkdirs();
|
|
||||||
}else{
|
|
||||||
//
|
|
||||||
// only write out to file if zip entry is newer than file
|
|
||||||
//
|
|
||||||
long zTime = entry.getTime();
|
|
||||||
boolean unzip = false;
|
|
||||||
if (!newFile.exists())
|
|
||||||
unzip = true;
|
|
||||||
else{
|
|
||||||
long fTime = newFile.lastModified();
|
|
||||||
if (zTime > fTime)
|
|
||||||
unzip = true;
|
|
||||||
}
|
|
||||||
if (unzip){
|
|
||||||
getLogger().info("Unzipping " + filename);
|
|
||||||
FileOutputStream fout = new FileOutputStream(newFile);
|
|
||||||
for (int c = zis.read(); c != -1; c = zis.read()) {
|
|
||||||
fout.write(c);
|
|
||||||
}
|
|
||||||
fout.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
zis.closeEntry();
|
|
||||||
}
|
|
||||||
zis.close();
|
|
||||||
}catch (IOException ioe){
|
|
||||||
getLogger().warning(ioe.getMessage());
|
|
||||||
ioe.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable()
|
|
||||||
{
|
|
||||||
unzipJS();
|
|
||||||
FileReader reader = null;
|
|
||||||
try{
|
|
||||||
ScriptEngineManager factory = new ScriptEngineManager();
|
|
||||||
File boot = new File(JS_PLUGINS_DIR + "/core/_scriptcraft.js");
|
|
||||||
this.engine = factory.getEngineByName("JavaScript");
|
|
||||||
this.engine.put("__engine",engine);
|
|
||||||
this.engine.put("__plugin",this);
|
|
||||||
this.engine.put("__script",boot.getCanonicalPath().replaceAll("\\\\","/"));
|
|
||||||
reader = new FileReader(boot);
|
|
||||||
this.engine.eval(reader);
|
|
||||||
/*
|
|
||||||
wph 20130811 Need to disable coffeescript support until issues loading and evaluating it are resolved.
|
|
||||||
See issue #92
|
|
||||||
// Load the CoffeeScript compiler
|
|
||||||
File coffeescript = new File(JS_PLUGINS_DIR + "/core/_coffeescript.js");
|
|
||||||
this.engine.eval(new FileReader(coffeescript));
|
|
||||||
*/
|
|
||||||
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}finally {
|
|
||||||
if (reader != null){
|
|
||||||
try {
|
|
||||||
reader.close();
|
|
||||||
}catch(IOException ioe){
|
|
||||||
// fail silently
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public List<String> onTabComplete(CommandSender sender, Command cmd,
|
|
||||||
String alias,
|
|
||||||
String[] args)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// delegate to javascript
|
|
||||||
//
|
|
||||||
List<String> result = new ArrayList<String>();
|
|
||||||
try {
|
|
||||||
this.engine.put("__onTC_result",result);
|
|
||||||
this.engine.put("__onTC_sender",sender);
|
|
||||||
this.engine.put("__onTC_cmd",cmd);
|
|
||||||
this.engine.put("__onTC_alias",alias);
|
|
||||||
this.engine.put("__onTC_args",args);
|
|
||||||
this.engine.eval("_onTabComplete()");
|
|
||||||
}catch (Exception e){
|
|
||||||
sender.sendMessage(e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
|
|
||||||
{
|
|
||||||
boolean result = false;
|
|
||||||
String javascriptCode = "";
|
|
||||||
|
|
||||||
if(cmd.getName().equalsIgnoreCase("js")){
|
|
||||||
for (int i = 0;i < args.length; i++){
|
|
||||||
javascriptCode = javascriptCode + args[i] + " ";
|
|
||||||
}
|
|
||||||
result = true;
|
|
||||||
} else if (cmd.getName().equalsIgnoreCase("jsp")){
|
|
||||||
javascriptCode = "command()";
|
|
||||||
this.engine.put("__cmdArgs",args);
|
|
||||||
result = true;
|
|
||||||
} else if (cmd.getName().equalsIgnoreCase("coffee")) {
|
|
||||||
for (int i = 0;i < args.length; i++)
|
|
||||||
javascriptCode += args[i] + " ";
|
|
||||||
javascriptCode = "eval(CoffeeScript.compile(\""+javascriptCode+"\", {bare: true}))";
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result){
|
|
||||||
this.engine.put("self",sender);
|
|
||||||
try{
|
|
||||||
Object resultObj = this.engine.eval(javascriptCode);
|
|
||||||
if (resultObj != null){
|
|
||||||
if (resultObj instanceof java.util.Collection){
|
|
||||||
java.util.Collection collection = (java.util.Collection)resultObj;
|
|
||||||
sender.sendMessage(Arrays.toString(collection.toArray()));
|
|
||||||
}else{
|
|
||||||
sender.sendMessage(resultObj.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch (Exception e){
|
|
||||||
sender.sendMessage(e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
plugin("alias", {
|
|
||||||
help: function(){
|
|
||||||
return [
|
|
||||||
"/jsp alias set <alias> <commands> : Set a shortcut/alias for one or more commands (separated by ';')\n" +
|
|
||||||
"For example: '/jsp alias set sunny time set 4000; weather clear'\n" +
|
|
||||||
"/jsp sunny (is the same as..\n/time set 4000\n/weather clear",
|
|
||||||
"/jsp alias delete <alias> : Removes a shortcut/alias",
|
|
||||||
"/jsp alias list : shows a list of the player's command aliases",
|
|
||||||
"/jsp alias help : Shows this message"
|
|
||||||
];
|
|
||||||
},
|
|
||||||
set: function(player, alias, commands){
|
|
||||||
var aliases = this.store.players;
|
|
||||||
var name = player.name;
|
|
||||||
aliases[name] = aliases[name] || {};
|
|
||||||
aliases[name][alias] = commands;
|
|
||||||
},
|
|
||||||
remove: function(player, alias){
|
|
||||||
var aliases = this.store.players;
|
|
||||||
if (aliases[player.name])
|
|
||||||
delete aliases[player.name][alias];
|
|
||||||
},
|
|
||||||
list: function(player){
|
|
||||||
var result = [];
|
|
||||||
var aliases = this.store.players[player.name];
|
|
||||||
for (var a in aliases)
|
|
||||||
result.push(a + " = " + aliases[a].join(";"));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
},true);
|
|
||||||
|
|
||||||
alias.store.players = alias.store.players || {};
|
|
||||||
|
|
||||||
command("alias",function(params){
|
|
||||||
/*
|
|
||||||
this function also intercepts command options for /jsp
|
|
||||||
*/
|
|
||||||
if (params[0] === "help"){
|
|
||||||
self.sendMessage(alias.help());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (params[0] === "set"){
|
|
||||||
var aliasCmd = params[1];
|
|
||||||
var cmdStr = params.slice(2).join(' ');
|
|
||||||
var cmds = cmdStr.split(';');
|
|
||||||
alias.set(self,aliasCmd,cmds);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (params[0] === "delete"){
|
|
||||||
alias.remove(self,params[1]);
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
if (params[0] === "list"){
|
|
||||||
self.sendMessage(alias.list(self));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (params.length == 0)
|
|
||||||
return self.sendMessage(alias.help());
|
|
||||||
|
|
||||||
var playerHasAliases = alias.store.players[self.name];
|
|
||||||
if (!playerHasAliases)
|
|
||||||
return false;
|
|
||||||
// is it an alias?
|
|
||||||
var commands = playerHasAliases[params[0]];
|
|
||||||
if (!commands)
|
|
||||||
return false;
|
|
||||||
for (var i = 0;i < commands.length; i++){
|
|
||||||
// fill in template
|
|
||||||
var cmd = commands[i];
|
|
||||||
cmd = cmd.replace(/{([0-9]*)}/g,function(dummy,index){ return params[index] || "";})
|
|
||||||
self.performCommand(cmd);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
},["help","set","delete","list"],true);
|
|
|
@ -1,138 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
The arrows mod adds fancy arrows to the game.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
/js arrows.sign() turns a targeted sign into a Arrows menu
|
|
||||||
/js arrows.normal() sets arrow type to normal.
|
|
||||||
/js arrows.explosive() - makes arrows explode.
|
|
||||||
/js arrows.teleport() - makes player teleport to where arrow has landed.
|
|
||||||
/js arrows.flourish() - makes a tree grow where the arrow lands.
|
|
||||||
/js arrows.lightning() - lightning strikes where the arrow lands.
|
|
||||||
/js arrows.firework() - A firework launches where the the arrow lands.
|
|
||||||
|
|
||||||
All of the above functions can take an optional player object or name as
|
|
||||||
a parameter. E.g.
|
|
||||||
|
|
||||||
/js arrows.explosive('player23') makes player23's arrows explosive.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
var arrows = arrows || plugin("arrows",{
|
|
||||||
/*
|
|
||||||
turn a sign into a menu of arrow choices
|
|
||||||
*/
|
|
||||||
sign: function(sign){},
|
|
||||||
/*
|
|
||||||
change player's arrows to normal
|
|
||||||
*/
|
|
||||||
normal: function(player){},
|
|
||||||
/*
|
|
||||||
change player's arrows to explode on impact
|
|
||||||
*/
|
|
||||||
explosive: function(player){},
|
|
||||||
/*
|
|
||||||
change player's arrows to teleporting
|
|
||||||
*/
|
|
||||||
teleport: function(player){},
|
|
||||||
/*
|
|
||||||
change player's arrows to plant trees where they land
|
|
||||||
*/
|
|
||||||
flourish: function(player){},
|
|
||||||
/*
|
|
||||||
change player's arrows to strike lightning where they land
|
|
||||||
*/
|
|
||||||
lightning: function(player){},
|
|
||||||
|
|
||||||
/*
|
|
||||||
launch a firework where the arrow lands
|
|
||||||
*/
|
|
||||||
explosiveYield: 2.5
|
|
||||||
|
|
||||||
},true);
|
|
||||||
/*
|
|
||||||
initialize data
|
|
||||||
*/
|
|
||||||
arrows.store.players = arrows.store.players || {};
|
|
||||||
|
|
||||||
/*
|
|
||||||
private implementation of normal, explosive, teleport, flourish and lightning functions
|
|
||||||
*/
|
|
||||||
(function(){
|
|
||||||
//
|
|
||||||
// setup functions for the arrow types
|
|
||||||
//
|
|
||||||
var _types = {normal: 0, explosive: 1, teleport: 2, flourish: 3, lightning: 4, firework: 5};
|
|
||||||
for (var type in _types)
|
|
||||||
{
|
|
||||||
arrows[type] = (function(n){
|
|
||||||
return function(player){
|
|
||||||
if (typeof player == "undefined")
|
|
||||||
player = self;
|
|
||||||
var playerName = null;
|
|
||||||
if (typeof player == "string")
|
|
||||||
playerName = player;
|
|
||||||
else
|
|
||||||
playerName = player.name;
|
|
||||||
arrows.store.players[playerName] = n;
|
|
||||||
};
|
|
||||||
})(_types[type]);
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
/*
|
|
||||||
Arrows depends on 2 other modules: 'signs' and 'events' so the following code
|
|
||||||
can't execute until all modules have loaded (ready).
|
|
||||||
*/
|
|
||||||
ready(function()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
called when the player chooses an arrow option from a menu sign
|
|
||||||
*/
|
|
||||||
var _onMenuChoice = function(event){
|
|
||||||
arrows.store.players[event.player.name] = event.number;
|
|
||||||
};
|
|
||||||
arrows.sign = signs.menu("Arrow",
|
|
||||||
["Normal","Explosive","Teleport","Flourish","Lightning","Firework"],
|
|
||||||
_onMenuChoice );
|
|
||||||
|
|
||||||
/*
|
|
||||||
event handler called when a projectile hits something
|
|
||||||
*/
|
|
||||||
var _onArrowHit = function(listener,event)
|
|
||||||
{
|
|
||||||
var projectile = event.entity;
|
|
||||||
var world = projectile.world;
|
|
||||||
var shooter = projectile.shooter;
|
|
||||||
if (projectile instanceof org.bukkit.entity.Arrow &&
|
|
||||||
shooter instanceof org.bukkit.entity.Player)
|
|
||||||
{
|
|
||||||
var arrowType = arrows.store.players[shooter.name];
|
|
||||||
switch (arrowType){
|
|
||||||
case 1:
|
|
||||||
projectile.remove();
|
|
||||||
world.createExplosion(projectile.location,arrows.explosiveYield);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
projectile.remove();
|
|
||||||
var teleportCause =org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
shooter.teleport(projectile.location,
|
|
||||||
teleportCause.PLUGIN);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
projectile.remove();
|
|
||||||
world.generateTree(projectile.location, org.bukkit.TreeType.BIG_TREE);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
projectile.remove();
|
|
||||||
world.strikeLightning(projectile.location);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
projectile.remove();
|
|
||||||
fireworks.firework(projectile.location);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
events.on("entity.ProjectileHitEvent",_onArrowHit);
|
|
||||||
});
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
declare a new javascript plugin for changing chat text color
|
|
||||||
*/
|
|
||||||
var chat = chat || plugin("chat", {
|
|
||||||
/*
|
|
||||||
set the color of text for a given player
|
|
||||||
*/
|
|
||||||
setColor: function(player, color){
|
|
||||||
this.store.players[player.name] = color;
|
|
||||||
}
|
|
||||||
},true);
|
|
||||||
/*
|
|
||||||
initialize the store
|
|
||||||
*/
|
|
||||||
chat.store.players = chat.store.players || {};
|
|
||||||
|
|
||||||
ready(function()
|
|
||||||
{
|
|
||||||
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++) {
|
|
||||||
var hexCode = i.toString(16);
|
|
||||||
colorCodes[colors[i]] = hexCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on("player.AsyncPlayerChatEvent",function(l,e){
|
|
||||||
var player = e.player;
|
|
||||||
var playerChatColor = chat.store.players[player.name];
|
|
||||||
if (playerChatColor){
|
|
||||||
e.message = "§" + colorCodes[playerChatColor] + e.message;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var listColors = function(params){
|
|
||||||
var colorNamesInColor = [];
|
|
||||||
for (var i = 0;i < colors.length;i++)
|
|
||||||
colorNamesInColor[i] = "§"+colorCodes[colors[i]] + colors[i];
|
|
||||||
self.sendMessage("valid chat colors are " + colorNamesInColor.join(", "));
|
|
||||||
};
|
|
||||||
command("list_colors", listColors);
|
|
||||||
command("chat_color",function(params){
|
|
||||||
var color = params[0];
|
|
||||||
if (colorCodes[color]){
|
|
||||||
chat.setColor(self,color);
|
|
||||||
}else{
|
|
||||||
self.sendMessage(color + " is not a valid color");
|
|
||||||
listColors();
|
|
||||||
}
|
|
||||||
},colors);
|
|
||||||
});
|
|
|
@ -1,78 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Classroom Module
|
|
||||||
================
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
To disallow scripting (and prevent players who join the server from using the commands)...
|
|
||||||
|
|
||||||
/js classroom.allowScripting(false)
|
|
||||||
|
|
||||||
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 classroom = {
|
|
||||||
allowScripting: function(/* boolean: true or false */ canScript){}
|
|
||||||
};
|
|
||||||
|
|
||||||
ready(function(){
|
|
||||||
classroom.allowScripting = function(canScript)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
only operators should be allowed run this function
|
|
||||||
*/
|
|
||||||
if (!self.isOp())
|
|
||||||
return;
|
|
||||||
if (canScript){
|
|
||||||
utils.foreach( server.onlinePlayers, function (player) {
|
|
||||||
player.addAttachment(__plugin, "scriptcraft.*", true);
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
utils.foreach( server.onlinePlayers, function(player) {
|
|
||||||
utils.foreach(player.getEffectivePermissions(), function(perm) {
|
|
||||||
if ((""+perm.permission).indexOf("scriptcraft.") == 0){
|
|
||||||
if (perm.attachment)
|
|
||||||
perm.attachment.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
classroom.canScript = canScript;
|
|
||||||
};
|
|
||||||
events.on("player.PlayerLoginEvent", function(listener, event) {
|
|
||||||
var player = event.player;
|
|
||||||
if (classroom.canScript){
|
|
||||||
player.addAttachment(__plugin, "scriptcraft.*", true);
|
|
||||||
}
|
|
||||||
}, "HIGHEST");
|
|
||||||
});
|
|
|
@ -1,135 +0,0 @@
|
||||||
var global = this;
|
|
||||||
//
|
|
||||||
// Define these primitive methods used by drone.js (and potentially others)
|
|
||||||
//
|
|
||||||
// getPlayerPos
|
|
||||||
// returns the player's x,y,z and yaw (direction)
|
|
||||||
//
|
|
||||||
// getMousePos
|
|
||||||
// returns the x,y,z of the current block being targeted.
|
|
||||||
//
|
|
||||||
// putBlock(x,y,z,blockId,metadata)
|
|
||||||
// puts a block at a location in current world
|
|
||||||
//
|
|
||||||
// getBlock(x,y,z)
|
|
||||||
// gets the block and metadata (returned as a string in form '35:15')
|
|
||||||
//
|
|
||||||
// putSign(texts,x,y,z,blockId,metadata)
|
|
||||||
// puts a sign at the given location
|
|
||||||
//
|
|
||||||
// notifyAdministrators(msg)
|
|
||||||
// sends a message to all admins/ops.
|
|
||||||
//
|
|
||||||
// echo(msg)
|
|
||||||
// prints a message on screen to current user.
|
|
||||||
//
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
//
|
|
||||||
// only execute once
|
|
||||||
//
|
|
||||||
if (typeof getPlayerPos != "undefined"){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var _getPlayerPos = function(){
|
|
||||||
if (typeof self == "undefined")
|
|
||||||
return;
|
|
||||||
return self.location;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getMousePos = function(){
|
|
||||||
if (typeof self == "undefined")
|
|
||||||
return;
|
|
||||||
// self might be CONSOLE or a CommandBlock
|
|
||||||
if (!self.getTargetBlock)
|
|
||||||
return;
|
|
||||||
var targetedBlock = self.getTargetBlock(null,5);
|
|
||||||
if (targetedBlock == null || targetedBlock.isEmpty()){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return targetedBlock.location;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _putBlock = function(x,y,z,blockId,metadata){
|
|
||||||
|
|
||||||
if (typeof metadata == "undefined")
|
|
||||||
metadata = 0;
|
|
||||||
var pl = org.bukkit.entity.Player;
|
|
||||||
var cs = org.bukkit.command.BlockCommandSender;
|
|
||||||
var world = (self instanceof pl)?self.location.world:(self instanceof cs)?self.block.location.world:null;
|
|
||||||
var block = world.getBlockAt(x,y,z);
|
|
||||||
if (block.typeId != blockId || block.data != metadata)
|
|
||||||
block.setTypeIdAndData(blockId,metadata,false);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var _putSign = function(texts, x, y, z, blockId, meta){
|
|
||||||
if (blockId != 63 && blockId != 68)
|
|
||||||
throw new Error("Invalid Parameter: blockId must be 63 or 68");
|
|
||||||
putBlock(x,y,z,blockId,meta);
|
|
||||||
var block = _getBlockObject(x,y,z);
|
|
||||||
var state = block.state;
|
|
||||||
if (state instanceof org.bukkit.block.Sign){
|
|
||||||
for (var i = 0;i < texts.length; i++)
|
|
||||||
state.setLine(i%4,texts[i]);
|
|
||||||
state.update(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getBlock = function(x,y,z){
|
|
||||||
var block = _getBlockObject(x,y,z);
|
|
||||||
if (block)
|
|
||||||
return "" + block.typeId + ":" + block.data;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getBlockObject = function(x,y,z){
|
|
||||||
var world = _getWorld();
|
|
||||||
if (world){
|
|
||||||
if (typeof z == "undefined"){
|
|
||||||
var loc = _getMousePos()
|
|
||||||
if (loc)
|
|
||||||
return world.getBlockAt(loc);
|
|
||||||
}else{
|
|
||||||
return world.getBlockAt(x,y,z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var _getWorld = function(){
|
|
||||||
if (self instanceof org.bukkit.entity.Player)
|
|
||||||
return self.location.world;
|
|
||||||
if (typeof self == "undefined")
|
|
||||||
return;
|
|
||||||
if (self instanceof org.bukkit.command.BlockCommandSender)
|
|
||||||
return self.block.location.world;
|
|
||||||
};
|
|
||||||
|
|
||||||
var _notifyAdministrators = function(msg){
|
|
||||||
var ops = __plugin.server.operators.toArray();
|
|
||||||
for (var i = 0; i < ops.length;i++){
|
|
||||||
var op = ops[i];
|
|
||||||
if (op.isOnline())
|
|
||||||
op.chat(msg);
|
|
||||||
}
|
|
||||||
__plugin.logger.info(msg);
|
|
||||||
};
|
|
||||||
var _echo = function(msg){
|
|
||||||
__plugin.logger.info(msg);
|
|
||||||
if (typeof self == "undefined"){
|
|
||||||
java.lang.System.out.println(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.sendMessage(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
global.getPlayerPos = _getPlayerPos;
|
|
||||||
global.getMousePos = _getMousePos;
|
|
||||||
global.putBlock = _putBlock;
|
|
||||||
global.getBlock = _getBlock;
|
|
||||||
global.putSign = _putSign;
|
|
||||||
global.notifyAdministrators = _notifyAdministrators;
|
|
||||||
global.echo = _echo;
|
|
||||||
|
|
||||||
}());
|
|
|
@ -1,798 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
ScriptCraft API Reference
|
|
||||||
=========================
|
|
||||||
|
|
||||||
Walter Higgins
|
|
||||||
|
|
||||||
[walter.higgins@gmail.com][email]
|
|
||||||
|
|
||||||
[email]: mailto:walter.higgins@gmail.com?subject=ScriptCraft_API_Reference
|
|
||||||
|
|
||||||
Module Loading
|
|
||||||
==============
|
|
||||||
At server startup the ScriptCraft Java plugin is loaded and once
|
|
||||||
loaded the Java plugin will in turn begin loading all of the
|
|
||||||
javascript (.js) files it finds in the js-plugins directory (in the
|
|
||||||
current working directory). If this is the first time the ScriptCraft
|
|
||||||
plugin is loaded, then the js-plugins directory will not yet exist, it
|
|
||||||
will be created and all of the bundled javascript files will be
|
|
||||||
unzipped into it from a bundled resource within the Java plugin. The
|
|
||||||
very first javascript file to load will always be
|
|
||||||
js-plugins/core/_scriptcraft.js. Then all other javascript files are
|
|
||||||
loaded.
|
|
||||||
|
|
||||||
Directory structure
|
|
||||||
-------------------
|
|
||||||
The js-plugins directory is loosely organised into subdirectories -
|
|
||||||
one for each module. Each subdirectory in turn can contain one or more
|
|
||||||
javascript files. Within each directory, a javascript file with the
|
|
||||||
same filename as the directory will always be loaded before all other
|
|
||||||
files in the same directory. So for example, drone/drone.js will
|
|
||||||
always load before any other files in the drone/ directory. Similarly
|
|
||||||
utils/utils.js will always load before any other files in the utils/
|
|
||||||
directory.
|
|
||||||
|
|
||||||
Directories
|
|
||||||
-----------
|
|
||||||
As of February 10 2013, the js-plugins directory has the following sub-directories...
|
|
||||||
|
|
||||||
* core - Contains javascript files containing Core functionality crucial to ScriptCraft and modules which use it.
|
|
||||||
* drone - Contains the drone module and drone extensions. Drone was the first scriptcraft module.
|
|
||||||
* ext - Contains external 3rd party javascript libraries (e.g. json2.js - the JSON lib)
|
|
||||||
* mini-games - Contains mini-games
|
|
||||||
* arrows - The arrows module
|
|
||||||
* signs - The signs module
|
|
||||||
* chat - The chat plugin/module
|
|
||||||
* alias - The alias plugin/module
|
|
||||||
|
|
||||||
Core Module
|
|
||||||
===========
|
|
||||||
This module defines commonly used functions by all plugins...
|
|
||||||
|
|
||||||
* load (filename,warnOnFileNotFound) - loads and evaluates a javascript file, returning the evaluated object.
|
|
||||||
|
|
||||||
* save (object, filename) - saves an object to a file.
|
|
||||||
|
|
||||||
* plugin (name, interface, isPersistent) - defines a new plugin. If
|
|
||||||
isPersistent is true then the plugin doesn't have to worry about
|
|
||||||
loading and saving state - that will be done by the framework. Just
|
|
||||||
make sure that anything you want to save (and restore) is in the
|
|
||||||
'store' property - this will be created automatically if not
|
|
||||||
already defined. (its type is object {} )
|
|
||||||
|
|
||||||
* ready (function) - specifies code to be executed only when all the plugins have loaded.
|
|
||||||
|
|
||||||
* command (name, function) - defines a command that can be used by non-operators.
|
|
||||||
|
|
||||||
load() function
|
|
||||||
---------------
|
|
||||||
The load() function is used by ScriptCraft at startup to load all of
|
|
||||||
the javascript modules and data. You normally wouldn't need to call
|
|
||||||
this function directly. If you put a javascript file anywhere in the
|
|
||||||
craftbukkit/js-plugins directory tree it will be loaded automatically
|
|
||||||
when craftbukkit starts up. The exception is files whose name begins
|
|
||||||
with an underscore `_` character. These files will not be
|
|
||||||
automatically loaded at startup as they are assumed to be files
|
|
||||||
managed / loaded by plugins.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* filenames - An array of file names or a single file name.
|
|
||||||
* warnOnFileNotFound (optional - default: false) - warn if the file was not found.
|
|
||||||
|
|
||||||
Return
|
|
||||||
------
|
|
||||||
load() will return the result of the last statement evaluated in the file.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
load(__folder + "myFile.js"); // loads a javascript file and evaluates it.
|
|
||||||
|
|
||||||
var myData = load("myData.json"); // loads a javascript file and evaluates it - eval'd contents are returned.
|
|
||||||
|
|
||||||
myData.json contents...
|
|
||||||
|
|
||||||
__data = {players:{
|
|
||||||
walterh:{
|
|
||||||
h: ["jsp home {1}"],
|
|
||||||
sunny:["time set 0",
|
|
||||||
"weather clear"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
save() function
|
|
||||||
---------------
|
|
||||||
The save() function saves an in-memory javascript object to a
|
|
||||||
specified file. Under the hood, save() 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 `save()` function can later be
|
|
||||||
restored using the `load()` function.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* objectToSave : The object you want to save.
|
|
||||||
* filename : The name of the file you want to save it to.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
var myObject = { name: 'John Doe',
|
|
||||||
aliases: ['John Ray', 'John Mee'],
|
|
||||||
date_of_birth: '1982/01/31' };
|
|
||||||
save(myObject, 'johndoe.json');
|
|
||||||
|
|
||||||
johndoe.json contents...
|
|
||||||
|
|
||||||
var __data = { "name": "John Doe",
|
|
||||||
"aliases": ["John Ray", "John Mee"],
|
|
||||||
"date_of_birth": "1982/01/31" };
|
|
||||||
|
|
||||||
plugin() function
|
|
||||||
-----------------
|
|
||||||
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 `state` - 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]: http://walterhiggins.net/blog/ScriptCraft-1-Month-later
|
|
||||||
|
|
||||||
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
|
|
||||||
----------
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
See chat/colors.js or alias/alias.js or homes/homes.js for examples of how to use the `command()` function.
|
|
||||||
|
|
||||||
ready() function
|
|
||||||
----------------
|
|
||||||
The `ready()` function provides a way for plugins to do additional
|
|
||||||
setup once all of the other plugins/modules have loaded. For example,
|
|
||||||
event listener registration can only be done after the
|
|
||||||
events/events.js module has loaded. A plugin author could load the
|
|
||||||
file explicilty like this...
|
|
||||||
|
|
||||||
load(__folder + "../events/events.js");
|
|
||||||
|
|
||||||
// event listener registration goes here
|
|
||||||
|
|
||||||
... or better still, just do event regristration using the `ready()`
|
|
||||||
handler knowing that by the time the `ready()` callback is invoked,
|
|
||||||
all of the scriptcraft modules have been loaded...
|
|
||||||
|
|
||||||
ready(function(){
|
|
||||||
// event listener registration goes here
|
|
||||||
// code that depends on other plugins/modules also goes here
|
|
||||||
});
|
|
||||||
|
|
||||||
The execution of the function object passed to the `ready()` function
|
|
||||||
is *deferred* until all of the plugins/modules have loaded. That way
|
|
||||||
you are guaranteed that when the function is invoked, all of the
|
|
||||||
plugins/modules have been loaded and evaluated and are ready to use.
|
|
||||||
***/
|
|
||||||
|
|
||||||
var global = this;
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
***/
|
|
||||||
global.setTimeout = function( callback, delayInMillis){
|
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
//
|
|
||||||
var bukkitTask = server.scheduler.runTaskLater(__plugin, callback, delayInMillis/50);
|
|
||||||
return bukkitTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
clearTimeout() function
|
|
||||||
---------------------
|
|
||||||
A scriptcraft implementation of clearTimeout().
|
|
||||||
|
|
||||||
***/
|
|
||||||
global.clearTimeout = function(bukkitTask){
|
|
||||||
bukkitTask.cancel();
|
|
||||||
};
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
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
|
|
||||||
|
|
||||||
***/
|
|
||||||
global.setInterval = function(callback, intervalInMillis){
|
|
||||||
var delay = intervalInMillis/ 50;
|
|
||||||
var bukkitTask = server.scheduler.runTaskTimer(__plugin, callback, delay, delay);
|
|
||||||
return bukkitTask;
|
|
||||||
};
|
|
||||||
/*************************************************************************
|
|
||||||
clearInterval() function
|
|
||||||
---------------------
|
|
||||||
A scriptcraft implementation of clearInterval().
|
|
||||||
|
|
||||||
***/
|
|
||||||
global.clearInterval = function(bukkitTask){
|
|
||||||
bukkitTask.cancel();
|
|
||||||
};
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
Core Module - Special Variables
|
|
||||||
===============================
|
|
||||||
There are a couple of special javascript variables available in ScriptCraft...
|
|
||||||
|
|
||||||
* __folder - The current working directory - this variable is only to be used within the main body of a .js file.
|
|
||||||
* __plugin - The ScriptCraft JavaPlugin object.
|
|
||||||
* server - The Minecraft Server object.
|
|
||||||
* self - the current player. (Note - this value should not be used in multi-threaded scripts - it's not thread-safe)
|
|
||||||
|
|
||||||
***/
|
|
||||||
var verbose = verbose || false;
|
|
||||||
/*
|
|
||||||
wph 20130124 - make self, plugin and server public - these are far more useful now that tab-complete works.
|
|
||||||
*/
|
|
||||||
var server = org.bukkit.Bukkit.server;
|
|
||||||
//
|
|
||||||
// private implementation
|
|
||||||
//
|
|
||||||
(function(){
|
|
||||||
//
|
|
||||||
// don't execute this more than once
|
|
||||||
//
|
|
||||||
if (typeof load == "function")
|
|
||||||
return ;
|
|
||||||
|
|
||||||
var _canonize = function(file){ return file.getCanonicalPath().replaceAll("\\\\","/"); };
|
|
||||||
|
|
||||||
var _originalScript = __script;
|
|
||||||
var parentFileObj = new java.io.File(__script).getParentFile();
|
|
||||||
var jsPluginsRootDir = parentFileObj.getParentFile();
|
|
||||||
var jsPluginsRootDirName = _canonize(jsPluginsRootDir);
|
|
||||||
|
|
||||||
|
|
||||||
var _loaded = {};
|
|
||||||
/*
|
|
||||||
Load the contents of the file and evaluate as javascript
|
|
||||||
*/
|
|
||||||
var _load = function(filename,warnOnFileNotFound)
|
|
||||||
{
|
|
||||||
var filenames = [];
|
|
||||||
if (filename.constructor == Array)
|
|
||||||
filenames = filename;
|
|
||||||
else
|
|
||||||
filenames = [filename];
|
|
||||||
|
|
||||||
var result = null;
|
|
||||||
|
|
||||||
for (var i =0;i < filenames.length; i++) {
|
|
||||||
|
|
||||||
var file = new java.io.File(filenames[0]);
|
|
||||||
var canonizedFilename = _canonize(file);
|
|
||||||
//
|
|
||||||
// wph 20130123 don't load the same file more than once.
|
|
||||||
//
|
|
||||||
if (_loaded[canonizedFilename])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
print("loading " + canonizedFilename);
|
|
||||||
|
|
||||||
if (file.exists()) {
|
|
||||||
var parent = file.getParentFile();
|
|
||||||
var reader = new java.io.FileReader(file);
|
|
||||||
var br = new java.io.BufferedReader(reader);
|
|
||||||
__engine.put("__script",canonizedFilename);
|
|
||||||
__engine.put("__folder",(parent?_canonize(parent):"")+"/");
|
|
||||||
|
|
||||||
var code = "";
|
|
||||||
try{
|
|
||||||
if (file.getCanonicalPath().endsWith(".coffee")) {
|
|
||||||
var r = undefined;
|
|
||||||
while ((r = br.readLine()) !== null) code += "\"" + r + "\" +\n";
|
|
||||||
code += "\"\"";
|
|
||||||
var code = "load(__folder + \"../core/_coffeescript.js\"); var ___code = "+code+"; eval(CoffeeScript.compile(___code, {bare: true}))";
|
|
||||||
} else {
|
|
||||||
while ((r = br.readLine()) !== null) code += r + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
result = __engine.eval(code);
|
|
||||||
_loaded[canonizedFilename] = true;
|
|
||||||
reader.close();
|
|
||||||
}catch (e){
|
|
||||||
__plugin.logger.severe("Error evaluating " + canonizedFilename + ", " + e );
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (warnOnFileNotFound)
|
|
||||||
__plugin.logger.warning(canonizedFilename + " not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
recursively walk the given directory and return a list of all .js files
|
|
||||||
*/
|
|
||||||
var _listSourceFiles = function(store,dir)
|
|
||||||
{
|
|
||||||
if (typeof dir == "undefined"){
|
|
||||||
dir = new java.io.File(_originalScript).getParentFile().getParentFile();
|
|
||||||
}
|
|
||||||
var files = dir.listFiles();
|
|
||||||
for (var i = 0;i < files.length; i++){
|
|
||||||
var file = files[i];
|
|
||||||
if (file.isDirectory()){
|
|
||||||
_listSourceFiles(store,file);
|
|
||||||
}else{
|
|
||||||
if ((file.getCanonicalPath().endsWith(".js") || file.getCanonicalPath().endsWith(".coffee")) &&
|
|
||||||
!(file.getName().startsWith("_")) &&
|
|
||||||
file.exists())
|
|
||||||
{
|
|
||||||
store.push(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
sort so that .js files with same name as parent directory appear before
|
|
||||||
other files in the same directory
|
|
||||||
*/
|
|
||||||
var sortByModule = function(a,b){
|
|
||||||
a = _canonize(a);
|
|
||||||
b = _canonize(b);
|
|
||||||
var aparts = (""+a).split(/\//);
|
|
||||||
var bparts = (""+b).split(/\//);
|
|
||||||
//var adir = aparts[aparts.length-2];
|
|
||||||
var adir = aparts.slice(0,aparts.length-1).join("/");
|
|
||||||
var afile = aparts[aparts.length-1];
|
|
||||||
//var bdir = bparts[bparts.length-2];
|
|
||||||
var bdir = bparts.slice(0,bparts.length-1).join("/");
|
|
||||||
var bfile = bparts[bparts.length-1];
|
|
||||||
|
|
||||||
if(adir<bdir) return -1;
|
|
||||||
if(adir>bdir) return 1;
|
|
||||||
|
|
||||||
afile = afile.match(/[a-zA-Z0-9\-_]+/)[0];
|
|
||||||
|
|
||||||
if (adir.match(new RegExp(afile + "$")))
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
Reload all of the .js files in the given directory
|
|
||||||
*/
|
|
||||||
var _reload = function(pluginDir)
|
|
||||||
{
|
|
||||||
_loaded = [];
|
|
||||||
var sourceFiles = [];
|
|
||||||
_listSourceFiles(sourceFiles,pluginDir);
|
|
||||||
|
|
||||||
sourceFiles.sort(sortByModule);
|
|
||||||
|
|
||||||
//
|
|
||||||
// script files whose name begins with _ (underscore)
|
|
||||||
// will not be loaded automatically at startup.
|
|
||||||
// These files are assumed to be dependencies/private to plugins
|
|
||||||
//
|
|
||||||
// E.g. If you have a plugin called myMiniGame.js in the myMiniGame directory
|
|
||||||
// and which in addition to myMiniGame.js also includes _myMiniGame_currency.js _myMiniGame_events.js etc.
|
|
||||||
// then it's assumed that _myMiniGame_currency.js and _myMiniGame_events.js will be loaded
|
|
||||||
// as dependencies by myMiniGame.js and do not need to be loaded via js reload
|
|
||||||
//
|
|
||||||
var len = sourceFiles.length;
|
|
||||||
for (var i = 0;i < len; i++){
|
|
||||||
load(_canonize(sourceFiles[i]),true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Save a javascript object to a file (saves using JSON notation)
|
|
||||||
*/
|
|
||||||
var _save = function(object, filename){
|
|
||||||
var objectToStr = null;
|
|
||||||
try{
|
|
||||||
objectToStr = JSON.stringify(object);
|
|
||||||
}catch(e){
|
|
||||||
print("ERROR: " + e.getMessage() + " while saving " + filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var f = new java.io.File(filename);
|
|
||||||
var out = new java.io.PrintWriter(new java.io.FileWriter(f));
|
|
||||||
out.println("__data = " + objectToStr);
|
|
||||||
out.close();
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
plugin management
|
|
||||||
*/
|
|
||||||
var _plugins = {};
|
|
||||||
var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPersistent)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// don't load plugin more than once
|
|
||||||
//
|
|
||||||
if (typeof _plugins[moduleName] != "undefined")
|
|
||||||
return;
|
|
||||||
|
|
||||||
var pluginData = {persistent: isPersistent, module: moduleObject};
|
|
||||||
moduleObject.store = moduleObject.store || {};
|
|
||||||
_plugins[moduleName] = pluginData;
|
|
||||||
|
|
||||||
if (isPersistent)
|
|
||||||
moduleObject.store = load(jsPluginsRootDirName + "/" + moduleName + "-store.txt") || {};
|
|
||||||
|
|
||||||
global[moduleName] = moduleObject;
|
|
||||||
return moduleObject;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
allow for deferred execution (once all modules have loaded)
|
|
||||||
*/
|
|
||||||
var _deferred = [];
|
|
||||||
var _ready = function( func ){
|
|
||||||
_deferred.push(func);
|
|
||||||
};
|
|
||||||
var _cmdInterceptors = [];
|
|
||||||
/*
|
|
||||||
command management - allow for non-ops to execute approved javascript code.
|
|
||||||
*/
|
|
||||||
var _commands = {};
|
|
||||||
var _command = function(name,func,options,intercepts)
|
|
||||||
{
|
|
||||||
if (typeof name == "undefined"){
|
|
||||||
// it's an invocation from the Java Plugin!
|
|
||||||
if (__cmdArgs.length === 0)
|
|
||||||
throw new Error("Usage: jsp command-name command-parameters");
|
|
||||||
var name = __cmdArgs[0];
|
|
||||||
var cmd = _commands[name];
|
|
||||||
if (typeof cmd === "undefined"){
|
|
||||||
// it's not a global command - pass it on to interceptors
|
|
||||||
var intercepted = false;
|
|
||||||
for (var i = 0;i < _cmdInterceptors.length;i++){
|
|
||||||
if (_cmdInterceptors[i](__cmdArgs))
|
|
||||||
intercepted = true;
|
|
||||||
}
|
|
||||||
if (!intercepted)
|
|
||||||
self.sendMessage("Command '" + name + "' is not recognised");
|
|
||||||
}else{
|
|
||||||
func = cmd.callback;
|
|
||||||
var params = [];
|
|
||||||
for (var i =1; i < __cmdArgs.length;i++){
|
|
||||||
params.push("" + __cmdArgs[i]);
|
|
||||||
}
|
|
||||||
return func(params);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (typeof options == "undefined")
|
|
||||||
options = [];
|
|
||||||
_commands[name] = {callback: func, options: options};
|
|
||||||
if (intercepts)
|
|
||||||
_cmdInterceptors.push(func);
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var _rmCommand = function(name){
|
|
||||||
delete _commands[name];
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
Tab Completion of the /js and /jsp commands
|
|
||||||
*/
|
|
||||||
var _isJavaObject = function(o){
|
|
||||||
var result = false;
|
|
||||||
try {
|
|
||||||
o.hasOwnProperty("testForJava");
|
|
||||||
}catch (e){
|
|
||||||
// java will throw an error when an attempt is made to access the
|
|
||||||
// hasOwnProperty method. (it won't exist for Java objects)
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var _javaLangObjectMethods = ["equals","getClass","class","getClass","hashCode","notify","notifyAll","toString","wait","clone","finalize"];
|
|
||||||
var _getProperties = function(o)
|
|
||||||
{
|
|
||||||
var result = [];
|
|
||||||
if (_isJavaObject(o))
|
|
||||||
{
|
|
||||||
propertyLoop:
|
|
||||||
for (var i in o)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// don't include standard Object methods
|
|
||||||
//
|
|
||||||
var isObjectMethod = false;
|
|
||||||
for (var j = 0;j < _javaLangObjectMethods.length; j++)
|
|
||||||
if (_javaLangObjectMethods[j] == i)
|
|
||||||
continue propertyLoop;
|
|
||||||
if (typeof o[i] == "function" )
|
|
||||||
result.push(i+"()");
|
|
||||||
else
|
|
||||||
result.push(i);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (o.constructor == Array)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
for (var i in o){
|
|
||||||
if (i.match(/^[^_]/)){
|
|
||||||
if (typeof o[i] == "function")
|
|
||||||
result.push(i+"()");
|
|
||||||
else
|
|
||||||
result.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.sort();
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
Tab completion for the /jsp commmand
|
|
||||||
*/
|
|
||||||
var __onTabCompleteJSP = function() {
|
|
||||||
var result = global.__onTC_result;
|
|
||||||
var args = global.__onTC_args;
|
|
||||||
var cmdInput = args[0];
|
|
||||||
var cmd = _commands[cmdInput];
|
|
||||||
if (cmd){
|
|
||||||
var opts = cmd.options;
|
|
||||||
var len = opts.length;
|
|
||||||
if (args.length == 1){
|
|
||||||
for (var i = 0;i < len; i++)
|
|
||||||
result.add(opts[i]);
|
|
||||||
}else{
|
|
||||||
// partial e.g. /jsp chat_color dar
|
|
||||||
for (var i = 0;i < len; i++){
|
|
||||||
if (opts[i].indexOf(args[1]) == 0){
|
|
||||||
result.add(opts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (args.length == 0){
|
|
||||||
for (var i in _commands)
|
|
||||||
result.add(i);
|
|
||||||
}else{
|
|
||||||
// partial e.g. /jsp al
|
|
||||||
// should tabcomplete to alias
|
|
||||||
//
|
|
||||||
for (var c in _commands){
|
|
||||||
if (c.indexOf(cmdInput) == 0){
|
|
||||||
result.add(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
Tab completion for the /js command
|
|
||||||
*/
|
|
||||||
var __onTabCompleteJS = function()
|
|
||||||
{
|
|
||||||
if (__onTC_cmd.name == "jsp")
|
|
||||||
return __onTabCompleteJSP()
|
|
||||||
|
|
||||||
var _globalSymbols = _getProperties(global)
|
|
||||||
var result = global.__onTC_result;
|
|
||||||
var args = global.__onTC_args;
|
|
||||||
var lastArg = args.length?args[args.length-1]+"":null;
|
|
||||||
var propsOfLastArg = [];
|
|
||||||
var statement = args.join(" ");
|
|
||||||
|
|
||||||
statement = statement.replace(/^\s+/,"").replace(/\s+$/,"");
|
|
||||||
|
|
||||||
|
|
||||||
if (statement.length == 0)
|
|
||||||
propsOfLastArg = _globalSymbols;
|
|
||||||
else{
|
|
||||||
var statementSyms = statement.split(/[^\$a-zA-Z0-9_\.]/);
|
|
||||||
var lastSymbol = statementSyms[statementSyms.length-1];
|
|
||||||
//print("DEBUG: lastSymbol=[" + lastSymbol + "]");
|
|
||||||
//
|
|
||||||
// try to complete the object ala java IDEs.
|
|
||||||
//
|
|
||||||
var parts = lastSymbol.split(/\./);
|
|
||||||
var name = parts[0];
|
|
||||||
var symbol = global[name];
|
|
||||||
var lastGoodSymbol = symbol;
|
|
||||||
if (typeof symbol != "undefined")
|
|
||||||
{
|
|
||||||
for (var i = 1; i < parts.length;i++){
|
|
||||||
name = parts[i];
|
|
||||||
symbol = symbol[name];
|
|
||||||
if (typeof symbol == "undefined")
|
|
||||||
break;
|
|
||||||
lastGoodSymbol = symbol;
|
|
||||||
}
|
|
||||||
//print("debug:name["+name+"]lastSymbol["+lastSymbol+"]symbol["+symbol+"]");
|
|
||||||
if (typeof symbol == "undefined"){
|
|
||||||
//
|
|
||||||
// look up partial matches against last good symbol
|
|
||||||
//
|
|
||||||
var objectProps = _getProperties(lastGoodSymbol);
|
|
||||||
if (name == ""){
|
|
||||||
// if the last symbol looks like this..
|
|
||||||
// ScriptCraft.
|
|
||||||
//
|
|
||||||
|
|
||||||
for (var i =0;i < objectProps.length;i++){
|
|
||||||
var candidate = lastSymbol + objectProps[i];
|
|
||||||
var re = new RegExp(lastSymbol + "$","g");
|
|
||||||
propsOfLastArg.push(lastArg.replace(re,candidate));
|
|
||||||
}
|
|
||||||
|
|
||||||
}else{
|
|
||||||
// it looks like this..
|
|
||||||
// ScriptCraft.co
|
|
||||||
//
|
|
||||||
//print("debug:case Y: ScriptCraft.co");
|
|
||||||
|
|
||||||
var li = statement.lastIndexOf(name);
|
|
||||||
for (var i = 0; i < objectProps.length;i++){
|
|
||||||
if (objectProps[i].indexOf(name) == 0)
|
|
||||||
{
|
|
||||||
var candidate = lastSymbol.substring(0,lastSymbol.lastIndexOf(name));
|
|
||||||
candidate = candidate + objectProps[i];
|
|
||||||
var re = new RegExp(lastSymbol+ "$","g");
|
|
||||||
//print("DEBUG: re=" + re + ",lastSymbol="+lastSymbol+",lastArg=" + lastArg + ",candidate=" + candidate);
|
|
||||||
propsOfLastArg.push(lastArg.replace(re,candidate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
//print("debug:case Z:ScriptCraft");
|
|
||||||
var objectProps = _getProperties(symbol);
|
|
||||||
for (var i = 0; i < objectProps.length; i++){
|
|
||||||
var re = new RegExp(lastSymbol+ "$","g");
|
|
||||||
propsOfLastArg.push(lastArg.replace(re,lastSymbol + "." + objectProps[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
//print("debug:case AB:ScriptCr");
|
|
||||||
// loop thru globalSymbols looking for a good match
|
|
||||||
for (var i = 0;i < _globalSymbols.length; i++){
|
|
||||||
if (_globalSymbols[i].indexOf(lastSymbol) == 0){
|
|
||||||
var possibleCompletion = _globalSymbols[i];
|
|
||||||
var re = new RegExp(lastSymbol+ "$","g");
|
|
||||||
propsOfLastArg.push(lastArg.replace(re,possibleCompletion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i = 0;i < propsOfLastArg.length; i++)
|
|
||||||
result.add(propsOfLastArg[i]);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Unload Handlers
|
|
||||||
*/
|
|
||||||
var unloadHandlers = [];
|
|
||||||
var _addUnloadHandler = function(f) {
|
|
||||||
unloadHandlers.push(f);
|
|
||||||
};
|
|
||||||
var _runUnloadHandlers = function() {
|
|
||||||
for (var i = 0; i < unloadHandlers.length; i++) {
|
|
||||||
unloadHandlers[i]();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
refresh() function
|
|
||||||
------------------
|
|
||||||
The refresh() function will ...
|
|
||||||
|
|
||||||
1. Disable the ScriptCraft plugin.
|
|
||||||
2. Unload all event listeners associated with the ScriptCraft plugin.
|
|
||||||
3. Enable the ScriptCraft plugin.
|
|
||||||
|
|
||||||
... refresh() can be used during development to reload only scriptcraft javascript files.
|
|
||||||
See [issue #69][issue69] for more information.
|
|
||||||
|
|
||||||
[issue69]: https://github.com/walterhiggins/ScriptCraft/issues/69
|
|
||||||
|
|
||||||
***/
|
|
||||||
global.refresh = function(){
|
|
||||||
__plugin.pluginLoader.disablePlugin(__plugin);
|
|
||||||
__plugin.pluginLoader.enablePlugin(__plugin);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
global.load = _load;
|
|
||||||
global.save = _save;
|
|
||||||
global.plugin = _plugin;
|
|
||||||
global.ready = _ready;
|
|
||||||
global.command = _command;
|
|
||||||
global._onTabComplete = __onTabCompleteJS;
|
|
||||||
global.addUnloadHandler = _addUnloadHandler;
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// assumes this was loaded from js-plugins/core/
|
|
||||||
// load all of the plugins.
|
|
||||||
//
|
|
||||||
_reload(jsPluginsRootDir);
|
|
||||||
|
|
||||||
//
|
|
||||||
// all modules have loaded
|
|
||||||
//
|
|
||||||
for (var i =0;i < _deferred.length;i++)
|
|
||||||
_deferred[i]();
|
|
||||||
|
|
||||||
events.on("server.PluginDisableEvent",function(l,e){
|
|
||||||
//
|
|
||||||
// save all plugins which have persistent data
|
|
||||||
//
|
|
||||||
for (var moduleName in _plugins){
|
|
||||||
var pluginData = _plugins[moduleName];
|
|
||||||
if (pluginData.persistent)
|
|
||||||
save(pluginData.module.store, jsPluginsRootDirName + "/" + moduleName + "-store.txt");
|
|
||||||
}
|
|
||||||
_runUnloadHandlers();
|
|
||||||
org.bukkit.event.HandlerList["unregisterAll(org.bukkit.plugin.Plugin)"](__plugin);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,274 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Blocks Module
|
|
||||||
=============
|
|
||||||
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.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
***/
|
|
||||||
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: '62:2',
|
|
||||||
jungle: '6:3'
|
|
||||||
},
|
|
||||||
bedrock: 7,
|
|
||||||
water: 8,
|
|
||||||
water_still: 9,
|
|
||||||
lava: 10,
|
|
||||||
lava_still: 11,
|
|
||||||
sand: 12,
|
|
||||||
gravel: 13,
|
|
||||||
gold_ore: 14,
|
|
||||||
iron_ore: 15,
|
|
||||||
coal_ore: 16,
|
|
||||||
wood: 17,
|
|
||||||
leaves: 18,
|
|
||||||
sponge: 19,
|
|
||||||
glass: 20,
|
|
||||||
lapis_lazuli_ore: 21,
|
|
||||||
lapis_lazuli_block: 22,
|
|
||||||
dispenser: 23,
|
|
||||||
sandstone: 24,
|
|
||||||
note: 25,
|
|
||||||
bed: 26,
|
|
||||||
powered_rail: 27,
|
|
||||||
detector_rail: 28,
|
|
||||||
sticky_piston: 29,
|
|
||||||
cobweb: 30,
|
|
||||||
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,
|
|
||||||
mushroom_brown: 39,
|
|
||||||
mushroom_red: 40,
|
|
||||||
gold: 41,
|
|
||||||
iron: 42,
|
|
||||||
tnt: 46,
|
|
||||||
bookshelf: 47,
|
|
||||||
moss_stone: 48,
|
|
||||||
obsidian: 49,
|
|
||||||
torch: 50,
|
|
||||||
fire: 51,
|
|
||||||
monster_spawner: 52,
|
|
||||||
stairs: {
|
|
||||||
oak: 53,
|
|
||||||
cobblestone: 67,
|
|
||||||
brick: 108,
|
|
||||||
stone: 109,
|
|
||||||
nether: 114,
|
|
||||||
sandstone: 128,
|
|
||||||
spruce: 134,
|
|
||||||
birch: 135,
|
|
||||||
jungle: 136,
|
|
||||||
quartz: 156
|
|
||||||
},
|
|
||||||
chest: 54,
|
|
||||||
redstone_wire: 55,
|
|
||||||
diamond_ore: 56,
|
|
||||||
diamond: 57,
|
|
||||||
crafting_table: 58,
|
|
||||||
wheat_seeds: 59,
|
|
||||||
farmland: 60,
|
|
||||||
furnace: 61,
|
|
||||||
furnace_burning: 62,
|
|
||||||
sign_post: 63,
|
|
||||||
door_wood: 64,
|
|
||||||
ladder: 65,
|
|
||||||
rail: 66,
|
|
||||||
sign: 68,
|
|
||||||
lever: 69,
|
|
||||||
pressure_plate_stone: 70,
|
|
||||||
door_iron: 71,
|
|
||||||
pressure_plate_wood: 72,
|
|
||||||
redstone_ore: 73,
|
|
||||||
redstone_ore_glowing: 74,
|
|
||||||
torch_redstone: 75,
|
|
||||||
torch_redstone_active: 76,
|
|
||||||
stone_button: 77,
|
|
||||||
ice: 79,
|
|
||||||
snow: 80,
|
|
||||||
cactus: 81,
|
|
||||||
clay: 82,
|
|
||||||
sugar_cane: 83,
|
|
||||||
jukebox: 84,
|
|
||||||
fence: 85,
|
|
||||||
pumpkin: 86,
|
|
||||||
netherrack: 87,
|
|
||||||
soulsand: 88,
|
|
||||||
glowstone: 89,
|
|
||||||
netherportal: 90,
|
|
||||||
jackolantern: 91,
|
|
||||||
cake: 92,
|
|
||||||
redstone_repeater: 93,
|
|
||||||
redeston_repeater_active: 94,
|
|
||||||
chest_locked: 95,
|
|
||||||
trapdoor: 96,
|
|
||||||
monster_egg: 97,
|
|
||||||
brick: {
|
|
||||||
stone: 98,
|
|
||||||
mossy: '98:1',
|
|
||||||
cracked: '98:2',
|
|
||||||
chiseled: '98:3',
|
|
||||||
red: 45
|
|
||||||
},
|
|
||||||
mushroom_brown_huge: 99,
|
|
||||||
mushroom_red_huge: 100,
|
|
||||||
iron_bars: 101,
|
|
||||||
glass_pane: 102,
|
|
||||||
melon: 103,
|
|
||||||
pumpkin_stem: 104,
|
|
||||||
melon_stem: 105,
|
|
||||||
vines: 106,
|
|
||||||
fence_gate: 107,
|
|
||||||
mycelium: 110,
|
|
||||||
lily_pad: 111,
|
|
||||||
nether: 112,
|
|
||||||
nether_fence: 113,
|
|
||||||
netherwart: 115,
|
|
||||||
table_enchantment: 116,
|
|
||||||
brewing_stand: 117,
|
|
||||||
cauldron: 118,
|
|
||||||
endportal: 119,
|
|
||||||
endportal_frame: 120,
|
|
||||||
endstone: 121,
|
|
||||||
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,
|
|
||||||
tripwire_hook: 131,
|
|
||||||
tripwire: 132,
|
|
||||||
emerald: 133,
|
|
||||||
command: 137,
|
|
||||||
beacon: 138,
|
|
||||||
cobblestone_wall: 139,
|
|
||||||
flowerpot: 140,
|
|
||||||
carrots: 141,
|
|
||||||
potatoes: 142,
|
|
||||||
button_wood: 143,
|
|
||||||
mobhead: 144,
|
|
||||||
anvil: 145,
|
|
||||||
chest_trapped: 146,
|
|
||||||
pressure_plate_weighted_light: 147,
|
|
||||||
pressure_plate_weighted_heavy: 148,
|
|
||||||
redstone_comparator: 149,
|
|
||||||
redstone_comparator_active: 150,
|
|
||||||
daylight_sensor: 151,
|
|
||||||
redstone: 152,
|
|
||||||
netherquartzore: 153,
|
|
||||||
hopper: 154,
|
|
||||||
quartz: 155,
|
|
||||||
rail_activator: 157,
|
|
||||||
dropper: 158,
|
|
||||||
stained_clay: {
|
|
||||||
white: 159 // All other colors added below
|
|
||||||
},
|
|
||||||
hay: 170,
|
|
||||||
carpet: {
|
|
||||||
white: 171 // All other colors added below
|
|
||||||
},
|
|
||||||
hardened_clay: 172,
|
|
||||||
coal_block: 173
|
|
||||||
};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
// 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"];
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
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];
|
|
||||||
})();
|
|
|
@ -1,373 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Drone.blocktype() method
|
|
||||||
========================
|
|
||||||
Creates the text out of blocks. Useful for large-scale in-game signs.
|
|
||||||
|
|
||||||
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
|
|
||||||
-------
|
|
||||||
To create a 2-line high message using glowstone...
|
|
||||||
|
|
||||||
blocktype("Hello\nWorld",blocks.glowstone);
|
|
||||||
|
|
||||||
![blocktype example][imgbt1]
|
|
||||||
|
|
||||||
[imgbt1]: img/blocktype1.png
|
|
||||||
|
|
||||||
***/
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
var bitmaps = {
|
|
||||||
raw: {
|
|
||||||
'0':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'1':' # '+
|
|
||||||
' ## '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'2':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'3':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ## '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'4':' # '+
|
|
||||||
' ## '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'5':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'6':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'7':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'8':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'9':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'a':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'b':' ## '+
|
|
||||||
' # # '+
|
|
||||||
' ## '+
|
|
||||||
' # # '+
|
|
||||||
' ## ',
|
|
||||||
|
|
||||||
'c':' ## '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' ## ',
|
|
||||||
|
|
||||||
'd':' ## '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ## ',
|
|
||||||
|
|
||||||
'e':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ## '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'f':' ### '+
|
|
||||||
' # '+
|
|
||||||
' ## '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'g':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'h':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'i':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'j':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'k':' # '+
|
|
||||||
' # # '+
|
|
||||||
' ## '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'l':' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'm':' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'n':' ## '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'o':' # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'p':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'q':' ### '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'r':' ## '+
|
|
||||||
' # # '+
|
|
||||||
' ## '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
's':' ## '+
|
|
||||||
' # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' ## ',
|
|
||||||
|
|
||||||
't':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'u':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'v':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'w':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' ### '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'x':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # '+
|
|
||||||
' # # '+
|
|
||||||
' # # ',
|
|
||||||
|
|
||||||
'y':' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # # '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'z':' ### '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' ### ',
|
|
||||||
|
|
||||||
'!':' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
':':' '+
|
|
||||||
' # '+
|
|
||||||
' '+
|
|
||||||
' # '+
|
|
||||||
' ',
|
|
||||||
|
|
||||||
';':' '+
|
|
||||||
' # '+
|
|
||||||
' '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
',':' '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'/':' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
'+':' '+
|
|
||||||
' # '+
|
|
||||||
' ### '+
|
|
||||||
' # '+
|
|
||||||
' ',
|
|
||||||
|
|
||||||
'-':' '+
|
|
||||||
' '+
|
|
||||||
' ### '+
|
|
||||||
' '+
|
|
||||||
' ',
|
|
||||||
|
|
||||||
'.':' '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' # ',
|
|
||||||
|
|
||||||
"'":' # '+
|
|
||||||
' # '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' ',
|
|
||||||
|
|
||||||
' ':' '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' '+
|
|
||||||
' '
|
|
||||||
},
|
|
||||||
computed: {}
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
wph 20130121 compute the width, and x,y coords of pixels ahead of time
|
|
||||||
*/
|
|
||||||
for (var c in bitmaps.raw){
|
|
||||||
var bits = bitmaps.raw[c];
|
|
||||||
var width = bits.length/5;
|
|
||||||
var bmInfo = {"width": width,"pixels":[]}
|
|
||||||
bitmaps.computed[c] = bmInfo;
|
|
||||||
for (var j = 0; j < bits.length; j++){
|
|
||||||
if (bits.charAt(j) != ' '){
|
|
||||||
bmInfo.pixels.push([j%width,Math.ceil(j/width)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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){
|
|
||||||
|
|
||||||
this.chkpt('blocktext');
|
|
||||||
|
|
||||||
if (typeof fg == "undefined")
|
|
||||||
fg = blocks.wool.black;
|
|
||||||
|
|
||||||
var bmfg = this._getBlockIdAndMeta(fg);
|
|
||||||
var bmbg = null;
|
|
||||||
if (typeof bg != "undefined")
|
|
||||||
bmbg = this._getBlockIdAndMeta(bg);
|
|
||||||
var lines = message.split("\n");
|
|
||||||
var lineCount = lines.length;
|
|
||||||
for (var h = 0;h < lineCount; h++) {
|
|
||||||
var line = lines[h];
|
|
||||||
line = line.toLowerCase().replace(/[^0-9a-z \.\-\+\/\;\'\:\!]/g,"");
|
|
||||||
this.up(7*(lineCount-(h+1)));
|
|
||||||
|
|
||||||
for (var i =0;i < line.length; i++) {
|
|
||||||
var ch = line.charAt(i)
|
|
||||||
var bits = bitmaps.computed[ch];
|
|
||||||
if (typeof bits == "undefined"){
|
|
||||||
bits = bitmaps.computed[' '];
|
|
||||||
}
|
|
||||||
var charWidth = bits.width;
|
|
||||||
if (typeof bg != "undefined")
|
|
||||||
this.cuboidX(bmbg[0],bmbg[1],charWidth,7,1);
|
|
||||||
for (var j = 0;j < bits.pixels.length;j++){
|
|
||||||
this.chkpt('btbl');
|
|
||||||
var x = bits.pixels[j][0];
|
|
||||||
var y = bits.pixels[j][1];
|
|
||||||
this.up(6-y).right(x).cuboidX(bmfg[0],bmfg[1]);
|
|
||||||
this.move('btbl');
|
|
||||||
}
|
|
||||||
this.right(charWidth-1);
|
|
||||||
}
|
|
||||||
this.move('blocktext');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.move('blocktext');
|
|
||||||
});
|
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
//
|
|
||||||
// a castle is just a big wide fort with 4 taller forts at each corner
|
|
||||||
//
|
|
||||||
Drone.extend('castle', function(side, height)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// use sensible default parameter values
|
|
||||||
// if no parameters are supplied
|
|
||||||
//
|
|
||||||
if (typeof side == "undefined")
|
|
||||||
side = 24;
|
|
||||||
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');
|
|
||||||
//
|
|
||||||
// how big the towers at each corner will be...
|
|
||||||
//
|
|
||||||
var towerSide = 10;
|
|
||||||
var towerHeight = height+4;
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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');
|
|
||||||
//
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
return this.move('castle');
|
|
||||||
});
|
|
|
@ -1,30 +0,0 @@
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
Drone.extend("chessboard", function(whiteBlock, blackBlock, width, depth) {
|
|
||||||
this.chkpt('chessboard-start');
|
|
||||||
width = width || 8;
|
|
||||||
depth = depth || width;
|
|
||||||
blackBlock = blackBlock || blocks.wool.black;
|
|
||||||
whiteBlock = whiteBlock || blocks.wool.white;
|
|
||||||
|
|
||||||
for(var i = 0; i < width; ++i) {
|
|
||||||
for(var j = 0; j < depth; ++j) {
|
|
||||||
var block = blackBlock;
|
|
||||||
if((i+j)%2 == 1) {
|
|
||||||
block = whiteBlock;
|
|
||||||
}
|
|
||||||
this.box(block);
|
|
||||||
this.right();
|
|
||||||
}
|
|
||||||
this.move('chessboard-start').fwd(i+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.move('chessboard-start');
|
|
||||||
});
|
|
|
@ -1,83 +0,0 @@
|
||||||
//
|
|
||||||
// need to use the drone module to create buildings easily
|
|
||||||
// it can be done using calls to putBlock(), putSign(), getPlayerPos() and getMousePos()
|
|
||||||
// but it's easier to use the Drone class
|
|
||||||
// __folder is a special javascript variable whose value is the directory where the
|
|
||||||
// current script resides.
|
|
||||||
//
|
|
||||||
// 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();
|
|
||||||
//
|
|
||||||
|
|
||||||
Drone.extend('cottage',function ()
|
|
||||||
{
|
|
||||||
this.chkpt('cottage')
|
|
||||||
.box0(48,7,2,6) // 4 walls
|
|
||||||
.right(3).door() // door front and center
|
|
||||||
.up(1).left(2).box(102) // windows to left and right
|
|
||||||
.right(4).box(102)
|
|
||||||
.left(5).up().prism0(53,7,6);
|
|
||||||
//
|
|
||||||
// put up a sign near door.
|
|
||||||
//
|
|
||||||
this.down().right(4).sign(["Home","Sweet","Home"],68);
|
|
||||||
|
|
||||||
return this.move('cottage');
|
|
||||||
});
|
|
||||||
//
|
|
||||||
// a more complex script that builds an tree-lined avenue with
|
|
||||||
// cottages on both sides.
|
|
||||||
//
|
|
||||||
Drone.extend('cottage_road', function(numberCottages)
|
|
||||||
{
|
|
||||||
if (typeof numberCottages == "undefined"){
|
|
||||||
numberCottages = 6;
|
|
||||||
}
|
|
||||||
var i=0, distanceBetweenTrees = 11;
|
|
||||||
//
|
|
||||||
// step 1 build the road.
|
|
||||||
//
|
|
||||||
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
|
|
||||||
.up().right() // now centered in middle of road
|
|
||||||
.chkpt("cr"); // will be returning to this position later
|
|
||||||
|
|
||||||
//
|
|
||||||
// step 2 line the road with trees
|
|
||||||
//
|
|
||||||
for (; i < cottagesPerSide+1;i++){
|
|
||||||
this
|
|
||||||
.left(5).oak()
|
|
||||||
.right(10).oak()
|
|
||||||
.left(5) // return to middle of road
|
|
||||||
.fwd(distanceBetweenTrees+1); // move forward.
|
|
||||||
}
|
|
||||||
this.move("cr").back(6); // move back 1/2 the distance between trees
|
|
||||||
|
|
||||||
// this function builds a path leading to a cottage.
|
|
||||||
function pathAndCottage(d){
|
|
||||||
return d.down().box(43,1,1,5).fwd(5).left(3).up().cottage();
|
|
||||||
};
|
|
||||||
//
|
|
||||||
// step 3 build cottages on each side
|
|
||||||
//
|
|
||||||
for (i = 0;i < cottagesPerSide; i++)
|
|
||||||
{
|
|
||||||
this.fwd(distanceBetweenTrees+1).chkpt("r"+i);
|
|
||||||
// build cottage on left
|
|
||||||
pathAndCottage(this.turn(3)).move("r"+i);
|
|
||||||
// build cottage on right
|
|
||||||
pathAndCottage(this.turn()).move("r"+i);
|
|
||||||
}
|
|
||||||
// return drone to where it was at start of function
|
|
||||||
return this.move("cottage_road");
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
//
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
});
|
|
|
@ -1,66 +0,0 @@
|
||||||
//
|
|
||||||
// constructs a medieval fort
|
|
||||||
//
|
|
||||||
Drone.extend('fort', function(side, height)
|
|
||||||
{
|
|
||||||
if (typeof side == "undefined")
|
|
||||||
side = 18;
|
|
||||||
if (typeof height == "undefined")
|
|
||||||
height = 6;
|
|
||||||
// make sure side is even
|
|
||||||
if (side%2)
|
|
||||||
side++;
|
|
||||||
if (height < 4 || side < 10)
|
|
||||||
throw new java.lang.RuntimeException("Forts must be at least 10 wide X 4 tall");
|
|
||||||
var brick = 98;
|
|
||||||
//
|
|
||||||
// build walls.
|
|
||||||
//
|
|
||||||
this.chkpt('fort').box0(brick,side,height-1,side);
|
|
||||||
//
|
|
||||||
// build battlements
|
|
||||||
//
|
|
||||||
this.up(height-1);
|
|
||||||
for (i = 0;i <= 3;i++){
|
|
||||||
var turret = [];
|
|
||||||
this.box(brick) // solid brick corners
|
|
||||||
.up().box('50:5').down() // light a torch on each corner
|
|
||||||
.fwd();
|
|
||||||
turret.push('109:'+ Drone.PLAYER_STAIRS_FACING[this.dir]);
|
|
||||||
turret.push('109:'+ Drone.PLAYER_STAIRS_FACING[(this.dir+2)%4]);
|
|
||||||
try{
|
|
||||||
this.boxa(turret,1,1,side-2).fwd(side-2).turn();
|
|
||||||
}catch(e){
|
|
||||||
self.sendMessage("ERROR: " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// build battlement's floor
|
|
||||||
//
|
|
||||||
this.move('fort');
|
|
||||||
this.up(height-2).fwd().right().box('126:0',side-2,1,side-2);
|
|
||||||
var battlementWidth = 3;
|
|
||||||
if (side <= 12)
|
|
||||||
battlementWidth = 2;
|
|
||||||
|
|
||||||
this.fwd(battlementWidth).right(battlementWidth)
|
|
||||||
.box(0,side-((1+battlementWidth)*2),1,side-((1+battlementWidth)*2));
|
|
||||||
//
|
|
||||||
// add door
|
|
||||||
//
|
|
||||||
var torch = '50:' + 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
|
|
||||||
//
|
|
||||||
// add ladder up to battlements
|
|
||||||
//
|
|
||||||
var 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);
|
|
||||||
return this.move('fort');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Drone.rainbow() method
|
|
||||||
======================
|
|
||||||
Creates a Rainbow.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* radius (optional - default:18) - The radius of the rainbow
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
var d = new Drone();
|
|
||||||
d.rainbow(30);
|
|
||||||
|
|
||||||
![rainbow example](img/rainbowex1.png)
|
|
||||||
|
|
||||||
***/
|
|
||||||
Drone.extend('rainbow', function(radius){
|
|
||||||
if (typeof radius == "undefined")
|
|
||||||
radius = 18;
|
|
||||||
|
|
||||||
this.chkpt('rainbow');
|
|
||||||
this.down(radius);
|
|
||||||
// copy blocks.rainbow and add air at end (to compensate for strokewidth)
|
|
||||||
var colors = blocks.rainbow.slice(0);
|
|
||||||
colors.push(blocks.air);
|
|
||||||
for (var i = 0;i < colors.length; i++) {
|
|
||||||
var bm = this._getBlockIdAndMeta(colors[i]);
|
|
||||||
this.arc({
|
|
||||||
blockType: bm[0],
|
|
||||||
meta: bm[1],
|
|
||||||
radius: radius-i,
|
|
||||||
strokeWidth: 2,
|
|
||||||
quadrants: {topright: true,
|
|
||||||
topleft: true},
|
|
||||||
orientation: 'vertical'}).right().up();
|
|
||||||
}
|
|
||||||
return this.move('rainbow');
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
Drone.extend('skyscraper',function(floors){
|
|
||||||
floors = floors || 10;
|
|
||||||
this.chkpt('skyscraper');
|
|
||||||
for (var i = 0;i < floors; i++)
|
|
||||||
{
|
|
||||||
this.box(blocks.iron,20,1,20)
|
|
||||||
.up()
|
|
||||||
.box0(blocks.glass_pane,20,3,20);
|
|
||||||
this.up(3);
|
|
||||||
}
|
|
||||||
return this.move('skyscraper');
|
|
||||||
});
|
|
|
@ -1,44 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Drone.spiral_stairs() method
|
|
||||||
============================
|
|
||||||
Constructs a spiral staircase with slabs at each corner.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
![Spiral Staircase](img/spiralstair1.png)
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
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');
|
|
||||||
|
|
||||||
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');
|
|
||||||
});
|
|
|
@ -1,19 +0,0 @@
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
for(var i = 0; i < maxIterations||1000; ++i) {
|
|
||||||
this.box(block);
|
|
||||||
this[dir].call(this);
|
|
||||||
if(getBlock(this.x, this.y, this.z) !== "0:0") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
});
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// constructs a mayan temple
|
|
||||||
//
|
|
||||||
Drone.extend('temple', function(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();
|
|
||||||
side = side - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.move('temple');
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
/**
|
|
||||||
* Create a partial function
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* func - base function
|
|
||||||
* [remaining arguments] - arguments bound to the partial function
|
|
||||||
*/
|
|
||||||
function partial(func /*, 0..n args */) {
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
return function() {
|
|
||||||
var allArguments = args.concat(Array.prototype.slice.call(arguments));
|
|
||||||
return func.apply(this, allArguments);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
});
|
|
|
@ -1,265 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Drone.sphere() method
|
|
||||||
=====================
|
|
||||||
Creates a sphere.
|
|
||||||
|
|
||||||
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...
|
|
||||||
|
|
||||||
sphere( blocks.iron, 10);
|
|
||||||
|
|
||||||
![sphere example](img/sphereex1.png)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
***/
|
|
||||||
Drone.extend('sphere', function(block,radius)
|
|
||||||
{
|
|
||||||
var lastRadius = radius;
|
|
||||||
var slices = [[radius,0]];
|
|
||||||
var diameter = radius*2;
|
|
||||||
var bm = this._getBlockIdAndMeta(block);
|
|
||||||
|
|
||||||
var r2 = radius*radius;
|
|
||||||
for (var i = 0; i <= radius;i++){
|
|
||||||
var newRadius = Math.round(Math.sqrt(r2 - i*i));
|
|
||||||
if (newRadius == lastRadius)
|
|
||||||
slices[slices.length-1][1]++;
|
|
||||||
else
|
|
||||||
slices.push([newRadius,1]);
|
|
||||||
lastRadius = newRadius;
|
|
||||||
}
|
|
||||||
this.chkpt('sphere');
|
|
||||||
//
|
|
||||||
// mid section
|
|
||||||
//
|
|
||||||
this.up(radius - slices[0][1])
|
|
||||||
.cylinder(block,radius,(slices[0][1]*2)-1,{blockType: bm[0],meta: bm[1]})
|
|
||||||
.down(radius-slices[0][1]);
|
|
||||||
|
|
||||||
var yOffset = -1;
|
|
||||||
for (var i = 1; i < slices.length;i++)
|
|
||||||
{
|
|
||||||
yOffset += slices[i-1][1];
|
|
||||||
var sr = slices[i][0];
|
|
||||||
var sh = slices[i][1];
|
|
||||||
var v = radius + yOffset, h = radius-sr;
|
|
||||||
// northern hemisphere
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
.left(h).back(h).down(v);
|
|
||||||
|
|
||||||
// southern hemisphere
|
|
||||||
v = radius - (yOffset+sh+1);
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
.left(h).back(h). down(v);
|
|
||||||
}
|
|
||||||
return this.move('sphere');
|
|
||||||
});
|
|
||||||
/************************************************************************
|
|
||||||
Drone.sphere0() method
|
|
||||||
======================
|
|
||||||
Creates an empty sphere.
|
|
||||||
|
|
||||||
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...
|
|
||||||
|
|
||||||
sphere0( blocks.iron, 10);
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
***/
|
|
||||||
Drone.extend('sphere0', function(block,radius)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
this.sphere(block,radius)
|
|
||||||
.fwd().right().up()
|
|
||||||
.sphere(0,radius-1)
|
|
||||||
.back().left().down();
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
var lastRadius = radius;
|
|
||||||
var slices = [[radius,0]];
|
|
||||||
var diameter = radius*2;
|
|
||||||
var bm = this._getBlockIdAndMeta(block);
|
|
||||||
|
|
||||||
var r2 = radius*radius;
|
|
||||||
for (var i = 0; i <= radius;i++){
|
|
||||||
var newRadius = Math.round(Math.sqrt(r2 - i*i));
|
|
||||||
if (newRadius == lastRadius)
|
|
||||||
slices[slices.length-1][1]++;
|
|
||||||
else
|
|
||||||
slices.push([newRadius,1]);
|
|
||||||
lastRadius = newRadius;
|
|
||||||
}
|
|
||||||
this.chkpt('sphere0');
|
|
||||||
//
|
|
||||||
// mid section
|
|
||||||
//
|
|
||||||
//.cylinder(block,radius,(slices[0][1]*2)-1,{blockType: bm[0],meta: bm[1]})
|
|
||||||
this.up(radius - slices[0][1])
|
|
||||||
.arc({blockType: bm[0],
|
|
||||||
meta: bm[1],
|
|
||||||
radius: radius,
|
|
||||||
strokeWidth: 2,
|
|
||||||
stack: (slices[0][1]*2)-1,
|
|
||||||
fill: false
|
|
||||||
})
|
|
||||||
.down(radius-slices[0][1]);
|
|
||||||
|
|
||||||
var yOffset = -1;
|
|
||||||
var len = slices.length;
|
|
||||||
for (var i = 1; i < len;i++)
|
|
||||||
{
|
|
||||||
yOffset += slices[i-1][1];
|
|
||||||
var sr = slices[i][0];
|
|
||||||
var sh = slices[i][1];
|
|
||||||
var v = radius + yOffset, h = radius-sr;
|
|
||||||
// northern hemisphere
|
|
||||||
// .cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.arc({
|
|
||||||
blockType: bm[0],
|
|
||||||
meta: bm[1],
|
|
||||||
radius: sr,
|
|
||||||
stack: sh,
|
|
||||||
fill: false,
|
|
||||||
strokeWidth: i<len-1?1+(sr-slices[i+1][0]):1
|
|
||||||
})
|
|
||||||
.left(h).back(h).down(v);
|
|
||||||
|
|
||||||
// southern hemisphere
|
|
||||||
v = radius - (yOffset+sh+1);
|
|
||||||
//.cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.arc({
|
|
||||||
blockType: bm[0],
|
|
||||||
meta: bm[1],
|
|
||||||
radius: sr,
|
|
||||||
stack: sh,
|
|
||||||
fill: false,
|
|
||||||
strokeWidth: i<len-1?1+(sr-slices[i+1][0]):1
|
|
||||||
})
|
|
||||||
.left(h).back(h). down(v);
|
|
||||||
}
|
|
||||||
this.move('sphere0');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
});
|
|
||||||
/************************************************************************
|
|
||||||
Drone.hemisphere() method
|
|
||||||
=========================
|
|
||||||
Creates a hemisphere. Hemispheres can be either north or south.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* block - the block the hemisphere will be made of.
|
|
||||||
* radius - the radius of the hemisphere
|
|
||||||
* northSouth - whether the hemisphere is 'north' or 'south'
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
To create a wood 'north' hemisphere with a radius of 7 blocks...
|
|
||||||
|
|
||||||
hemisphere(blocks.oak, 7, 'north');
|
|
||||||
|
|
||||||
![hemisphere example](img/hemisphereex1.png)
|
|
||||||
|
|
||||||
***/
|
|
||||||
Drone.extend('hemisphere', function(block,radius, northSouth){
|
|
||||||
var lastRadius = radius;
|
|
||||||
var slices = [[radius,0]];
|
|
||||||
var diameter = radius*2;
|
|
||||||
var bm = this._getBlockIdAndMeta(block);
|
|
||||||
|
|
||||||
var r2 = radius*radius;
|
|
||||||
for (var i = 0; i <= radius;i++){
|
|
||||||
var newRadius = Math.round(Math.sqrt(r2 - i*i));
|
|
||||||
if (newRadius == lastRadius)
|
|
||||||
slices[slices.length-1][1]++;
|
|
||||||
else
|
|
||||||
slices.push([newRadius,1]);
|
|
||||||
lastRadius = newRadius;
|
|
||||||
}
|
|
||||||
this.chkpt('hsphere');
|
|
||||||
//
|
|
||||||
// mid section
|
|
||||||
//
|
|
||||||
if (northSouth == "north"){
|
|
||||||
this.cylinder(block,radius,slices[0][1],{blockType: bm[0],meta: bm[1]});
|
|
||||||
} else {
|
|
||||||
this.up(radius - slices[0][1])
|
|
||||||
.cylinder(block,radius,slices[0][1],{blockType: bm[0],meta: bm[1]})
|
|
||||||
.down(radius - slices[0][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var yOffset = -1;
|
|
||||||
for (var i = 1; i < slices.length;i++)
|
|
||||||
{
|
|
||||||
yOffset += slices[i-1][1];
|
|
||||||
var sr = slices[i][0];
|
|
||||||
var sh = slices[i][1];
|
|
||||||
var v = yOffset, h = radius-sr;
|
|
||||||
if (northSouth == "north") {
|
|
||||||
// northern hemisphere
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
.left(h).back(h).down(v);
|
|
||||||
}else{
|
|
||||||
// southern hemisphere
|
|
||||||
v = radius - (yOffset+sh+1);
|
|
||||||
this.up(v).fwd(h).right(h)
|
|
||||||
.cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
|
|
||||||
.left(h).back(h). down(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.move('hsphere');
|
|
||||||
|
|
||||||
});
|
|
||||||
/************************************************************************
|
|
||||||
Drone.hemisphere0() method
|
|
||||||
=========================
|
|
||||||
Creates a hollow hemisphere. Hemispheres can be either north or south.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* block - the block the hemisphere will be made of.
|
|
||||||
* radius - the radius of the hemisphere
|
|
||||||
* northSouth - whether the hemisphere is 'north' or 'south'
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
To create a glass 'north' hemisphere with a radius of 20 blocks...
|
|
||||||
|
|
||||||
hemisphere0(blocks.glass, 20, 'north');
|
|
||||||
|
|
||||||
![hemisphere example](img/hemisphereex2.png)
|
|
||||||
|
|
||||||
***/
|
|
||||||
Drone.extend('hemisphere0', function(block,radius,northSouth){
|
|
||||||
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);
|
|
||||||
});
|
|
|
@ -1,139 +0,0 @@
|
||||||
var global = this;
|
|
||||||
/************************************************************************
|
|
||||||
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.
|
|
||||||
|
|
||||||
events.on() static method
|
|
||||||
=========================
|
|
||||||
This method is used to register event listeners.
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
* callback - A function which will be called whenever the event
|
|
||||||
fires. The callback should take 2 parameters, listener (the Bukkit
|
|
||||||
registered listener for this callback) and 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].
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
An org.bukkit.plugin.RegisteredListener object which can be used to
|
|
||||||
unregister the listener. This same object is passed to the callback
|
|
||||||
function each time the event is fired.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
------
|
|
||||||
The following code will print a message on screen every time a block is broken in the game
|
|
||||||
|
|
||||||
events.on("block.BlockBreakEvent", function(listener, evt){
|
|
||||||
echo (evt.player.name + " broke a block!");
|
|
||||||
});
|
|
||||||
|
|
||||||
To handle an event only once and unregister from further events...
|
|
||||||
|
|
||||||
events.on("block.BlockBreakEvent", function(listener, evt){
|
|
||||||
print (evt.player.name + " broke a block!");
|
|
||||||
evt.handlers.unregister(listener);
|
|
||||||
});
|
|
||||||
|
|
||||||
To unregister a listener *outside* of the listener function...
|
|
||||||
|
|
||||||
var myBlockBreakListener = events.on("block.BlockBreakEvent",function(l,e){ ... });
|
|
||||||
...
|
|
||||||
var handlers = org.bukkit.event.block.BlockBreakEvent.getHandlerList();
|
|
||||||
handlers.unregister(myBlockBreakListener);
|
|
||||||
|
|
||||||
[buk2]: http://wiki.bukkit.org/Event_API_Reference
|
|
||||||
[buk]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/event/Event.html
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
var events = events || {
|
|
||||||
//
|
|
||||||
// handle events in Minecraft
|
|
||||||
// --------------------------
|
|
||||||
// eventType can be a string (assumed to be a sub package of org.bukkit.event - e.g.
|
|
||||||
// if the string "block.BlockBreakEvent" is supplied then it's converted to the
|
|
||||||
// org.bukkit.event.block.BlockBreakEvent class . For custom event classes, just
|
|
||||||
// supply the custom event class e.g.
|
|
||||||
// events.on(net.yourdomain.events.YourCustomEvent,function(l,e){ ... });
|
|
||||||
//
|
|
||||||
on: function(
|
|
||||||
/* String or java Class */ eventType,
|
|
||||||
/* function( registeredListener, event) */ handler,
|
|
||||||
/* (optional) String (HIGH, HIGHEST, LOW, LOWEST, NORMAL, MONITOR), */ priority
|
|
||||||
){}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
// private implementation from here on in...
|
|
||||||
//
|
|
||||||
(function(){
|
|
||||||
if (events._eventsLoaded){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var _event = org.bukkit.event;
|
|
||||||
var _plugin = org.bukkit.plugin;
|
|
||||||
|
|
||||||
var _on = function(eventType, handler, priority)
|
|
||||||
{
|
|
||||||
if (typeof priority == "undefined"){
|
|
||||||
priority = _event.EventPriority.HIGHEST;
|
|
||||||
}else{
|
|
||||||
priority = _event.EventPriority[priority];
|
|
||||||
}
|
|
||||||
if (typeof eventType == "string"){
|
|
||||||
var subPkgs = eventType.split('.');
|
|
||||||
eventType = _event[subPkgs[0]];
|
|
||||||
for (var i = 1;i < subPkgs.length; i++){
|
|
||||||
eventType = eventType[subPkgs[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var handlerList = eventType.getHandlerList();
|
|
||||||
var listener = {};
|
|
||||||
var eventExecutor = new _plugin.EventExecutor(){
|
|
||||||
execute: function(l,e){
|
|
||||||
handler(listener.reg,e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
listener.reg = new _plugin.RegisteredListener(
|
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
__plugin
|
|
||||||
,eventExecutor
|
|
||||||
,priority
|
|
||||||
,__plugin
|
|
||||||
,true
|
|
||||||
)
|
|
||||||
handlerList.register(listener.reg);
|
|
||||||
return listener.reg;
|
|
||||||
};
|
|
||||||
events.on = _on;
|
|
||||||
events._eventsLoaded = true;
|
|
||||||
}());
|
|
|
@ -1,80 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
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 fireworks.firework(self.location);
|
|
||||||
|
|
||||||
![firework example](img/firework.png)
|
|
||||||
|
|
||||||
***/
|
|
||||||
plugin("fireworks", {
|
|
||||||
/*
|
|
||||||
create a firework at the given location
|
|
||||||
*/
|
|
||||||
firework: function(location){
|
|
||||||
importPackage(org.bukkit.entity);
|
|
||||||
importPackage(org.bukkit);
|
|
||||||
|
|
||||||
var randInt = function(n){
|
|
||||||
return Math.floor(Math.random() * n);
|
|
||||||
};
|
|
||||||
var getColor = function(i){
|
|
||||||
var colors = [
|
|
||||||
Color.AQUA, Color.BLACK, Color.BLUE, Color.FUCHSIA, Color.GRAY,
|
|
||||||
Color.GREEN, Color.LIME, Color.MAROON, Color.NAVY, Color.OLIVE,
|
|
||||||
Color.ORANGE, Color.PURPLE, Color.RED, Color.SILVER, Color.TEAL,
|
|
||||||
Color.WHITE, Color.YELLOW];
|
|
||||||
return colors[i];
|
|
||||||
};
|
|
||||||
var fw = location.world.spawnEntity(location, EntityType.FIREWORK);
|
|
||||||
var fwm = fw.getFireworkMeta();
|
|
||||||
var fwTypes = [FireworkEffect.Type.BALL,
|
|
||||||
FireworkEffect.Type.BALL_LARGE,
|
|
||||||
FireworkEffect.Type.BURST,
|
|
||||||
FireworkEffect.Type.CREEPER,
|
|
||||||
FireworkEffect.Type.STAR];
|
|
||||||
var type = fwTypes[randInt(5)];
|
|
||||||
|
|
||||||
var r1i = randInt(17);
|
|
||||||
var r2i = randInt(17);
|
|
||||||
var c1 = getColor(r1i);
|
|
||||||
var c2 = getColor(r2i);
|
|
||||||
var effectBuilder = FireworkEffect.builder()
|
|
||||||
.flicker(Math.round(Math.random())==0)
|
|
||||||
.withColor(c1)
|
|
||||||
.withFade(c2).trail(Math.round(Math.random())==0);
|
|
||||||
effectBuilder['with'](type);
|
|
||||||
var effect = effectBuilder.build();
|
|
||||||
fwm.addEffect(effect);
|
|
||||||
fwm.setPower(randInt(2)+1);
|
|
||||||
fw.setFireworkMeta(fwm);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Drone.extend('firework',function()
|
|
||||||
{
|
|
||||||
fireworks.firework(this.getLocation());
|
|
||||||
});
|
|
|
@ -1,281 +0,0 @@
|
||||||
/*
|
|
||||||
The homes plugin lets players set a location as home and return to the location, invite
|
|
||||||
other players to their home and also visit other player's homes.
|
|
||||||
*/
|
|
||||||
plugin("homes", {
|
|
||||||
help: function(){
|
|
||||||
return [
|
|
||||||
/* basic functions */
|
|
||||||
"/jsp home : Return to your own home",
|
|
||||||
"/jsp home <player> : Go to player's home",
|
|
||||||
"/jsp home set : Set your current location as home",
|
|
||||||
"/jsp home delete : Delete your home location",
|
|
||||||
|
|
||||||
/* social */
|
|
||||||
"/jsp home list : List homes you can visit",
|
|
||||||
"/jsp home ilist : List players who can visit your home",
|
|
||||||
"/jsp home invite <player> : Invite <player> to your home",
|
|
||||||
"/jsp home uninvite <player> : Uninvite <player> to your home",
|
|
||||||
"/jsp home public : Open your home to all players",
|
|
||||||
"/jsp home private : Make your home private",
|
|
||||||
|
|
||||||
/* administration */
|
|
||||||
"/jsp home listall : Show all houses (ops only)",
|
|
||||||
"/jsp home clear <player> : Clears player's home location (ops only)"
|
|
||||||
];
|
|
||||||
},
|
|
||||||
/* ========================================================================
|
|
||||||
basic functions
|
|
||||||
======================================================================== */
|
|
||||||
|
|
||||||
go: function(guest, host){
|
|
||||||
if (typeof host == "undefined")
|
|
||||||
host = guest;
|
|
||||||
guest = utils.getPlayerObject(guest);
|
|
||||||
host = utils.getPlayerObject(host);
|
|
||||||
var loc = this.store.houses[host.name];
|
|
||||||
if (!loc){
|
|
||||||
guest.sendMessage(host.name + " has no home");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this._canVisit(guest,host)){
|
|
||||||
guest.sendMessage("You can't visit " + host.name + "'s home yet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var worldName = loc[0], x = loc[1], y = loc[2], z=loc[3], yaw=loc[4];
|
|
||||||
var teleportCause = org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
|
||||||
var homeLoc = new org.bukkit.Location(org.bukkit.Bukkit.getWorld(worldName),x,y,z,yaw,0);
|
|
||||||
guest.teleport(homeLoc, teleportCause.PLUGIN);
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
determine whether a guest is allow visit a host's home
|
|
||||||
*/
|
|
||||||
_canVisit: function(guest, host){
|
|
||||||
if (guest == host)
|
|
||||||
return true;
|
|
||||||
if (this.store.openHouses[host.name])
|
|
||||||
return true;
|
|
||||||
var invitations = this.store.invites[host.name];
|
|
||||||
if (invitations)
|
|
||||||
for (var i = 0;i < invitations.length;i++)
|
|
||||||
if (invitations[i] == guest.name)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
set: function(player){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
var loc = player.location;
|
|
||||||
this.store.houses[player.name] = [""+loc.world.name
|
|
||||||
,Math.floor(loc.x)
|
|
||||||
,Math.floor(loc.y)
|
|
||||||
,Math.floor(loc.z)
|
|
||||||
,Math.floor(loc.yaw)
|
|
||||||
,Math.floor(loc.pitch)];
|
|
||||||
},
|
|
||||||
remove: function(player){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
delete this.store.houses[player.name];
|
|
||||||
},
|
|
||||||
/* ========================================================================
|
|
||||||
social functions
|
|
||||||
======================================================================== */
|
|
||||||
|
|
||||||
/*
|
|
||||||
list homes which the player can visit
|
|
||||||
*/
|
|
||||||
list: function(player){
|
|
||||||
var result = [];
|
|
||||||
for (var ohp in this.store.openHouses)
|
|
||||||
result.push(ohp);
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
for (var host in this.store.invites){
|
|
||||||
var guests = this.store.invites[host];
|
|
||||||
for (var i = 0;i < guests.length; i++)
|
|
||||||
if (guests[i] == player.name)
|
|
||||||
result.push(host);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
list who can visit the player's home
|
|
||||||
*/
|
|
||||||
ilist: function(player){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
var result = [];
|
|
||||||
// if home is public - all players
|
|
||||||
if (this.store.openHouses[player.name]){
|
|
||||||
var online = org.bukkit.Bukkit.getOnlinePlayers();
|
|
||||||
for (var i = 0;i < online.length; i++)
|
|
||||||
if (online[i].name != player.name)
|
|
||||||
result.push(online[i].name);
|
|
||||||
}else{
|
|
||||||
result = this.store.invites[player.name] || [];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
Invite a player to the home
|
|
||||||
*/
|
|
||||||
invite: function(host, guest){
|
|
||||||
host = utils.getPlayerObject(host);
|
|
||||||
guest = utils.getPlayerObject(guest);
|
|
||||||
var invitations = this.store.invites[host.name] || [];
|
|
||||||
invitations.push(guest.name);
|
|
||||||
this.store.invites[host.name] = invitations;
|
|
||||||
guest.sendMessage(host.name + " has invited you to their home.");
|
|
||||||
guest.sendMessage("type '/jsp home " + host.name + "' to accept");
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
Uninvite someone to the home
|
|
||||||
*/
|
|
||||||
uninvite: function(host, guest){
|
|
||||||
host = utils.getPlayerObject(host);
|
|
||||||
guest = utils.getPlayerObject(guest);
|
|
||||||
var invitations = this.store.invites[host.name];
|
|
||||||
if (!invitations)
|
|
||||||
return;
|
|
||||||
var revisedInvites = [];
|
|
||||||
for (var i =0;i < invitations.length; i++)
|
|
||||||
if (invitations[i] != guest.name)
|
|
||||||
revisedInvites.push(invitations[i]);
|
|
||||||
this.store.invites[host.name] = revisedInvites;
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
make the player's house public
|
|
||||||
*/
|
|
||||||
open: function(player, optionalMsg){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
this.store.openHouses[player.name] = true;
|
|
||||||
if (typeof optionalMsg != "undefined")
|
|
||||||
__plugin.server.broadcastMessage(optionalMsg);
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
make the player's house private
|
|
||||||
*/
|
|
||||||
close: function(player){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
delete this.store.openHouses[player.name];
|
|
||||||
},
|
|
||||||
/* ========================================================================
|
|
||||||
admin functions
|
|
||||||
======================================================================== */
|
|
||||||
listall: function(){
|
|
||||||
var result = [];
|
|
||||||
for (var home in this.store.houses)
|
|
||||||
result.push(home);
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
clear: function(player){
|
|
||||||
player = utils.getPlayerObject(player);
|
|
||||||
delete this.store.houses[player.name];
|
|
||||||
delete this.store.openHouses[player.name];
|
|
||||||
}
|
|
||||||
|
|
||||||
}, true);
|
|
||||||
/*
|
|
||||||
private implementation
|
|
||||||
*/
|
|
||||||
(function(){
|
|
||||||
/*
|
|
||||||
define a set of command options that can be used by players
|
|
||||||
*/
|
|
||||||
var options = {
|
|
||||||
set: function(){homes.set();},
|
|
||||||
'delete': function(){ homes.remove();},
|
|
||||||
help: function(){ self.sendMessage(homes.help());},
|
|
||||||
list: function(){
|
|
||||||
var visitable = homes.list();
|
|
||||||
if (visitable.length == 0){
|
|
||||||
self.sendMessage("There are no homes to visit");
|
|
||||||
return;
|
|
||||||
}else{
|
|
||||||
self.sendMessage([
|
|
||||||
"You can visit any of these " + visitable.length + " homes"
|
|
||||||
,visitable.join(", ")
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ilist: function(){
|
|
||||||
var potentialVisitors = homes.ilist();
|
|
||||||
if (potentialVisitors.length == 0)
|
|
||||||
self.sendMessage("No one can visit your home");
|
|
||||||
else
|
|
||||||
self.sendMessage([
|
|
||||||
"These " + potentialVisitors.length + "players can visit your home",
|
|
||||||
potentialVisitors.join(", ")]);
|
|
||||||
},
|
|
||||||
invite: function(params){
|
|
||||||
if (params.length == 1){
|
|
||||||
self.sendMessage("You must provide a player's name");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var playerName = params[1];
|
|
||||||
var guest = utils.getPlayerObject(playerName);
|
|
||||||
if (!guest)
|
|
||||||
self.sendMessage(playerName + " is not here");
|
|
||||||
else
|
|
||||||
homes.invite(self,guest);
|
|
||||||
},
|
|
||||||
uninvite: function(params){
|
|
||||||
if (params.length == 1){
|
|
||||||
self.sendMessage("You must provide a player's name");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var playerName = params[1];
|
|
||||||
var guest = utils.getPlayerObject(playerName);
|
|
||||||
if (!guest)
|
|
||||||
self.sendMessage(playerName + " is not here");
|
|
||||||
else
|
|
||||||
homes.uninvite(self,guest);
|
|
||||||
},
|
|
||||||
'public': function(params){
|
|
||||||
homes.open(self,params.slice(1).join(' '));
|
|
||||||
self.sendMessage("Your home is open to the public");
|
|
||||||
},
|
|
||||||
'private': function(){
|
|
||||||
homes.close();
|
|
||||||
self.sendMessage("Your home is closed to the public");
|
|
||||||
},
|
|
||||||
listall: function(){
|
|
||||||
if (!self.isOp())
|
|
||||||
self.sendMessage("Only operators can do this");
|
|
||||||
else
|
|
||||||
self.sendMessage(homes.listall().join(", "));
|
|
||||||
},
|
|
||||||
clear: function(params){
|
|
||||||
if (!self.isOp())
|
|
||||||
self.sendMessage("Only operators can do this");
|
|
||||||
else
|
|
||||||
homes.clear(params[1]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var optionList = [];
|
|
||||||
for (var o in options)
|
|
||||||
optionList.push(o);
|
|
||||||
/*
|
|
||||||
Expose a set of commands that players can use at the in-game command prompt
|
|
||||||
*/
|
|
||||||
command("home", function(params){
|
|
||||||
if (params.length == 0){
|
|
||||||
homes.go();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var option = options[params[0]];
|
|
||||||
if (option)
|
|
||||||
option(params);
|
|
||||||
else{
|
|
||||||
var host = utils.getPlayerObject(params[0]);
|
|
||||||
if (!host)
|
|
||||||
self.sendMessage(params[0] + " is not here");
|
|
||||||
else
|
|
||||||
homes.go(self,host);
|
|
||||||
}
|
|
||||||
},optionList);
|
|
||||||
|
|
||||||
/*
|
|
||||||
initialize the store
|
|
||||||
*/
|
|
||||||
homes.store.houses = homes.store.houses || {};
|
|
||||||
homes.store.openHouses = homes.store.openHouses || {};
|
|
||||||
homes.store.invites = homes.store.invites || {};
|
|
||||||
}());
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*************************************************************************
|
|
||||||
http.request() function
|
|
||||||
====================
|
|
||||||
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.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
* 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
|
|
||||||
-------
|
|
||||||
The following example illustrates how to use http.request to make a request to a JSON web service and evaluate its response...
|
|
||||||
|
|
||||||
var jsResponse;
|
|
||||||
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...
|
|
||||||
|
|
||||||
http.request({ url: "http://pixenate.com/pixenate/pxn8.pl",
|
|
||||||
method: "POST",
|
|
||||||
params: {script: "[]"}
|
|
||||||
}, function( responseCode, responseBody){
|
|
||||||
var jsObj = eval("(" + responseBody + ")");
|
|
||||||
});
|
|
||||||
|
|
||||||
***/
|
|
||||||
var http = http || {};
|
|
||||||
|
|
||||||
http.request = function( request, callback)
|
|
||||||
{
|
|
||||||
var paramsToString = function(params){
|
|
||||||
var result = "";
|
|
||||||
var paramNames = [];
|
|
||||||
for (var i in params){
|
|
||||||
paramNames.push(i);
|
|
||||||
}
|
|
||||||
for (var 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()
|
|
||||||
{
|
|
||||||
var url, paramsAsString, conn, requestMethod;
|
|
||||||
if (typeof request === "string"){
|
|
||||||
url = request;
|
|
||||||
requestMethod = "GET";
|
|
||||||
}else{
|
|
||||||
paramsAsString = paramsToString(request.params);
|
|
||||||
requestMethod = request.method || "GET";
|
|
||||||
if (requestMethod == "GET" && request.params){
|
|
||||||
// append each parameter to the URL
|
|
||||||
url = request.url + "?" + paramsAsString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn = new java.net.URL(url).openConnection();
|
|
||||||
conn.requestMethod = requestMethod;
|
|
||||||
conn.doOutput = true;
|
|
||||||
conn.instanceFollowRedirects = false;
|
|
||||||
|
|
||||||
if (conn.requestMethod == "POST"){
|
|
||||||
conn.doInput = true;
|
|
||||||
// put each parameter in the outputstream
|
|
||||||
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 ());
|
|
||||||
wr.writeBytes(paramsAsString);
|
|
||||||
wr.flush();
|
|
||||||
wr.close();
|
|
||||||
}
|
|
||||||
var rc = conn.responseCode;
|
|
||||||
var response;
|
|
||||||
var stream;
|
|
||||||
if (rc == 200){
|
|
||||||
stream = conn.getInputStream();
|
|
||||||
response = new java.util.Scanner(stream).useDelimiter("\\A").next();
|
|
||||||
}
|
|
||||||
server.scheduler.runTask(__plugin,function(){
|
|
||||||
callback(rc,response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
A basic number-guessing game that uses the Bukkit Conversation API.
|
|
||||||
*/
|
|
||||||
ready(function(){
|
|
||||||
|
|
||||||
global.GuessTheNumber = function()
|
|
||||||
{
|
|
||||||
importPackage(org.bukkit.conversations);
|
|
||||||
|
|
||||||
var number = Math.ceil(Math.random() * 10);
|
|
||||||
|
|
||||||
var prompt = new Prompt()
|
|
||||||
{
|
|
||||||
getPromptText: function(ctx){
|
|
||||||
var hint = "";
|
|
||||||
var h = ctx.getSessionData("hint");
|
|
||||||
if (h){
|
|
||||||
hint = h;
|
|
||||||
}
|
|
||||||
return hint + "Think of a number between 1 and 10";
|
|
||||||
},
|
|
||||||
acceptInput: function(ctx, s)
|
|
||||||
{
|
|
||||||
s = s.replace(/^[^0-9]+/,""); // strip leading non-numeric characters (e.g. '/' )
|
|
||||||
s = parseInt(s);
|
|
||||||
if (s == number){
|
|
||||||
setTimeout(function(){
|
|
||||||
ctx.forWhom.sendRawMessage("You guessed Correct!");
|
|
||||||
},100);
|
|
||||||
return null;
|
|
||||||
}else{
|
|
||||||
if (s < number)
|
|
||||||
ctx.setSessionData("hint","too low\n");
|
|
||||||
if (s > number)
|
|
||||||
ctx.setSessionData("hint","too high\n");
|
|
||||||
return prompt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
blocksForInput: function(ctx){ return true; }
|
|
||||||
};
|
|
||||||
var cf = new ConversationFactory(__plugin);
|
|
||||||
var conv = cf.withModality(true)
|
|
||||||
.withFirstPrompt(prompt)
|
|
||||||
.withPrefix(new ConversationPrefix(){ getPrefix: function(ctx){ return "[1-10] ";} })
|
|
||||||
.buildConversation(self);
|
|
||||||
conv.begin();
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,180 +0,0 @@
|
||||||
load(__folder + "../events/events.js");
|
|
||||||
/*
|
|
||||||
OK - this is a rough and ready prototype of a simple multi-player shoot-em-up.
|
|
||||||
Get a bunch of players in close proximity and issue the following commands...
|
|
||||||
|
|
||||||
/js var redTeam = ['<player1>','<player2>',...etc]
|
|
||||||
/js var blueTeam = ['<player3>','<player4>,...etc]
|
|
||||||
/js var greenTeam = ['<player5>','<player6>,...etc]
|
|
||||||
/js new SnowBallFight({red: redTeam,blue: blueTeam,green: greenTeam},60).start();
|
|
||||||
|
|
||||||
Alternatively you can just have all players play against each other...
|
|
||||||
|
|
||||||
/js new SnowBallFight(['player1','player2','player3'],60).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. Create a small arena
|
|
||||||
with a couple of small buildings for cover to make the game more fun :-)
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
var SnowBallFight = function(teams, duration){};
|
|
||||||
SnowBallFight.prototype.start = function(){};
|
|
||||||
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/*
|
|
||||||
setup game
|
|
||||||
*/
|
|
||||||
var _startGame = function(gameState){
|
|
||||||
// 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 (var i = 10;i < gameState.duration;i+=10)
|
|
||||||
gameState.ammo.push(gameState.ammo[0]);
|
|
||||||
|
|
||||||
for (var teamName in gameState.teams)
|
|
||||||
{
|
|
||||||
gameState.teamScores[teamName] = 0;
|
|
||||||
var team = gameState.teams[teamName];
|
|
||||||
for (var i = 0;i < team.length;i++) {
|
|
||||||
var player = server.getPlayer(team[i]);
|
|
||||||
gameState.savedModes[player.name] = player.gameMode;
|
|
||||||
player.gameMode = org.bukkit.GameMode.SURVIVAL;
|
|
||||||
player.inventory.addItem(gameState.ammo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
end the game
|
|
||||||
*/
|
|
||||||
var _endGame = function(gameState){
|
|
||||||
var scores = [];
|
|
||||||
|
|
||||||
var leaderBoard = [];
|
|
||||||
for (var tn in gameState.teamScores){
|
|
||||||
leaderBoard.push([tn,gameState.teamScores[tn]]);
|
|
||||||
}
|
|
||||||
leaderBoard.sort(function(a,b){ return b[1] - a[1];});
|
|
||||||
|
|
||||||
for (var i = 0;i < leaderBoard.length; i++){
|
|
||||||
scores.push("Team " + leaderBoard[i][0] + " scored " + leaderBoard[i][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var teamName in gameState.teams) {
|
|
||||||
var team = gameState.teams[teamName];
|
|
||||||
for (var i = 0;i < team.length;i++) {
|
|
||||||
// restore player's previous game mode and take back snowballs
|
|
||||||
var player = server.getPlayer(team[i]);
|
|
||||||
player.gameMode = gameState.savedModes[player.name];
|
|
||||||
player.inventory.removeItem(gameState.ammo);
|
|
||||||
player.sendMessage("GAME OVER.");
|
|
||||||
player.sendMessage(scores);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var handlerList = org.bukkit.event.entity.EntityDamageByEntityEvent.getHandlerList();
|
|
||||||
handlerList.unregister(gameState.listener);
|
|
||||||
gameState.inProgress = false;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
get the team the player belongs to
|
|
||||||
*/
|
|
||||||
var _getTeam = function(player,pteams) {
|
|
||||||
for (var teamName in pteams) {
|
|
||||||
var team = pteams[teamName];
|
|
||||||
for (var i = 0;i < team.length; i++)
|
|
||||||
if (team[i] == player.name)
|
|
||||||
return teamName;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
construct a new game
|
|
||||||
*/
|
|
||||||
var _constructor = function(duration, teams) {
|
|
||||||
|
|
||||||
var _snowBalls = new org.bukkit.inventory.ItemStack(org.bukkit.Material.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 = [];
|
|
||||||
var players = server.onlinePlayers;
|
|
||||||
for (var 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 (var 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(l,event){
|
|
||||||
var snowball = event.damager;
|
|
||||||
if (!snowball || !(snowball instanceof org.bukkit.entity.Snowball))
|
|
||||||
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.on("entity.EntityDamageByEntityEvent",_onSnowballHit);
|
|
||||||
new java.lang.Thread(function(){
|
|
||||||
while (_gameState.duration--)
|
|
||||||
java.lang.Thread.sleep(1000); // sleep 1,000 millisecs (1 second)
|
|
||||||
_endGame(_gameState);
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
SnowBallFight = _constructor;
|
|
||||||
|
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
Define the signs module - signs are persistent
|
|
||||||
(that is - a menu sign will still be a menu after th
|
|
||||||
server has shut down and started up) plugins now have persistent state - Yay!
|
|
||||||
*/
|
|
||||||
var signs = 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 ){}
|
|
||||||
/*
|
|
||||||
more to come - clocks
|
|
||||||
*/
|
|
||||||
},true);
|
|
||||||
/*
|
|
||||||
private implementation
|
|
||||||
*/
|
|
||||||
(function(){
|
|
||||||
/*
|
|
||||||
redraw a menu sign
|
|
||||||
*/
|
|
||||||
var _redrawMenuSign = function(p_sign,p_selectedIndex,p_displayOptions)
|
|
||||||
{
|
|
||||||
var optLen = p_displayOptions.length;
|
|
||||||
// the offset is where the menu window begins
|
|
||||||
var offset = Math.max(0, Math.min(optLen-3, Math.floor(p_selectedIndex/3) * 3));
|
|
||||||
for (var i = 0;i < 3; i++){
|
|
||||||
var text = "";
|
|
||||||
if (offset+i < optLen)
|
|
||||||
text = p_displayOptions[offset+i];
|
|
||||||
if (offset+i == p_selectedIndex)
|
|
||||||
text = ("" + text).replace(/^ /,">");
|
|
||||||
p_sign.setLine(i+1,text);
|
|
||||||
}
|
|
||||||
p_sign.update(true);
|
|
||||||
};
|
|
||||||
signs._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)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (typeof selectedIndex == "undefined")
|
|
||||||
selectedIndex = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// variables common to all instances of this menu can go here
|
|
||||||
//
|
|
||||||
var labelPadding = "---------------";
|
|
||||||
var optionPadding = " ";
|
|
||||||
|
|
||||||
var paddedLabel = (labelPadding+label+labelPadding).substr(((label.length+30)/2)-7,15);
|
|
||||||
var optLen = options.length;
|
|
||||||
var displayOptions = [];
|
|
||||||
for (var i =0;i < options.length;i++){
|
|
||||||
displayOptions[i] = (" " + options[i] + optionPadding).substring(0,15);
|
|
||||||
}
|
|
||||||
|
|
||||||
var theSigns = this;
|
|
||||||
|
|
||||||
/*
|
|
||||||
this function is returned by signs.menu and when it is invoked it will
|
|
||||||
attach menu behaviour to an existing sign in the world.
|
|
||||||
signs.menu is for use by Plugin Authors.
|
|
||||||
The function returned by signs.menu is for use by admins/ops.
|
|
||||||
*/
|
|
||||||
var convertToMenuSign = function(/* Sign */ sign, save)
|
|
||||||
{
|
|
||||||
if (typeof save == "undefined")
|
|
||||||
save = true;
|
|
||||||
|
|
||||||
if (typeof sign == "undefined"){
|
|
||||||
var mouseLoc = getMousePos();
|
|
||||||
if (mouseLoc){
|
|
||||||
sign = mouseLoc.block.state;
|
|
||||||
}else{
|
|
||||||
throw new Exception("You must provide a sign!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// per-sign variables go here
|
|
||||||
//
|
|
||||||
var cSelectedIndex = selectedIndex;
|
|
||||||
sign.setLine(0,paddedLabel.bold());
|
|
||||||
var _updateSign = function(p_player,p_sign) {
|
|
||||||
cSelectedIndex = (cSelectedIndex+1) % optLen;
|
|
||||||
_redrawMenuSign(p_sign,cSelectedIndex,displayOptions);
|
|
||||||
var signSelectionEvent = {player: p_player,
|
|
||||||
sign: p_sign,
|
|
||||||
text: options[cSelectedIndex],
|
|
||||||
number:cSelectedIndex};
|
|
||||||
|
|
||||||
callback(signSelectionEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
get a unique ID for this particular sign instance
|
|
||||||
*/
|
|
||||||
var signLoc = sign.block.location;
|
|
||||||
var menuSignSaveData = [""+signLoc.world.name, signLoc.x,signLoc.y,signLoc.z];
|
|
||||||
var menuSignUID = JSON.stringify(menuSignSaveData);
|
|
||||||
/*
|
|
||||||
keep a reference to the update function for use by the event handler
|
|
||||||
*/
|
|
||||||
theSigns._updaters[menuSignUID] = _updateSign;
|
|
||||||
|
|
||||||
// initialize the sign
|
|
||||||
_redrawMenuSign(sign,cSelectedIndex,displayOptions);
|
|
||||||
|
|
||||||
/*
|
|
||||||
whenever a sign is placed somewhere in the world
|
|
||||||
(which is what this function does)
|
|
||||||
save its location for loading and initialization
|
|
||||||
when the server starts up again.
|
|
||||||
*/
|
|
||||||
if (save){
|
|
||||||
if (typeof theSigns.store.menus == "undefined")
|
|
||||||
theSigns.store.menus = {};
|
|
||||||
var signLocations = theSigns.store.menus[label];
|
|
||||||
if (typeof signLocations == "undefined")
|
|
||||||
signLocations = theSigns.store.menus[label] = [];
|
|
||||||
signLocations.push(menuSignSaveData);
|
|
||||||
}
|
|
||||||
return sign;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
a new sign definition - need to store (in-memory only)
|
|
||||||
it's behaviour and bring back to life other signs of the
|
|
||||||
same type in the world. Look for other static signs in the
|
|
||||||
world with this same label and make dynamic again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (this.store.menus && this.store.menus[label])
|
|
||||||
{
|
|
||||||
var signsOfSameLabel = this.store.menus[label];
|
|
||||||
var defragged = [];
|
|
||||||
var len = signsOfSameLabel.length;
|
|
||||||
for (var i = 0; i < len ; i++)
|
|
||||||
{
|
|
||||||
var loc = signsOfSameLabel[i];
|
|
||||||
var world = org.bukkit.Bukkit.getWorld(loc[0]);
|
|
||||||
if (!world)
|
|
||||||
continue;
|
|
||||||
var block = world.getBlockAt(loc[1],loc[2],loc[3]);
|
|
||||||
if (block.state instanceof org.bukkit.block.Sign){
|
|
||||||
convertToMenuSign(block.state,false);
|
|
||||||
defragged.push(loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
remove data for signs which no longer exist.
|
|
||||||
*/
|
|
||||||
if (defragged.length != len){
|
|
||||||
this.store.menus[label] = defragged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return convertToMenuSign;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
All dependecies ( 'events' module ) have loaded
|
|
||||||
*/
|
|
||||||
ready(function(){
|
|
||||||
//
|
|
||||||
// Usage:
|
|
||||||
// In game, create a sign , target it and type /js signs.testMenu()
|
|
||||||
//
|
|
||||||
signs.testMenu = signs.menu(
|
|
||||||
"Dinner",
|
|
||||||
["Lamb","Pork","Chicken","Duck","Beef"],
|
|
||||||
function(event){
|
|
||||||
event.player.sendMessage("You chose " + event.text);
|
|
||||||
});
|
|
||||||
//
|
|
||||||
// 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 signs.timeOfDay()
|
|
||||||
//
|
|
||||||
signs.timeOfDay = signs.menu(
|
|
||||||
"Time",
|
|
||||||
["Dawn","Midday","Dusk","Midnight"],
|
|
||||||
function(event){
|
|
||||||
event.player.location.world.setTime( event.number * 6000 );
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
|
||||||
// update it every time player interacts with it.
|
|
||||||
//
|
|
||||||
events.on("player.PlayerInteractEvent",function(listener, 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 org.bukkit.block.Sign)
|
|
||||||
return;
|
|
||||||
var evtLocStr = utils.locationToString(event.clickedBlock.location);
|
|
||||||
var signUpdater = signs._updaters[evtLocStr]
|
|
||||||
if (signUpdater)
|
|
||||||
signUpdater(event.player, event.clickedBlock.state);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
String class extensions
|
|
||||||
-----------------------
|
|
||||||
The following chat-formatting methods are added to the javascript String class..
|
|
||||||
|
|
||||||
* black()
|
|
||||||
* darkblue()
|
|
||||||
* blue()
|
|
||||||
* darkgreen()
|
|
||||||
* darkaqua()
|
|
||||||
* darkred()
|
|
||||||
* purple()
|
|
||||||
* gold()
|
|
||||||
* gray()
|
|
||||||
* darkgray()
|
|
||||||
* indigo()
|
|
||||||
* brightgreen()
|
|
||||||
* green()
|
|
||||||
* aqua()
|
|
||||||
* red()
|
|
||||||
* pink()
|
|
||||||
* yellow()
|
|
||||||
* white()
|
|
||||||
* bold()
|
|
||||||
* random()
|
|
||||||
* strike()
|
|
||||||
* underline()
|
|
||||||
* italic()
|
|
||||||
* reset()
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
var boldGoldText = "Hello World".bold().gold();
|
|
||||||
self.sendMessage(boldGoldText);
|
|
||||||
|
|
||||||
<p style="color:gold;font-weight:bold">Hello World</p>
|
|
||||||
|
|
||||||
***/
|
|
||||||
(function(){
|
|
||||||
var formattingCodes = {
|
|
||||||
black: 0,
|
|
||||||
darkblue: 1,
|
|
||||||
blue: 1,
|
|
||||||
darkgreen: 2,
|
|
||||||
darkaqua: 3,
|
|
||||||
darkred: 4,
|
|
||||||
purple: 5,
|
|
||||||
gold: 6,
|
|
||||||
gray: 7,
|
|
||||||
darkgray: 8,
|
|
||||||
indigo: 9,
|
|
||||||
brightgreen: 'a',
|
|
||||||
green: 'a',
|
|
||||||
aqua: 'b',
|
|
||||||
red: 'c',
|
|
||||||
pink: 'd',
|
|
||||||
yellow: 'e',
|
|
||||||
white: 'f',
|
|
||||||
bold: 'l',
|
|
||||||
random:'k',
|
|
||||||
strike: 'm',
|
|
||||||
underline: 'n',
|
|
||||||
italic: 'o',
|
|
||||||
reset: 'r'
|
|
||||||
};
|
|
||||||
for (var method in formattingCodes){
|
|
||||||
String.prototype[method] = function(m,c){
|
|
||||||
return function(){ return "§"+c + this;};
|
|
||||||
}(method,formattingCodes[method]);
|
|
||||||
}
|
|
||||||
}());
|
|
|
@ -1,209 +0,0 @@
|
||||||
/************************************************************************
|
|
||||||
Utilities Module
|
|
||||||
================
|
|
||||||
Miscellaneous utility functions and classes to help with programming.
|
|
||||||
|
|
||||||
* locationToString(Location) - returns a bukkit Location object in string form.
|
|
||||||
|
|
||||||
* getPlayerObject(playerName) - returns the Player object for a named
|
|
||||||
player or `self` if no name is provided.
|
|
||||||
|
|
||||||
***/
|
|
||||||
var utils = utils || {
|
|
||||||
locationToString: function(location){
|
|
||||||
return JSON.stringify([""+location.world.name,location.x, location.y, location.z]);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPlayerObject: function(playerName){
|
|
||||||
if (typeof playerName == "undefined")
|
|
||||||
return self;
|
|
||||||
if (typeof playerName == "string")
|
|
||||||
return org.bukkit.Bukkit.getPlayer(playerName);
|
|
||||||
return 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.
|
|
||||||
|
|
||||||
[sched]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/package-summary.html
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* 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....
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
* object (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).
|
|
||||||
|
|
||||||
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
|
|
||||||
-------
|
|
||||||
The following example illustrates how to use foreach for immediate processing of an array...
|
|
||||||
|
|
||||||
var players = ["moe", "larry", "curly"];
|
|
||||||
utils.foreach (players, function(item){
|
|
||||||
server.getPlayer(item).sendMessage("Hi " + item);
|
|
||||||
});
|
|
||||||
|
|
||||||
... 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...
|
|
||||||
|
|
||||||
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...
|
|
||||||
|
|
||||||
// build a structure 200 wide x 200 tall x 200 long
|
|
||||||
// (That's 8 Million Blocks - enough to tax any machine!)
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
***/
|
|
||||||
foreach: function(array, callback, object, delay, onCompletion) {
|
|
||||||
if (array instanceof java.util.Collection)
|
|
||||||
array = array.toArray();
|
|
||||||
var i = 0;
|
|
||||||
var len = array.length;
|
|
||||||
if (delay){
|
|
||||||
var next = function(){ callback(array[i],i,object,array); i++;};
|
|
||||||
var hasNext = function(){return i < len;};
|
|
||||||
utils.nicely(next,hasNext,onCompletion,delay);
|
|
||||||
}else{
|
|
||||||
for (;i < len; i++){
|
|
||||||
callback(array[i],i,object,array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/************************************************************************
|
|
||||||
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.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
See the source code to utils.foreach for an example of how utils.nicely is used.
|
|
||||||
|
|
||||||
***/
|
|
||||||
nicely: function(next, hasNext, onDone, delay){
|
|
||||||
if (hasNext()){
|
|
||||||
next();
|
|
||||||
server.scheduler.runTaskLater(__plugin,function(){
|
|
||||||
utils.nicely(next,hasNext,onDone,delay);
|
|
||||||
},delay);
|
|
||||||
}else{
|
|
||||||
if (onDone)
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/************************************************************************
|
|
||||||
utils.at() function
|
|
||||||
===================
|
|
||||||
The utils.at() function will perform a given task at a given time every
|
|
||||||
(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.
|
|
||||||
* world : (optional) Each world has its own clock. If no world is specified, the server's first world is used.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
To warn players when night is approaching...
|
|
||||||
|
|
||||||
utils.at( "19:00", function() {
|
|
||||||
|
|
||||||
utils.foreach( server.onlinePlayers, function(player){
|
|
||||||
player.chat("The night is dark and full of terrors!");
|
|
||||||
});
|
|
||||||
|
|
||||||
}, self.world);
|
|
||||||
|
|
||||||
***/
|
|
||||||
at: function(time24hr, callback, world){
|
|
||||||
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 world == "undefined"){
|
|
||||||
world = server.worlds.get(0);
|
|
||||||
}
|
|
||||||
utils.nicely(function(){
|
|
||||||
var time = world.getTime();
|
|
||||||
var diff = timeMc - time;
|
|
||||||
if (diff > 0 && diff < 100){
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},forever, null, 100);
|
|
||||||
},
|
|
||||||
};
|
|
2
src/main/js/lib/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/events-helper-bukkit.js
|
||||||
|
/events-helper-canary.js
|
64
src/main/js/lib/command.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
'use strict';
|
||||||
|
/*
|
||||||
|
command management - allow for non-ops to execute approved javascript code.
|
||||||
|
*/
|
||||||
|
var _commands = {},
|
||||||
|
_cmdInterceptors = [];
|
||||||
|
/*
|
||||||
|
execute a JSP command.
|
||||||
|
*/
|
||||||
|
var executeCmd = function( args, player ) {
|
||||||
|
var name,
|
||||||
|
cmd,
|
||||||
|
intercepted,
|
||||||
|
result = null;
|
||||||
|
|
||||||
|
if ( args.length === 0 ) {
|
||||||
|
throw new Error('Usage: jsp command-name command-parameters');
|
||||||
|
}
|
||||||
|
name = args[0];
|
||||||
|
cmd = _commands[name];
|
||||||
|
if ( typeof cmd === 'undefined' ) {
|
||||||
|
// it's not a global command - pass it on to interceptors
|
||||||
|
intercepted = false;
|
||||||
|
for ( var i = 0; i < _cmdInterceptors.length; i++ ) {
|
||||||
|
if ( _cmdInterceptors[i]( args, player ) )
|
||||||
|
intercepted = true;
|
||||||
|
}
|
||||||
|
if ( !intercepted ) {
|
||||||
|
console.warn( 'Command %s is not recognised', name );
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
try {
|
||||||
|
result = cmd.callback( args.slice(1), player );
|
||||||
|
} catch ( e ) {
|
||||||
|
console.error( 'Error while trying to execute command: ' + JSON.stringify( args ) );
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
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 = [];
|
||||||
|
}
|
||||||
|
_commands[name] = { callback: func, options: options };
|
||||||
|
if ( intercepts ) {
|
||||||
|
_cmdInterceptors.push(func);
|
||||||
|
}
|
||||||
|
return func;
|
||||||
|
};
|
||||||
|
exports.command = defineCmd;
|
||||||
|
exports.commands = _commands;
|
||||||
|
exports.exec = executeCmd;
|
79
src/main/js/lib/console.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
'use strict';
|
||||||
|
/*************************************************************************
|
||||||
|
## console global variable
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
***/
|
||||||
|
function argsToArray( args ) {
|
||||||
|
var result = [];
|
||||||
|
for ( var i =0; i < args.length; i++ ) {
|
||||||
|
result.push(args[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function consMsg(params){
|
||||||
|
var args = argsToArray(params);
|
||||||
|
if ( args.length > 1 ) {
|
||||||
|
return java.lang.String.format( args[0], args.slice(1) );
|
||||||
|
} else {
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(logger){
|
||||||
|
|
||||||
|
function bukkitLog( level, restOfArgs ) {
|
||||||
|
logger['log(java.util.logging.Level,java.lang.String)'](
|
||||||
|
java.util.logging.Level[level],
|
||||||
|
consMsg(restOfArgs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
78
src/main/js/lib/events-bukkit.js
Normal file
|
@ -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;
|
||||||
|
};
|
68
src/main/js/lib/events-canary.js
Normal file
|
@ -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;
|
||||||
|
};
|
112
src/main/js/lib/events.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
'use strict';
|
||||||
|
/************************************************************************
|
||||||
|
## 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
|
||||||
|
|
||||||
|
***/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
module.exports = require('events-bukkit');
|
||||||
|
helper = require('events-helper-bukkit');
|
||||||
|
}
|
||||||
|
for ( var func in helper ) {
|
||||||
|
module.exports[func] = helper[func];
|
||||||
|
};
|
29
src/main/js/lib/find.js
Normal file
|
@ -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;
|
||||||
|
};
|
29
src/main/js/lib/java-utils.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
exports.isJavaObject = function( o ) {
|
||||||
|
if (o === global){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (o !== undefined && o !== null){
|
||||||
|
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;
|
||||||
|
};
|
77
src/main/js/lib/js-patch.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
module.exports = function( $ ) {
|
||||||
|
|
||||||
|
// wph 20140105 trim not availabe in String on Mac OS.
|
||||||
|
if ( typeof String.prototype.trim == 'undefined' ) {
|
||||||
|
String.prototype.trim = function( ) {
|
||||||
|
return this.replace( /^\s+|\s+$/g, '' );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// wph 20140316 Java 1.6.0_65 on mac does not have Function.prototype.bind
|
||||||
|
// code from http://webreflection.blogspot.ie/2010/02/functionprototypebind.html
|
||||||
|
if (typeof Function.prototype.bind == 'undefined' ) {
|
||||||
|
Function.prototype.bind = (function (slice){
|
||||||
|
// (C) WebReflection - Mit Style License
|
||||||
|
function bind(context) {
|
||||||
|
var self = this; // "trapped" function reference
|
||||||
|
// only if there is more than an argument
|
||||||
|
// we are interested into more complex operations
|
||||||
|
// this will speed up common bind creation
|
||||||
|
// avoiding useless slices over arguments
|
||||||
|
if (1 < arguments.length) {
|
||||||
|
// extra arguments to send by default
|
||||||
|
var $arguments = slice.call(arguments, 1);
|
||||||
|
return function () {
|
||||||
|
return self.apply(
|
||||||
|
context,
|
||||||
|
// thanks @kangax for this suggestion
|
||||||
|
arguments.length ?
|
||||||
|
// concat arguments with those received
|
||||||
|
$arguments.concat(slice.call(arguments)) :
|
||||||
|
// send just arguments, no concat, no slice
|
||||||
|
$arguments
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// optimized callback
|
||||||
|
return function () {
|
||||||
|
// speed up when function is called without arguments
|
||||||
|
return arguments.length ? self.apply(context, arguments) : self.call(context);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// the named function
|
||||||
|
return bind;
|
||||||
|
}(Array.prototype.slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__plugin.canary){
|
||||||
|
require('task-canary')($);
|
||||||
|
} else {
|
||||||
|
require('task-bukkit')($);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function unitTest( console ) {
|
||||||
|
/*
|
||||||
|
sanity tests
|
||||||
|
*/
|
||||||
|
$.setTimeout(function(){
|
||||||
|
console.log('js-patch setTimeout() test complete');
|
||||||
|
},100);
|
||||||
|
var clearMe = $.setTimeout(function(){
|
||||||
|
console.error('js-patch clearTimeout() test failed');
|
||||||
|
},100);
|
||||||
|
$.clearTimeout( clearMe );
|
||||||
|
|
||||||
|
var runs = 3;
|
||||||
|
var clearAfterRuns = $.setInterval(function(){
|
||||||
|
runs --;
|
||||||
|
if (runs == 0){
|
||||||
|
$.clearInterval(clearAfterRuns);
|
||||||
|
}
|
||||||
|
if (runs < 0){
|
||||||
|
console.error('js-patch clearInterval test failed.');
|
||||||
|
}
|
||||||
|
},100);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
json2.js
|
json2.js
|
||||||
2012-10-08
|
2013-05-26
|
||||||
|
|
||||||
Public Domain.
|
Public Domain.
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ if (typeof JSON !== 'object') {
|
||||||
|
|
||||||
if (typeof Date.prototype.toJSON !== 'function') {
|
if (typeof Date.prototype.toJSON !== 'function') {
|
||||||
|
|
||||||
Date.prototype.toJSON = function (key) {
|
Date.prototype.toJSON = function () {
|
||||||
|
|
||||||
return isFinite(this.valueOf())
|
return isFinite(this.valueOf())
|
||||||
? this.getUTCFullYear() + '-' +
|
? this.getUTCFullYear() + '-' +
|
||||||
|
@ -187,7 +187,7 @@ if (typeof JSON !== 'object') {
|
||||||
|
|
||||||
String.prototype.toJSON =
|
String.prototype.toJSON =
|
||||||
Number.prototype.toJSON =
|
Number.prototype.toJSON =
|
||||||
Boolean.prototype.toJSON = function (key) {
|
Boolean.prototype.toJSON = function () {
|
||||||
return this.valueOf();
|
return this.valueOf();
|
||||||
};
|
};
|
||||||
}
|
}
|
32
src/main/js/lib/legacy-check.js
Normal file
|
@ -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 );
|
||||||
|
}
|
||||||
|
};
|
8
src/main/js/lib/nashorn-type.js
Normal file
|
@ -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;
|
||||||
|
};
|
52
src/main/js/lib/persistence.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
var _dataDir = null,
|
||||||
|
_persistentData = {};
|
||||||
|
|
||||||
|
module.exports = function( rootDir, $ ) {
|
||||||
|
|
||||||
|
var _load = function( name ) {
|
||||||
|
var result = $.scloadJSON( _dataDir.canonicalPath + '/' + name + '-store.json' );
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var _save = function( name, objToSave ) {
|
||||||
|
$.scsave( objToSave, _dataDir.canonicalPath + '/' + name + '-store.json' );
|
||||||
|
};
|
||||||
|
|
||||||
|
_dataDir = new java.io.File( rootDir, 'data' );
|
||||||
|
|
||||||
|
$.persist = function( name, data, write ) {
|
||||||
|
var i,
|
||||||
|
dataFromFile;
|
||||||
|
if ( typeof data == 'undefined' ) {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
if ( typeof write == 'undefined' ) {
|
||||||
|
write = false;
|
||||||
|
}
|
||||||
|
if ( !write ) {
|
||||||
|
dataFromFile = _load( name );
|
||||||
|
if ( typeof dataFromFile != 'undefined') {
|
||||||
|
for ( i in dataFromFile ) {
|
||||||
|
data[i] = dataFromFile[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// flush data to file
|
||||||
|
_save( name, data );
|
||||||
|
}
|
||||||
|
_persistentData[name] = data;
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
persist on shutdown
|
||||||
|
*/
|
||||||
|
$.addUnloadHandler( function( ) {
|
||||||
|
var name,
|
||||||
|
data;
|
||||||
|
for ( name in _persistentData ) {
|
||||||
|
data = _persistentData[name];
|
||||||
|
_save( name, data );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
72
src/main/js/lib/plugin.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
'use strict';
|
||||||
|
/*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 = {};
|
||||||
|
|
||||||
|
function _plugin(/* String */ moduleName, /* Object */ moduleObject, isPersistent ) {
|
||||||
|
//
|
||||||
|
// don't load plugin more than once
|
||||||
|
//
|
||||||
|
if ( typeof _plugins[moduleName] != 'undefined' ) {
|
||||||
|
return _plugins[moduleName].module;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginData = { persistent: isPersistent, module: moduleObject };
|
||||||
|
if ( typeof moduleObject.store == 'undefined' ) {
|
||||||
|
moduleObject.store = {};
|
||||||
|
}
|
||||||
|
_plugins[moduleName] = pluginData;
|
||||||
|
|
||||||
|
if ( isPersistent ) {
|
||||||
|
moduleObject.store = persist( moduleName, moduleObject.store );
|
||||||
|
}
|
||||||
|
return moduleObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _autoload( context, pluginDir, options ) {
|
||||||
|
/*
|
||||||
|
Reload all of the .js files in the given directory
|
||||||
|
*/
|
||||||
|
var sourceFiles = [],
|
||||||
|
property,
|
||||||
|
module,
|
||||||
|
pluginPath;
|
||||||
|
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++ ) {
|
||||||
|
|
||||||
|
pluginPath = sourceFiles[i];
|
||||||
|
if (!pluginPath.match(/\.js$/)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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 ) {
|
||||||
|
var msg = 'Plugin ' + pluginPath + ' ' + e ;
|
||||||
|
console.error( msg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
exports.plugin = _plugin;
|
||||||
|
exports.autoload = _autoload;
|
||||||
|
|
12
src/main/js/lib/readme.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# lib directory
|
||||||
|
|
||||||
|
This directory contains core scriptcraft files and modules.
|
||||||
|
|
||||||
|
* plugin.js - A module which provides support for persistent plugins (plugins which need to save state)
|
||||||
|
* require.js - The require() function implementation. See [Node.js modules] documentation.
|
||||||
|
* scriptcraft.js - The core scriptcraft code.
|
||||||
|
* events.js - Event handling module for use by plugin/module developers.
|
||||||
|
|
||||||
|
When `require('modulename')` is called, if a file in the current working directory called 'modulename' is not found then the `lib` folder is the first location that `require()` looks for modules.
|
||||||
|
|
||||||
|
[njsmod]: http://nodejs.org/api/modules.html
|
321
src/main/js/lib/require.js
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
/*************************************************************************
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
***/
|
||||||
|
(function ( rootDir, modulePaths, hooks, evaluate ) {
|
||||||
|
|
||||||
|
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("\\\\","/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function readModuleFromDirectory( dir ) {
|
||||||
|
|
||||||
|
// look for a package.json file
|
||||||
|
var pkgJsonFile = new File( dir, './package.json' );
|
||||||
|
if ( pkgJsonFile.exists() ) {
|
||||||
|
var pkg = scload( pkgJsonFile );
|
||||||
|
var mainFile = new File( dir, pkg.main );
|
||||||
|
if ( mainFile.exists() ) {
|
||||||
|
return mainFile;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// look for an index.js file
|
||||||
|
var indexJsFile = new File( dir, './index.js' );
|
||||||
|
if ( indexJsFile.exists() ) {
|
||||||
|
return indexJsFile;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
### 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
|
||||||
|
|
||||||
|
***/
|
||||||
|
function resolveModuleToFile( moduleName, parentDir ) {
|
||||||
|
var file = new File(moduleName),
|
||||||
|
i = 0,
|
||||||
|
pathWithJSExt,
|
||||||
|
resolvedFile;
|
||||||
|
if ( file.exists() ) {
|
||||||
|
return fileExists(file);
|
||||||
|
}
|
||||||
|
if ( moduleName.match( /^[^\.\/]/ ) ) {
|
||||||
|
// it's a module named like so ... 'events' , 'net/http'
|
||||||
|
//
|
||||||
|
for ( ; i < modulePaths.length; i++ ) {
|
||||||
|
resolvedFile = new File(modulePaths[i] + moduleName);
|
||||||
|
if ( resolvedFile.exists() ) {
|
||||||
|
return fileExists(resolvedFile);
|
||||||
|
} else {
|
||||||
|
// try appending a .js to the end
|
||||||
|
resolvedFile = new File(modulePaths[i] + moduleName + '.js');
|
||||||
|
if ( resolvedFile.exists() ) {
|
||||||
|
return resolvedFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((file = new File(parentDir, moduleName)).exists()) {
|
||||||
|
return fileExists(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;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
require() function implementation
|
||||||
|
*/
|
||||||
|
function _require( parentFile, path, options ) {
|
||||||
|
var file,
|
||||||
|
canonizedFilename,
|
||||||
|
moduleInfo,
|
||||||
|
buffered,
|
||||||
|
head = '(function(exports,module,require,__filename,__dirname){ ',
|
||||||
|
code = '',
|
||||||
|
line = null;
|
||||||
|
|
||||||
|
if ( typeof options == 'undefined' ) {
|
||||||
|
options = { cache: true };
|
||||||
|
} else {
|
||||||
|
if ( typeof options.cache == 'undefined' ) {
|
||||||
|
options.cache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file = resolveModuleToFile(path, parentFile);
|
||||||
|
if ( !file ) {
|
||||||
|
var errMsg = '' + _format("require() failed to find matching file for module '%s' " +
|
||||||
|
"in working directory '%s' ", [path, parentFile.canonicalPath]);
|
||||||
|
if (! ( (''+path).match( /^\./ ) ) ) {
|
||||||
|
errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
moduleInfo = _loadedModules[canonizedFilename];
|
||||||
|
if ( moduleInfo ) {
|
||||||
|
if ( options.cache ) {
|
||||||
|
return moduleInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( hooks ) {
|
||||||
|
hooks.loading( canonizedFilename );
|
||||||
|
}
|
||||||
|
buffered = new BufferedReader(new FileReader(file));
|
||||||
|
while ( (line = buffered.readLine()) !== null ) {
|
||||||
|
code += line + '\n';
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
exports: {},
|
||||||
|
require: _requireClosure(file.parentFile)
|
||||||
|
};
|
||||||
|
var tail = '})';
|
||||||
|
code = head + code + tail;
|
||||||
|
|
||||||
|
if ( options.cache ) {
|
||||||
|
_loadedModules[canonizedFilename] = moduleInfo;
|
||||||
|
}
|
||||||
|
var compiledWrapper = null;
|
||||||
|
try {
|
||||||
|
compiledWrapper = evaluate(code);
|
||||||
|
} catch (e) {
|
||||||
|
/*
|
||||||
|
wph 20140313 JRE8 (nashorn) gives misleading linenumber of evaluating code not evaluated code.
|
||||||
|
This can be fixed by instead using __engine.eval
|
||||||
|
*/
|
||||||
|
throw new Error( "Error evaluating module " + path
|
||||||
|
+ " line #" + e.lineNumber
|
||||||
|
+ " : " + e.message, canonizedFilename, e.lineNumber );
|
||||||
|
}
|
||||||
|
var __dirname = '' + file.parentFile.canonicalPath;
|
||||||
|
var parameters = [
|
||||||
|
moduleInfo.exports, /* exports */
|
||||||
|
moduleInfo, /* module */
|
||||||
|
moduleInfo.require, /* require */
|
||||||
|
canonizedFilename, /* __filename */
|
||||||
|
__dirname /* __dirname */
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
compiledWrapper
|
||||||
|
.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 + (snippet?('\n' + snippet):''), canonizedFilename, e.lineNumber );
|
||||||
|
}
|
||||||
|
if ( hooks ) {
|
||||||
|
hooks.loaded( canonizedFilename );
|
||||||
|
}
|
||||||
|
moduleInfo.loaded = true;
|
||||||
|
return moduleInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
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!
|
||||||
|
})
|
785
src/main/js/lib/scriptcraft.js
Normal file
|
@ -0,0 +1,785 @@
|
||||||
|
'use strict';
|
||||||
|
/*global require*/
|
||||||
|
/************************************************************************
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
/*
|
||||||
|
wph 20130124 - make self, plugin and server public - these are far more useful now that tab-complete works.
|
||||||
|
*/
|
||||||
|
var global = this;
|
||||||
|
var server;
|
||||||
|
global.nashorn = typeof Java !== 'undefined';
|
||||||
|
/*
|
||||||
|
private implementation
|
||||||
|
*/
|
||||||
|
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( '\\\\', '/' );
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Save a javascript object to a file (saves using JSON notation)
|
||||||
|
*/
|
||||||
|
function _save( objToSave, filename ) {
|
||||||
|
var objectToStr = null,
|
||||||
|
f,
|
||||||
|
out;
|
||||||
|
try {
|
||||||
|
objectToStr = JSON.stringify( objToSave, null, 2 );
|
||||||
|
|
||||||
|
} catch( e ) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
function _load( filename, warnOnFileNotFound )
|
||||||
|
{
|
||||||
|
var result = null,
|
||||||
|
file = filename,
|
||||||
|
r,
|
||||||
|
parent,
|
||||||
|
reader,
|
||||||
|
br,
|
||||||
|
code,
|
||||||
|
wrappedCode;
|
||||||
|
|
||||||
|
if ( !( filename instanceof File ) ) {
|
||||||
|
file = new File(filename);
|
||||||
|
}
|
||||||
|
var canonizedFilename = _canonize( file );
|
||||||
|
|
||||||
|
if ( file.exists() ) {
|
||||||
|
reader = new FileReader( file );
|
||||||
|
br = new BufferedReader( reader );
|
||||||
|
code = '';
|
||||||
|
try {
|
||||||
|
while ( (r = br.readLine()) !== null ) {
|
||||||
|
code += r + '\n';
|
||||||
|
}
|
||||||
|
wrappedCode = '(' + code + ')';
|
||||||
|
result = __engine.eval( wrappedCode );
|
||||||
|
// issue #103 avoid side-effects of || operator on Mac Rhino
|
||||||
|
} catch ( e ) {
|
||||||
|
logError('Error evaluating ' + canonizedFilename + ', ' + e );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch ( re ) {
|
||||||
|
// fail silently on reader close error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( warnOnFileNotFound ) {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
var configFile = new File(jsPluginsRootDir, 'data/');
|
||||||
|
configFile.mkdirs();
|
||||||
|
configFile = new File(configFile,'global-config.json');
|
||||||
|
var config = _load( configFile );
|
||||||
|
if ( !config ) {
|
||||||
|
config = { verbose: false };
|
||||||
|
}
|
||||||
|
global.config = config;
|
||||||
|
global.__plugin = __plugin;
|
||||||
|
/*
|
||||||
|
wph 20131229 Issue #103 JSON is not bundled with javax.scripting / Rhino on Mac.
|
||||||
|
*/
|
||||||
|
(function(){
|
||||||
|
var jsonFileReader = new FileReader( new File( jsPluginsRootDirName + '/lib/json2.js' ) );
|
||||||
|
var jsonLoaded = __engine['eval(java.io.Reader)']( jsonFileReader );
|
||||||
|
}());
|
||||||
|
|
||||||
|
global.addUnloadHandler = _addUnloadHandler;
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
var modulePaths = [ jsPluginsRootDirName + '/lib/',
|
||||||
|
jsPluginsRootDirName + '/modules/' ];
|
||||||
|
|
||||||
|
if ( config.verbose ) {
|
||||||
|
logger.info( 'Setting up CommonJS-style module system. Root Directory: ' + jsPluginsRootDirName );
|
||||||
|
logger.info( 'Module paths: ' + JSON.stringify(modulePaths) );
|
||||||
|
}
|
||||||
|
var requireHooks = {
|
||||||
|
loading: function( path ) {
|
||||||
|
if ( config.verbose ) {
|
||||||
|
logger.info( 'loading ' + path );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loaded: function( path ) {
|
||||||
|
if ( config.verbose ) {
|
||||||
|
logger.info( 'loaded ' + path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
global.require = configRequire(
|
||||||
|
jsPluginsRootDirName,
|
||||||
|
modulePaths,
|
||||||
|
requireHooks,
|
||||||
|
function( code ) {
|
||||||
|
return __engine.eval( code );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var testJSPatch = require('js-patch')( global );
|
||||||
|
var console = require('console')(logger);
|
||||||
|
global.console = console;
|
||||||
|
testJSPatch(console);
|
||||||
|
|
||||||
|
/*
|
||||||
|
setup persistence
|
||||||
|
*/
|
||||||
|
require('persistence')( jsPluginsRootDir, global );
|
||||||
|
|
||||||
|
var isJavaObject = require('java-utils').isJavaObject;
|
||||||
|
|
||||||
|
var cmdModule = require('command');
|
||||||
|
global.command = cmdModule.command;
|
||||||
|
var plugins = require('plugin');
|
||||||
|
global.__onTabComplete = require('tabcomplete');
|
||||||
|
global.plugin = plugins.plugin;
|
||||||
|
|
||||||
|
var events = require('events');
|
||||||
|
// wph 20131226 - make events global as it is used by many plugins/modules
|
||||||
|
global.events = events;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
48
src/main/js/lib/tabcomplete-jsp.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'use strict';
|
||||||
|
var _commands = require('command').commands;
|
||||||
|
/*
|
||||||
|
Tab completion for the /jsp commmand
|
||||||
|
*/
|
||||||
|
var __onTabCompleteJSP = function( result, cmdArgs ) {
|
||||||
|
var cmdInput = cmdArgs[0],
|
||||||
|
opts,
|
||||||
|
cmd,
|
||||||
|
len,
|
||||||
|
i;
|
||||||
|
cmd = _commands[cmdInput];
|
||||||
|
if ( cmd ) {
|
||||||
|
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 ) {
|
||||||
|
result.add( opts[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( cmdArgs.length == 0 ) {
|
||||||
|
for ( i in _commands ) {
|
||||||
|
result.add( i );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// partial e.g. /jsp ho
|
||||||
|
// should tabcomplete to home
|
||||||
|
//
|
||||||
|
for ( i in _commands ) {
|
||||||
|
if ( i.indexOf( cmdInput ) == 0 ) {
|
||||||
|
result.add( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
module.exports = __onTabCompleteJSP;
|
||||||
|
|
||||||
|
|
255
src/main/js/lib/tabcomplete.js
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
'use strict';
|
||||||
|
var tabCompleteJSP = require('tabcomplete-jsp'),
|
||||||
|
isJavaObject = require('java-utils').isJavaObject;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tab Completion of the /js and /jsp commands
|
||||||
|
*/
|
||||||
|
var _javaLangObjectMethods = [
|
||||||
|
'equals'
|
||||||
|
,'getClass'
|
||||||
|
,'class'
|
||||||
|
,'getClass'
|
||||||
|
,'hashCode'
|
||||||
|
,'notify'
|
||||||
|
,'notifyAll'
|
||||||
|
,'toString'
|
||||||
|
,'wait'
|
||||||
|
,'clone'
|
||||||
|
,'finalize'
|
||||||
|
];
|
||||||
|
|
||||||
|
var _getProperties = function( o ) {
|
||||||
|
var result = [],
|
||||||
|
i,
|
||||||
|
j,
|
||||||
|
isObjectMethod,
|
||||||
|
propValue,
|
||||||
|
typeofProperty;
|
||||||
|
|
||||||
|
if ( isJavaObject( o ) ) {
|
||||||
|
/*
|
||||||
|
fix for issue #115 - java objects are not iterable
|
||||||
|
see: http://mail.openjdk.java.net/pipermail/nashorn-dev/2014-March/002790.html
|
||||||
|
*/
|
||||||
|
if ( typeof Object.bindProperties === 'function' ) {
|
||||||
|
var placeholder = {};
|
||||||
|
Object.bindProperties(placeholder, o);
|
||||||
|
o = placeholder;
|
||||||
|
}
|
||||||
|
propertyLoop:
|
||||||
|
for ( i in o ) {
|
||||||
|
//
|
||||||
|
// don't include standard Object methods
|
||||||
|
//
|
||||||
|
isObjectMethod = false;
|
||||||
|
for ( j = 0; j < _javaLangObjectMethods.length; j++ ) {
|
||||||
|
if ( _javaLangObjectMethods[j] == i ) {
|
||||||
|
continue propertyLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeofProperty = null;
|
||||||
|
try {
|
||||||
|
propValue = o[i];
|
||||||
|
typeofProperty = typeof propValue;
|
||||||
|
} catch( e ) {
|
||||||
|
if ( e.message == 'java.lang.IllegalStateException: Entity not leashed' ) {
|
||||||
|
// wph 20131020 fail silently for Entity leashing in craftbukkit
|
||||||
|
} else {
|
||||||
|
// don't throw an error during tab completion just make a best effort to
|
||||||
|
// do the job.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( typeofProperty == 'function' ) {
|
||||||
|
result.push( i+'()' );
|
||||||
|
} else {
|
||||||
|
result.push( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( o.constructor == Array ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for ( i in o ) {
|
||||||
|
if ( i.match( /^[^_]/ ) ) {
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.sort();
|
||||||
|
};
|
||||||
|
|
||||||
|
var onTabCompleteJS = function( ) {
|
||||||
|
|
||||||
|
var _globalSymbols,
|
||||||
|
lastArg,
|
||||||
|
propsOfLastArg,
|
||||||
|
statement,
|
||||||
|
statementSyms,
|
||||||
|
lastSymbol,
|
||||||
|
parts,
|
||||||
|
name,
|
||||||
|
symbol,
|
||||||
|
lastGoodSymbol,
|
||||||
|
lastArgProp,
|
||||||
|
i,
|
||||||
|
objectProps,
|
||||||
|
candidate,
|
||||||
|
re,
|
||||||
|
li,
|
||||||
|
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 (__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
|
||||||
|
|
||||||
|
_globalSymbols = _getProperties(global);
|
||||||
|
|
||||||
|
lastArg = cmdArgs.length ? cmdArgs[ cmdArgs.length - 1 ] + '' : null;
|
||||||
|
propsOfLastArg = [];
|
||||||
|
statement = cmdArgs.join(' ');
|
||||||
|
|
||||||
|
statement = statement.replace(/^\s+/,'').replace(/\s+$/,'');
|
||||||
|
|
||||||
|
if ( statement.length == 0 ) {
|
||||||
|
propsOfLastArg = _globalSymbols;
|
||||||
|
} else {
|
||||||
|
if (statement.match(/\)$/)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
statementSyms = statement.split(/[^\$a-zA-Z0-9_\.]/);
|
||||||
|
|
||||||
|
lastSymbol = statementSyms[statementSyms.length-1];
|
||||||
|
//print('DEBUG: lastSymbol=[' + lastSymbol + ']');
|
||||||
|
//
|
||||||
|
// try to complete the object ala java IDEs.
|
||||||
|
//
|
||||||
|
parts = lastSymbol.split(/\./);
|
||||||
|
name = parts[0];
|
||||||
|
|
||||||
|
symbol = global[name];
|
||||||
|
|
||||||
|
//print('DEBUG: name=' + name + ',symbol=' + symbol);
|
||||||
|
|
||||||
|
lastGoodSymbol = symbol;
|
||||||
|
if ( typeof symbol !== 'undefined' ) {
|
||||||
|
for ( i = 1; i < parts.length; i++ ) {
|
||||||
|
name = parts[i];
|
||||||
|
if ( !name ) { // fix issue #115
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// nashorn - object[missingProperty] returns null not undefined
|
||||||
|
if ( symbol == null ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastGoodSymbol = symbol;
|
||||||
|
}
|
||||||
|
if ( typeof symbol == 'undefined' || symbol === null) {
|
||||||
|
//
|
||||||
|
// look up partial matches against last good symbol
|
||||||
|
//
|
||||||
|
objectProps = _getProperties( lastGoodSymbol );
|
||||||
|
if ( name == '' ) {
|
||||||
|
// if the last symbol looks like this..
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
//print('debug:case Y1: server.');
|
||||||
|
|
||||||
|
for ( i = 0; i < objectProps.length; i++ ) {
|
||||||
|
candidate = lastSymbol + objectProps[i];
|
||||||
|
re = new RegExp( lastSymbol + '$', 'g' );
|
||||||
|
propsOfLastArg.push( lastArg.replace( re, candidate ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// it looks like this..
|
||||||
|
// server.wo
|
||||||
|
//
|
||||||
|
//print('debug:case Y2: server.wo');
|
||||||
|
|
||||||
|
li = statement.lastIndexOf(name);
|
||||||
|
for ( i = 0; i < objectProps.length; i++ ) {
|
||||||
|
if ( objectProps[i].indexOf(name) == 0 ) {
|
||||||
|
candidate = lastSymbol.substring( 0, lastSymbol.lastIndexOf( name ) );
|
||||||
|
candidate = candidate + objectProps[i];
|
||||||
|
re = new RegExp( lastSymbol + '$', 'g' );
|
||||||
|
propsOfLastArg.push( lastArg.replace( re, candidate ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//print('debug:case Y3: server');
|
||||||
|
objectProps = _getProperties( symbol );
|
||||||
|
for ( i = 0; i < objectProps.length; i++ ) {
|
||||||
|
re = new RegExp( lastSymbol+ '$', 'g' );
|
||||||
|
lastArgProp = lastArg.replace( re, lastSymbol + '.' + objectProps[i] ) ;
|
||||||
|
lastArgProp = lastArgProp.replace(/\.\./g,'.');
|
||||||
|
propsOfLastArg.push( lastArgProp );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ( i = 0; i < _globalSymbols.length; i++ ) {
|
||||||
|
if ( _globalSymbols[i].indexOf(lastSymbol) == 0 ) {
|
||||||
|
possibleCompletion = _globalSymbols[i];
|
||||||
|
re = new RegExp( lastSymbol+ '$', 'g' );
|
||||||
|
propsOfLastArg.push( lastArg.replace( re, possibleCompletion ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( i = 0; i < propsOfLastArg.length; i++ ) {
|
||||||
|
result.add( propsOfLastArg[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
delete global.self; // delete self when no longer needed for autocomplete
|
||||||
|
};
|
||||||
|
module.exports = onTabCompleteJS;
|
24
src/main/js/lib/task-bukkit.js
Normal file
|
@ -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;
|
||||||
|
};
|
35
src/main/js/lib/task-canary.js
Normal file
|
@ -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;
|
||||||
|
};
|