First phase of transition from Bukkit to Canary.

Some of the plugins are not yet supported.
If you're feeling brave you can build from source using ant.
This commit is contained in:
walterhiggins 2014-09-29 23:42:41 +01:00
parent cc4f98474d
commit 19162c3688
55 changed files with 1436 additions and 808 deletions

View file

@ -1,7 +1,7 @@
<project name="scriptcraft" default="package" basedir=".">
<property file="build.properties"/>
<description>Builds the scriptcraft.jar file - a plugin for bukkit</description>
<property name="src" location="src/main/java"/>
<property name="src.canary" location="src/main/java/canary"/>
<property name="build" location="target/classes"/>
<property name="dist" location="target/" />
<property name="minecraft.dir" location="${dist}/minecraft" />
@ -24,46 +24,15 @@
</target>
<target name="server-setup" depends="init" description="Downloads the latest bukkit dev jar"
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"
verbose="true"
httpusecaches="false"
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 name="run" depends="server-setup, package, update-live-cb" description="Starts Bukkit with ScriptCraft">
<echo>Starting Bukkit with ScriptCraft</echo>
<java jar="${minecraft.dir}/craftbukkit.jar" maxmemory="1024m" fork="true" dir="${minecraft.dir}" />
</target>
<target name="compile" depends="init, server-setup" description="compile bukkit plugin source">
<target name="compile-canary" depends="init" description="compile canary plugin source">
<javac includeantruntime="false"
srcdir="${src}"
srcdir="${src.canary}"
source="1.6"
target="1.6"
destdir="${build}"
debug="true"
classpath="${minecraft.dir}/craftbukkit.jar" />
classpath="lib/canary.jar" />
</target>
<target name="gendocs" depends="construct-ypgpm, construct-api-ref" description="Generate API documentation">
@ -87,14 +56,33 @@
</target>
<target name="gen-events-helper" depends="compile-docs,server-setup,init">
<target name="gen-events-helper-canary" depends="compile-docs,init">
<mkdir dir="${dist}/js/lib"/>
<java classname="jscript" failonerror="true" fork="true" output="${dist}/js/lib/events-helper.js" error="${dist}/geneventserror.log">
<classpath>
<pathelement path="${build}"/>
<pathelement path="${minecraft.dir}/craftbukkit.jar"/>
<pathelement path="lib/canary.jar"/>
</classpath>
<arg value="src/generateEventsHelper.js"/>
<arg value="canary"/>
<arg value="lib/canary.jar"/>
<arg value="blockDestroy"/>
<arg value="net.canarymod.hook.player.BlockDestroyHook"/>
</java>
</target>
<target name="gen-events-helper-bukkit" depends="compile-docs,init">
<mkdir dir="${dist}/js/lib"/>
<java classname="jscript" failonerror="true" fork="true" output="${dist}/js/lib/events-helper.js" error="${dist}/geneventserror.log">
<classpath>
<pathelement path="${build}"/>
<pathelement path="lib/canary.jar"/>
</classpath>
<arg value="src/generateEventsHelper.js"/>
<arg value="canary"/>
<arg value="lib/canary.jar"/>
<arg value="Break"/>
<arg value="org.bukkit.event.block.BlockBreakEvent"/>
</java>
</target>
@ -148,7 +136,7 @@ Walter Higgins
<target name="zip_js" depends="zip_lib, zip_modules, zip_plugins">
</target>
<target name="copy-js" depends="gen-events-helper,init">
<target name="copy-js" depends="gen-events-helper-canary,init">
<copy todir="${dist}/js">
<fileset dir="src/main/js"/>
</copy>
@ -185,7 +173,7 @@ Walter Higgins
</zip>
</target>
<target name="package" depends="gendocs,zip_js,compile" description="generate the distribution" >
<target name="package" depends="gendocs,zip_js,compile-canary" description="generate the distribution" >
<!-- ensure plugin.yml is always copied -->
<delete file="${build}/plugin.yml" />
<copy todir="${build}">
@ -206,7 +194,7 @@ Walter Higgins
<delete dir="${dist}"/>
</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" />
<delete>
<fileset dir="${minecraft.dir}/plugins/" includes="scriptcraft*.*"/>

View file

@ -171,7 +171,7 @@ called `location`. We can use that name like this...
Blackrock Castle
...You might be wondering where the enclosing `'` single-quotes went.
...You might be wondering why there's no enclosing `'` single quotes.
When telling the computer to store some text, you have to put `'`
(that's the single-quote character) at the start and end
of the text. The computer doesn't store these quote characters, only the

BIN
lib/canary.jar Normal file

Binary file not shown.

View file

@ -1,3 +1,5 @@
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,
@ -6,27 +8,21 @@ var File = java.io.File,
Modifier = java.lang.reflect.Modifier,
clz,
ZipInputStream = java.util.zip.ZipInputStream,
zis = new ZipInputStream(new FileInputStream('./target/minecraft/craftbukkit.jar')),
zis = new ZipInputStream(new FileInputStream(args[1])),
entry = null;
var content = [
'/*********************',
'## Events Helper Module',
'The Events helper module provides a suite of functions - one for each possible event.',
'For example, the events.blockBreak() function is just a wrapper function which calls events.on(org.bukkit.event.block.BlockBreakEvent, callback, priority)',
'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.blockBreak( function( event ) { ',
' event.player.sendMessage(\'You broke a block!\'); ',
' });',
'',
'... which is just a shorter and less error-prone way of writing ...',
'',
' events.on(\'block.BlockBreakEvent\',function( event ) { ',
' event.player.sendMessage(\'You broke a block!\');',
' 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.',
@ -38,7 +34,11 @@ for (var i = 0; i< content.length; i++){
}
while ( ( entry = zis.nextEntry) != null) {
var name = '' + entry.name;
if (name.match(/org\/bukkit\/event\/.+Event\.class$/)){
var re1 = /org\/bukkit\/event\/.+Event\.class$/;
if (args[0] == 'canary'){
re1 = /net\/canarymod\/hook\/.+Hook\.class$/;
}
if (name.match(re1)){
name = name.replace(/\//g,'.').replace('.class','');
try {
clz = java.lang.Class.forName(name);
@ -50,8 +50,21 @@ while ( ( entry = zis.nextEntry) != null) {
continue;
}
var parts = name.split('.');
var shortName = name.replace('org.bukkit.event.','');
var fname = parts.reverse().shift().replace(/^(.)/,function(a){ return a.toLowerCase();}).replace(/Event$/,'');
var shortName = null;
if (args[0] == 'canary'){
shortName = name.replace('net.canarymod.hook.','');
}
if (args[0] == 'bukkit'){
shortName = name.replace('org.bukkit.event.','');
}
var fname = parts.reverse().shift().replace(/^(.)/,function(a){
return a.toLowerCase();});
if (args[0] == 'bukkit'){
fname = fname.replace(/Event$/,'');
}
if (args[0] == 'canary'){
fname = fname.replace(/Hook$/,'');
}
var comment = [
'/*********************',
@ -70,7 +83,12 @@ while ( ( entry = zis.nextEntry) != null) {
out.println(comment[i]);
}
out.println('exports.' + fname + ' = function(callback,priority){ ');
out.println(' return events.on(' + name + ',callback,priority);');
if (args[0] == 'canary'){
out.println(' return events.on(Packages.' + name + ',callback,priority);');
}
if (args[0] == 'bukkit'){
out.println(' return events.on(' + name + ',callback,priority);');
}
out.println('};');
}
}

View file

@ -0,0 +1,143 @@
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;
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;
}
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) {
boolean result = false;
String javascriptCode = "";
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 = { "canary.super.js", "canary.command.super.js" },
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 = { "canary.jsp", "canary.command.jsp" },
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");
}
}

View file

@ -35,39 +35,44 @@ ScriptCraft uses Java's [String.format()][strfmt] so any string substitution ide
[webcons]: https://developer.mozilla.org/en-US/docs/Web/API/console
***/
var logger = __plugin.logger,
logMethodName = 'log(java.util.logging.Level,java.lang.String)';
var argsToArray = function( args ) {
function argsToArray( args ) {
var result = [];
for ( var i =0; i < args.length; i++ ) {
result.push(args[i]);
}
return result;
}
var log = function( level, restOfArgs ) {
var args = argsToArray( restOfArgs );
function consMsg(params){
var args = argsToArray(params);
if ( args.length > 1 ) {
var msg = java.lang.String.format( args[0], args.slice(1) );
logger[logMethodName]( level, msg );
return java.lang.String.format( args[0], args.slice(1) );
} else {
logger[logMethodName]( level, args[0] );
return args[0];
}
}
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 ); }
};
}
};
var Level = java.util.logging.Level;
exports.log = function( ) {
log( Level.INFO, arguments );
};
exports.info = function( ) {
log( Level.INFO, arguments );
};
exports.warn = function( ) {
log( Level.WARNING, arguments );
};
exports.error = function( ) {
log( Level.SEVERE, arguments );
};

View file

@ -0,0 +1,63 @@
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 ) {
handler.call( result, evt );
}
} );
/*
wph 20130222 issue #64 bad interaction with Essentials plugin
if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
then BOOM! the other plugin will throw an error because Rhino can't coerce an
equals() method from an Interface.
The workaround is to make the ScriptCraftPlugin java class a Listener.
Should only unregister() registered plugins in ScriptCraft js code.
*/
regd = new bkRegisteredListener( __plugin, eventExecutor, priority, __plugin, true );
handlerList.register( regd );
result.unregister = function(){
handlerList.unregister( regd );
};
return result;
};

View file

@ -0,0 +1,45 @@
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 = new cmDispatcher( {
execute: function (l, evt) {
handler.call(result, evt );
}
});
/*
wph 20130222 issue #64 bad interaction with Essentials plugin
if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
then BOOM! the other plugin will throw an error because Rhino can't coerce an
equals() method from an Interface.
The workaround is to make the ScriptCraftPlugin java class a Listener.
Should only unregister() registered plugins in ScriptCraft js code.
*/
regd = new cmPluginListener({});
cmHookExecutor.registerHook(regd, __plugin, eventType.class, eventExecutor, priority);
result.unregister = function(){
cmHookExecutor.unregisterPluginListener(regd);
};
return result;
};

View file

@ -13,9 +13,7 @@ This method is used to register event listeners.
#### Parameters
* eventName - A java class. See [Bukkit API][buk] for
details of the many bukkit event types. Provide the full class name (without
enclosing quotes).
* eventName - A Java class. See [Bukkit API][buk] for details of the many bukkit event types.
* callback - A function which will be called whenever the event
fires. The callback should take a single parameter, event (the event fired).
@ -35,16 +33,16 @@ An object which can be used to unregister the listener.
The following code will print a message on screen every time a block is broken in the game
```javascript
events.on( Packages.org.bukkit.event.block.BlockBreakEvent, function( evt ) {
evt.player.sendMessage( evt.player.name + ' broke a block!');
events.on( org.bukkit.block.BlockBreakEvent, function( evt ) {
echo(evt.player, evt.player.name + ' broke a block!');
} );
```
To handle an event only once and unregister from further events...
```javascript
events.on( Packages.org.bukkit.event.block.BlockBreakEvent, function( evt ) {
evt.player.sendMessage( evt.player.name + ' broke a block!');
events.on( org.bukkit.block.BlockBreakEvent, function( evt ) {
echo( evt.player, evt.player.name + ' broke a block!');
this.unregister();
} );
```
@ -57,79 +55,22 @@ object which is returned by the `events.on()` function.
To unregister a listener *outside* of the listener function...
```javascript
var myBlockBreakListener = events.on( Packages.org.bukkit.event.block.BlockBreakEvent, function( evt ) { ... } );
var myBlockBreakListener = events.on( org.bukkit.block.BlockBreakEvent, 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
***/
if (__plugin.canary){
module.exports = require('events-canary');
} else {
module.exports = require('events-bukkit');
}
var helper = require('events-helper');
for ( var func in helper ) {
exports[func] = helper[func];
};
var bkEventPriority = org.bukkit.event.EventPriority,
bkEventExecutor = org.bukkit.plugin.EventExecutor,
bkRegisteredListener = org.bukkit.plugin.RegisteredListener,
bkEventPackage = 'org.bukkit.event.';
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 ) {
handler.call( result, evt );
}
} );
/*
wph 20130222 issue #64 bad interaction with Essentials plugin
if another plugin tries to unregister a Listener (not a Plugin or a RegisteredListener)
then BOOM! the other plugin will throw an error because Rhino can't coerce an
equals() method from an Interface.
The workaround is to make the ScriptCraftPlugin java class a Listener.
Should only unregister() registered plugins in ScriptCraft js code.
*/
regd = new bkRegisteredListener( __plugin, eventExecutor, priority, __plugin, true );
handlerList.register( regd );
result.unregister = function(){
handlerList.unregister( regd );
};
return result;
module.exports[func] = helper[func];
};

View file

@ -1,4 +1,3 @@
module.exports = function( $ ) {
// wph 20140105 trim not availabe in String on Mac OS.
@ -44,29 +43,35 @@ module.exports = function( $ ) {
return bind;
}(Array.prototype.slice));
}
$.setTimeout = function( callback, delayInMillis ) {
if (__plugin.canary){
require('task-canary')($);
} else {
require('task-bukkit')($);
}
return function unitTest( console ) {
/*
javascript programmers familiar with setTimeout know that it expects
a delay in milliseconds. However, bukkit's scheduler expects a delay in ticks
(where 1 tick = 1/20th second)
sanity tests
*/
var bukkitTask = server.scheduler.runTaskLater( __plugin, callback, Math.ceil( delayInMillis / 50 ) );
return bukkitTask;
};
$.setTimeout(function(){
console.log('js-patch setTimeout() test complete');
},100);
var clearMe = $.setTimeout(function(){
console.error('js-patch clearTimeout() test failed');
},100);
$.clearTimeout( clearMe );
$.clearTimeout = function( bukkitTask ) {
bukkitTask.cancel();
var runs = 3;
var clearAfterRuns = $.setInterval(function(){
runs --;
if (runs == 0){
$.clearInterval(clearAfterRuns);
}
if (runs < 0){
console.error('js-patch clearInterval test failed.');
}
},100);
};
$.setInterval = function( callback, intervalInMillis ) {
var delay = Math.ceil( intervalInMillis / 50);
var bukkitTask = server.scheduler.runTaskTimer( __plugin, callback, delay, delay );
return bukkitTask;
};
$.clearInterval = function( bukkitTask ) {
bukkitTask.cancel();
};
};

View file

@ -1,13 +1,19 @@
/*
wph 20140102 - warn if legacy 'craftbukkit/js-plugins' or 'craftbukkit/scriptcraft' directories are present
*/
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 cbPluginsDir = jsPluginsRootDir.parentFile,
cbDir = new File(cbPluginsDir.canonicalPath).parentFile,
legacyExists = false,
legacyDirs = [new File( cbDir, 'js-plugins' ),
new File( cbDir, 'scriptcraft' )];
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' ),
new File( mcServerDir, 'plugins/scriptcraft' )
];
for ( var i = 0; i < legacyDirs.length; i++ ) {
if ( legacyDirs[i].exists()
@ -17,7 +23,7 @@ module.exports = function( jsPluginsRootDir ) {
console.warn('Legacy ScriptCraft directory %s was found. This directory is no longer used.',
legacyDirs[i].canonicalPath);
console.warn('Please put plugins in the plugins/scriptcraft/plugins directory');
console.warn('Please put plugins in the ' + jsPluginsRootDir.canonicalPath + '/plugins directory');
}
}
if ( legacyExists ) {

View file

@ -1,7 +1,6 @@
'use strict';
var console = require('./console'),
File = java.io.File,
var File = java.io.File,
FileWriter = java.io.FileWriter,
PrintWriter = java.io.PrintWriter;
/*
@ -32,7 +31,6 @@ var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPer
exports.plugin = _plugin;
exports.autoload = function( context, pluginDir, logger, options ) {
var _canonize = function( file ) {
return '' + file.canonicalPath.replaceAll('\\\\','/');
};
@ -67,10 +65,12 @@ exports.autoload = function( context, pluginDir, logger, options ) {
_listSourceFiles( sourceFiles, pluginDir );
var len = sourceFiles.length;
if ( config.verbose ) {
console.info( len + ' scriptcraft plugins found in ' + pluginDir );
if ( config && config.verbose ) {
logger.info( len + ' scriptcraft plugins found in ' + pluginDir );
}
for ( var i = 0; i < len; i++ ) {
pluginPath = _canonize( sourceFiles[i] );
module = {};
@ -84,7 +84,7 @@ exports.autoload = function( context, pluginDir, logger, options ) {
}
} catch ( e ) {
if ( typeof logger != 'undefined' ) {
logger.severe( 'Plugin ' + pluginPath + ' ' + e );
logger.error( 'Plugin ' + pluginPath + ' ' + e );
} else {
java.lang.System.out.println( 'Error: Plugin ' + pluginPath + ' ' + e );
}

View file

@ -209,7 +209,7 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
if (! ( (''+path).match( /^\./ ) ) ) {
errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths);
}
throw errMsg;
throw new Error(errMsg);
}
canonizedFilename = _canonize(file);
@ -249,8 +249,8 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
This can be fixed by instead using __engine.eval
*/
throw new Error( "Error evaluating module " + path
+ " line #" + e.lineNumber
+ " : " + e.message, canonizedFilename, e.lineNumber );
+ " line #" + e.lineNumber
+ " : " + e.message, canonizedFilename, e.lineNumber );
}
var __dirname = '' + file.parentFile.canonicalPath;
var parameters = [
@ -265,9 +265,20 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
.apply(moduleInfo.exports, /* this */
parameters);
} catch (e) {
var snippet = '';
if ((''+e.lineNumber).match(/[0-9]/)){
var lines = code.split(/\n/);
if (e.lineNumber > 1){
snippet = ' ' + lines[e.lineNumber-2] + '\n';
}
snippet += '> ' + lines[e.lineNumber-1] + '\n';
if (e.lineNumber < lines.length){
snippet += ' ' + lines[e.lineNumber] + '\n';
}
}
throw new Error( "Error executing module " + path
+ " line #" + e.lineNumber
+ " : " + e.message, canonizedFilename, e.lineNumber );
+ " : " + e.message + (snippet?('\n' + snippet):''), canonizedFilename, e.lineNumber );
}
if ( hooks ) {
hooks.loaded( canonizedFilename );

View file

@ -70,7 +70,7 @@ directory....
```javascript
exports.greet = function(player) {
player.sendMessage('Hello ' + player.name);
echo(player, 'Hello ' + player.name);
};
```
@ -166,21 +166,15 @@ ScripCraft provides some global functions which can be used by all plugins/modul
### echo function
The `echo()` function displays a message on the in-game screen. The message is displayed to the `self` player (this is usually the player who issued the `/js` or `/jsp` command).
The `echo()` function displays a message on the in-game screen.
#### Example
/js echo('Hello World')
/js echo( self, 'Hello World')
For programmers familiar with Javascript web programming, an `alert`
function is also provided. `alert` works exactly the same as `echo`
e.g. `alert('Hello World')`.
#### Notes
The `echo` and `alert` functions are provided as convenience functions for beginning programmers. The use of these 2 functions is not recommended in event-handling code or multi-threaded code. In such cases, if you want to send a message to a given player then use the Bukkit API's [Player.sendMessage()][plsm] function instead.
[plsm]: http://jd.bukkit.org/dev/apidocs/org/bukkit/command/CommandSender.html#sendMessage(java.lang.String)
e.g. `alert( self, 'Hello World')`.
### require() function
@ -308,7 +302,7 @@ The `command()` function is used to expose javascript functions for use by non-o
// javascript code
function boo( params, sender) {
sender.sendMessage( params[0] );
echo( sender, params[0] );
}
command( boo );
@ -322,7 +316,7 @@ To use a callback for options (TAB-Completion) ...
function boo( params, sender ) {
var receiver = server.getPlayer( params[0] );
if ( receiver ){
receiver.sendMessage( sender.name + ' says boo!');
echo( receiver, sender.name + ' says boo!');
}
}
command( boo, bukkit.playerNames );
@ -331,12 +325,10 @@ See chat/colors.js or alias/alias.js or homes/homes.js for more examples of how
### setTimeout() function
This function mimics the setTimeout() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setTimeout() in the browser returns a numeric value which can be subsequently passed to clearTimeout(), This implementation returns a [BukkitTask][btdoc] object which can be subsequently passed to ScriptCraft's own clearTimeout() implementation.
This function mimics the setTimeout() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setTimeout() in the browser returns a numeric value which can be subsequently passed to clearTimeout(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearTimeout() implementation.
If Node.js supports setTimeout() then it's probably good for ScriptCraft to support it too.
[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
#### Example
```javascript
@ -355,11 +347,7 @@ A scriptcraft implementation of clearTimeout().
### setInterval() function
This function mimics the setInterval() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setInterval() in the browser returns a numeric value which can be subsequently passed to clearInterval(), This implementation returns a [BukkitTask][btdoc] object which can be subsequently passed to ScriptCraft's own clearInterval() implementation.
If Node.js supports setInterval() then it's probably good for ScriptCraft to support it too.
[btdoc]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scheduler/BukkitTask.html
This function mimics the setInterval() function used in browser-based javascript. However, the function will only accept a function reference, not a string of javascript code. Where setInterval() in the browser returns a numeric value which can be subsequently passed to clearInterval(), This implementation returns an object which can be subsequently passed to ScriptCraft's own clearInterval() implementation.
### clearInterval() function
@ -384,39 +372,50 @@ The addUnloadHandler() function takes a callback function as a parameter. The ca
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 = org.bukkit.Bukkit.server;
var server;
/*
private implementation
*/
function __onEnable ( __engine, __plugin, __script )
{
var File = java.io.File,
FileReader = java.io.FileReader,
BufferedReader = java.io.BufferedReader,
PrintWriter = java.io.PrintWriter,
FileWriter = java.io.FileWriter;
var debug = function(msg){
java.lang.System.out.println('DEBUG:' + msg);
};
var _canonize = function( file ) {
var __onDisableImpl;
function __onDisable ( __engine, __plugin ) {
__onDisableImpl( __engine, __plugin);
}
function __onEnable ( __engine, __plugin, __script ) {
function _echo( ) {
var sender, msg;
if (arguments.length == 2){
sender = arguments[0];
msg = arguments[1];
} else {
if ( typeof self == 'undefined' ) {
return;
}
sender = self;
msg = arguments[0];
}
if (__plugin.canary){
sender.message( msg );
} else {
sender.sendMessage( msg );
}
} // end echo()
function _canonize( file ) {
return '' + file.getCanonicalPath().replaceAll( '\\\\', '/' );
};
// lib (assumes scriptcraft.js is in craftbukkit/plugins/scriptcraft/lib directory
var libDir = __script.parentFile,
jsPluginsRootDir = libDir.parentFile, // scriptcraft
jsPluginsRootDirName = _canonize(jsPluginsRootDir),
logger = __plugin.logger;
}
/*
Save a javascript object to a file (saves using JSON notation)
*/
var _save = function( objToSave, filename ) {
function _save( objToSave, filename ) {
var objectToStr = null,
f,
out;
@ -431,17 +430,8 @@ function __onEnable ( __engine, __plugin, __script )
out = new PrintWriter(new FileWriter(f));
out.println( objectToStr );
out.close();
};
/*
make sure eval is present: it's present on JRE 6, 7, and 8 on Linux
*/
if ( typeof eval == 'undefined' ) {
global.eval = function( str ) {
return __engine.eval( str );
};
}
var _loadJSON = function ( filename ){
}
function _loadJSON( filename ){
var result = null,
file = filename,
r,
@ -464,7 +454,7 @@ function __onEnable ( __engine, __plugin, __script )
}
result = JSON.parse(contents);
} catch ( e ) {
logger.severe( 'Error evaluating ' + canonizedFilename + ', ' + e );
logger.error( 'Error evaluating ' + canonizedFilename + ', ' + e );
}
finally {
try {
@ -475,12 +465,11 @@ function __onEnable ( __engine, __plugin, __script )
}
}
return result;
};
}
/*
Load the contents of the file and evaluate as javascript
*/
var _load = function( filename, warnOnFileNotFound )
function _load( filename, warnOnFileNotFound )
{
var result = null,
file = filename,
@ -508,7 +497,7 @@ function __onEnable ( __engine, __plugin, __script )
result = __engine.eval( wrappedCode );
// issue #103 avoid side-effects of || operator on Mac Rhino
} catch ( e ) {
logger.severe( 'Error evaluating ' + canonizedFilename + ', ' + e );
logger.error( 'Error evaluating ' + canonizedFilename + ', ' + e );
}
finally {
try {
@ -523,7 +512,164 @@ function __onEnable ( __engine, __plugin, __script )
}
}
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( ) {
if ( typeof self !== 'undefined' ) {
if ( !_isOp(self) ) {
echo( self, 'Only operators can refresh()');
return;
}
}
if (__plugin.canary){
var pluginName = __plugin.name;
Canary.manager().disablePlugin( pluginName );
Canary.manager().enablePlugin( pluginName );
} else {
__plugin.pluginLoader.disablePlugin( __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 ) {
logger.error( 'Error while trying to display result: ' + jsResult + ', Error: '+ displayError );
}
}
}
} catch ( e ) {
logger.error( '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 ( typeof Java === 'undefined' ) { // Java is an object in Nashorn
delete global.self;
delete global.__engine;
}
}
}
if ( cmdName == 'jsp' ) {
cmdModule.exec( jsArgs, sender );
result = true;
}
return result;
} // 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;
}
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
*/
@ -532,7 +678,7 @@ function __onEnable ( __engine, __plugin, __script )
configFile = new File(configFile,'global-config.json');
var config = _load( configFile );
if ( !config ) {
config = { verbose: false };
config = { verbose: true };
}
global.config = config;
global.__plugin = __plugin;
@ -544,45 +690,14 @@ function __onEnable ( __engine, __plugin, __script )
var jsonLoaded = __engine['eval(java.io.Reader)']( jsonFileReader );
}());
/*
Unload Handlers
*/
var unloadHandlers = [];
var _addUnloadHandler = function( f ) {
unloadHandlers.push( f );
};
var _runUnloadHandlers = function() {
for ( var i = 0; i < unloadHandlers.length; i++ ) {
unloadHandlers[i]( );
}
};
global.addUnloadHandler = _addUnloadHandler;
global.refresh = function( ) {
if ( typeof self !== 'undefined' ) {
if ( !self.op ) {
self.sendMessage('Only operators can refresh()');
return;
}
}
__plugin.pluginLoader.disablePlugin( __plugin );
__plugin.pluginLoader.enablePlugin( __plugin );
};
var _echo = function ( msg ) {
if ( typeof self == 'undefined' ) {
return;
}
self.sendMessage( msg );
};
global.refresh = _refresh;
global.echo = _echo;
global.alert = _echo;
global.scload = _load;
global.scsave = _save;
global.scloadJSON = _loadJSON;
global.isOp = _isOp;
var configRequire = _load( jsPluginsRootDirName + '/lib/require.js', true );
/*
setup paths to search for modules
@ -615,8 +730,11 @@ function __onEnable ( __engine, __plugin, __script )
}
);
require('js-patch')( global );
global.console = require('console');
var testJSPatch = require('js-patch')( global );
var console = require('console')(logger);
global.console = console;
testJSPatch(console);
/*
setup persistence
*/
@ -634,86 +752,13 @@ function __onEnable ( __engine, __plugin, __script )
// wph 20131226 - make events global as it is used by many plugins/modules
global.events = events;
events.pluginDisable(function( evt ) {
// save config
_save( global.config, new File( jsPluginsRootDir, 'data/global-config.json' ) );
_runUnloadHandlers();
org.bukkit.event.HandlerList['unregisterAll(org.bukkit.plugin.Plugin)'](__plugin);
});