Fix issue #111, reorg of lib/ and (undoc'd) persistence

This commit is contained in:
walterhiggins 2014-01-12 11:26:26 +00:00
parent c77e78c2e5
commit aefc98f172
18 changed files with 514 additions and 106 deletions

View file

@ -7,13 +7,6 @@
<property name="minecraft.dir" location="${dist}/minecraft" /> <property name="minecraft.dir" location="${dist}/minecraft" />
<property name="js-plugins-dir" value="scriptcraft"/> <property name="js-plugins-dir" value="scriptcraft"/>
<property name="http.agent" value="'Walter'" /> <property name="http.agent" value="'Walter'" />
<target name="testy">
<echo message="${http.agent}"/>
<get src="http://dl.bukkit.org/api/1.0/downloads/projects/CraftBukkit/?_accept=application/xml"
verbose="true"
httpusecaches="false"
dest="${minecraft.dir}/waltbukkit.xml" />
</target>
<target name="init"> <target name="init">
<property file="build.local.properties"/> <property file="build.local.properties"/>
<tstamp> <tstamp>
@ -144,7 +137,7 @@ Walter Higgins
<!-- 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}"/>
</target> </target>
<target name="clean" description="clean up" > <target name="clean" description="clean up" >
@ -157,6 +150,6 @@ Walter Higgins
<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>

View file

@ -117,6 +117,8 @@ Walter Higgins
* [Example Plugin #7 - Listening for events, Greet players when they join the game.](#example-plugin-7---listening-for-events-greet-players-when-they-join-the-game) * [Example Plugin #7 - Listening for events, Greet players when they join the game.](#example-plugin-7---listening-for-events-greet-players-when-they-join-the-game)
* [Arrows Plugin](#arrows-plugin) * [Arrows Plugin](#arrows-plugin)
* [Usage:](#usage-6) * [Usage:](#usage-6)
* [Spawn Plugin](#spawn-plugin)
* [Usage](#usage-7)
* [alias Plugin](#alias-plugin) * [alias Plugin](#alias-plugin)
* [Examples](#examples-2) * [Examples](#examples-2)
* [Classroom Plugin](#classroom-plugin) * [Classroom Plugin](#classroom-plugin)
@ -135,6 +137,10 @@ Walter Higgins
* [Example](#example-1) * [Example](#example-1)
* [SnowballFight mini-game](#snowballfight-mini-game) * [SnowballFight mini-game](#snowballfight-mini-game)
* [Description](#description-2) * [Description](#description-2)
* [Cow Clicker Mini-Game](#cow-clicker-mini-game)
* [How to Play](#how-to-play)
* [Rules](#rules)
* [Gameplay Mechanics](#gameplay-mechanics)
## Modules in Scriptcraft ## Modules in Scriptcraft
@ -2340,6 +2346,18 @@ All of the above functions can take an optional player object or name
as a parameter. For example: `/js arrows.explosive('player23')` makes as a parameter. For example: `/js arrows.explosive('player23')` makes
player23's arrows explosive. player23's arrows explosive.
## Spawn Plugin
Allows in-game operators to easily spawn creatures at current location.
### Usage
/jsp spawn cow
/jsp spawn sheep
/jsp spawn wolf
See <http://jd.bukkit.org/beta/apidocs/org/bukkit/entity/EntityType.html> for a list of possible entities (creatures) which can be spawned.
## alias Plugin ## alias Plugin
The alias module lets players and server admins create their own The alias module lets players and server admins create their own
@ -2621,3 +2639,44 @@ player returns to their previous mode of play (creative or
survival). Create a small arena with a couple of small buildings for survival). Create a small arena with a couple of small buildings for
cover to make the game more fun. cover to make the game more fun.
## Cow Clicker Mini-Game
### How to Play
At the in-game prompt type `jsp cowclicker` to start or stop
playing. Right-Click on Cows to score points. No points for killing
cows (hint: use the same keyboard keys you'd use for opening doors).
Every time you click a cow your score increases by 1 point. Your score
is displayed in a side-bar along the right edge of of the screen.
![cow clicker](img/cowclicker.png)
### Rules
* You can join and leave the Cow Clicker game at any time by typing
`/jsp cowclicker` at the in-game prompt.
* Once you leave the game, your score is reset to zero.
* You can disconnect from the server and your score will be saved for
the next time you join.
### Gameplay Mechanics
This is meant as a trivially simple use of the [Bukkit Scoreboard
API][bukscore]. There are many things you'll want to consider when constructing
your own mini-game...
* Is the game itself a long-lived game - that is - should players and
scores be persisted (stored) between server restarts?
* What should happen when a player quits the server - should this also be
understood as quitting the mini-game?
* What should happen when a player who was previously playing the
mini-game, joins the server - should they automatically resume the
mini-game?
[bukscore]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scoreboard/package-summary.html

View file

@ -35,11 +35,11 @@ using the more succinct...
it is easier to read. The important thing to remember when using the 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 Bukkit (or any Java API) from Javascript is that for any Java Bean, a
property called `propertyName` will have a getter called property called `propertyName` will have a getter called
`getPropertyName()` and a setter called `setPropertyName`. From this `getPropertyName()` and a setter called `setPropertyName()`. From this
rule you can infer what any Bukkit class properties are. For example, rule you can infer what any Bukkit class properties are. For example,
the [Bukkit Player][bukpl] object has the following methods... the [Bukkit Player][bukpl] object has the following methods...
* float getWalSpeed() * float getWalkSpeed()
* void setWalkSpeed(float speed) * void setWalkSpeed(float speed)
... so from this you can infer that every Player object has a ... so from this you can infer that every Player object has a
@ -61,7 +61,7 @@ the world in which a player is located...
If you're new to Java and the [Bukkit API][bukapi] is the first time 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 you've browsed Java documentation, you may be wondering where the
`location` property came from - the `location` property is "inherited" `location` property came from - the `location` property is "inherited"
by one of the Player classes super-classes. You'll see the from one of the Player class's super-classes (it's ancestors). You'll see the
`getLocation()` method listed under a section titled **Methods `getLocation()` method listed under a section titled **Methods
inherited from interface org.bukkit.entity.Entity** in the inherited from interface org.bukkit.entity.Entity** in the
[Player][bukpl] javadoc page. [Player][bukpl] javadoc page.

BIN
docs/img/cowclicker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View file

@ -1,3 +1,25 @@
# 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 # 2014 01 05
Bug Fix: On Mac OS, alias plugin caused Exceptions due to missing Bug Fix: On Mac OS, alias plugin caused Exceptions due to missing

View file

@ -1,5 +1,12 @@
args = args.slice(1); args = args.slice(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 template = args[0];
var BufferedReader = java.io.BufferedReader; var BufferedReader = java.io.BufferedReader;

View file

@ -29,7 +29,7 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
/** /**
* Unzips bundled javascript code. * Unzips bundled javascript code.
*/ */
private void unzipJS() private void unzipJS() throws IOException
{ {
// //
// does the js-plugins directory exist? // does the js-plugins directory exist?
@ -37,9 +37,13 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
File jsPlugins = new File(JS_PLUGINS_DIR); File jsPlugins = new File(JS_PLUGINS_DIR);
if (!jsPlugins.exists()) if (!jsPlugins.exists())
{ {
getLogger().finest("Directory " + JS_PLUGINS_DIR + " does not exist."); getLogger().info("Directory " + jsPlugins.getCanonicalPath() + " does not exist.");
getLogger().finest("Initializing " + JS_PLUGINS_DIR + " directory with contents from plugin archive."); getLogger().info("Initializing " + jsPlugins.getCanonicalPath() + " directory with contents from plugin archive.");
jsPlugins.mkdir(); try{
jsPlugins.mkdirs();
}catch(Exception e){
throw new RuntimeException("Failed to create directory " + jsPlugins.getCanonicalPath() + ":" + e.getMessage());
}
} }
ZipInputStream zis = new ZipInputStream(getResource(JS_PLUGINS_ZIP)); ZipInputStream zis = new ZipInputStream(getResource(JS_PLUGINS_ZIP));
@ -48,7 +52,7 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
while ( ( entry = zis.getNextEntry() ) != null) while ( ( entry = zis.getNextEntry() ) != null)
{ {
String filename = entry.getName(); String filename = entry.getName();
//File newFile = new File(jsPlugins.getName() + File.separator + filename);
File newFile = new File(jsPlugins, filename); File newFile = new File(jsPlugins, filename);
//create all non exists folders //create all non exists folders
@ -59,17 +63,23 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
// //
// only write out to file if zip entry is newer than file // only write out to file if zip entry is newer than file
// //
String reason = null;
long zTime = entry.getTime(); long zTime = entry.getTime();
boolean unzip = false; boolean unzip = false;
if (!newFile.exists()) if (!newFile.exists()){
reason = "NE";
unzip = true; unzip = true;
}
else{ else{
long fTime = newFile.lastModified(); long fTime = newFile.lastModified();
if (zTime > fTime) if (zTime > fTime){
reason = "" + new Long((zTime-fTime)/3600000) + "h";
unzip = true; unzip = true;
}
} }
if (unzip){ if (unzip){
getLogger().info("Unzipping " + newFile.getCanonicalPath()); getLogger().info("Unzipping " + newFile.getCanonicalPath() + " (" + reason + ")" );
FileOutputStream fout = new FileOutputStream(newFile); FileOutputStream fout = new FileOutputStream(newFile);
for (int c = zis.read(); c != -1; c = zis.read()) { for (int c = zis.read(); c != -1; c = zis.read()) {
fout.write(c); fout.write(c);
@ -90,9 +100,9 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
@Override @Override
public void onEnable() public void onEnable()
{ {
unzipJS();
FileReader reader = null; FileReader reader = null;
try{ try{
unzipJS();
ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngineManager factory = new ScriptEngineManager();
File bootScript = new File(JS_PLUGINS_DIR + "/lib/scriptcraft.js"); File bootScript = new File(JS_PLUGINS_DIR + "/lib/scriptcraft.js");
this.engine = factory.getEngineByName("JavaScript"); this.engine = factory.getEngineByName("JavaScript");
@ -103,6 +113,7 @@ public class ScriptCraftPlugin extends JavaPlugin implements Listener
}catch(Exception e){ }catch(Exception e){
e.printStackTrace(); e.printStackTrace();
this.getLogger().severe(e.getMessage());
}finally { }finally {
if (reader != null){ if (reader != null){
try { try {

View file

@ -0,0 +1,33 @@
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,'');
};
}
$.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(bukkitTask){
bukkitTask.cancel();
};
$.setInterval = function(callback, intervalInMillis){
var delay = intervalInMillis/ 50;
var bukkitTask = server.scheduler.runTaskTimer(__plugin, callback, delay, delay);
return bukkitTask;
};
$.clearInterval = function(bukkitTask){
bukkitTask.cancel();
};
};

View file

@ -0,0 +1,37 @@
var _dataDir = null;
var _persistentData = {};
module.exports = function( rootDir, $ ){
_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(_dataDir.canonicalPath + '/' + name + '-store.json');
if (dataFromFile){
for (i in dataFromFile){
data[i] = dataFromFile[i];
}
}
}else{
// flush data to file
$.save(data, _dataDir.canonicalPath + '/' + name + '-store.json');
}
_persistentData[name] = data;
return data;
};
$.addUnloadHandler(function(){
for (var name in _persistentData){
var data = _persistentData[name];
$.save(data, _dataDir.canonicalPath + '/' + name + '-store.json');
}
});
};

View file

@ -3,23 +3,6 @@ var console = require('./console');
var File = java.io.File; var File = java.io.File;
var FileWriter = java.io.FileWriter; var FileWriter = java.io.FileWriter;
var PrintWriter = java.io.PrintWriter; var PrintWriter = java.io.PrintWriter;
/*
Save a javascript object to a file (saves using JSON notation)
*/
var _save = function(object, filename){
var objectToStr = null;
try{
objectToStr = JSON.stringify(object,null,2);
}catch(e){
print("ERROR: " + e.getMessage() + " while saving " + filename);
return;
}
var f = (filename instanceof File) ? filename : new File(filename);
var out = new PrintWriter(new FileWriter(f));
out.println( objectToStr );
out.close();
};
/* /*
plugin management plugin management
*/ */
@ -34,25 +17,18 @@ var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPer
return _plugins[moduleName].module; return _plugins[moduleName].module;
var pluginData = {persistent: isPersistent, module: moduleObject}; var pluginData = {persistent: isPersistent, module: moduleObject};
moduleObject.store = moduleObject.store || {}; if (typeof moduleObject.store == 'undefined')
moduleObject.store = {};
_plugins[moduleName] = pluginData; _plugins[moduleName] = pluginData;
if (isPersistent){ if (isPersistent){
if (!moduleObject.store){ moduleObject.store = persist(moduleName, moduleObject.store);
moduleObject.store = {};
}
var loadedStore = load(dataDir.canonicalPath + "/" + moduleName + "-store.json");
if (loadedStore){
for (var i in loadedStore){
moduleObject.store[i] = loadedStore[i];
}
}
} }
return moduleObject; return moduleObject;
}; };
exports.plugin = _plugin; exports.plugin = _plugin;
exports.save = _save;
var scriptCraftDir = null; var scriptCraftDir = null;
var pluginDir = null; var pluginDir = null;
@ -65,7 +41,7 @@ exports.autoload = function(dir) {
dataDir = new File(dir, "data"); dataDir = new File(dir, "data");
var _canonize = function(file){ var _canonize = function(file){
return "" + file.getCanonicalPath().replaceAll("\\\\","/"); return '' + file.canonicalPath.replaceAll("\\\\","/");
}; };
/* /*
recursively walk the given directory and return a list of all .js files recursively walk the given directory and return a list of all .js files
@ -78,9 +54,7 @@ exports.autoload = function(dir) {
if (file.isDirectory()){ if (file.isDirectory()){
_listSourceFiles(store,file); _listSourceFiles(store,file);
}else{ }else{
if ((file.getCanonicalPath().endsWith(".js") if ( file.canonicalPath.endsWith('.js') ){
|| file.getCanonicalPath().endsWith(".coffee"))
) {
store.push(file); store.push(file);
} }
} }
@ -97,11 +71,11 @@ exports.autoload = function(dir) {
var len = sourceFiles.length; var len = sourceFiles.length;
if (config.verbose) if (config.verbose)
console.info(len + " scriptcraft plugins found."); console.info(len + ' scriptcraft plugins found.');
for (var i = 0;i < len; i++){ for (var i = 0;i < len; i++){
var pluginPath = _canonize(sourceFiles[i]); var pluginPath = _canonize(sourceFiles[i]);
if (config.verbose) if (config.verbose)
console.info("Loading plugin: " + pluginPath); console.info('Loading plugin: ' + pluginPath);
var module = {}; var module = {};
try { try {
module = require(pluginPath); module = require(pluginPath);
@ -119,13 +93,3 @@ exports.autoload = function(dir) {
_reload(pluginDir); _reload(pluginDir);
}; };
addUnloadHandler(function(){
//
// save all plugins which have persistent data
//
for (var moduleName in _plugins){
var pluginData = _plugins[moduleName];
if (pluginData.persistent)
_save(pluginData.module.store, dataDir.canonicalPath + "/" + moduleName + "-store.json");
}
});

View file

@ -187,11 +187,12 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
{ {
var file = resolveModuleToFile(path, parentFile); var file = resolveModuleToFile(path, parentFile);
if (!file){ if (!file){
var errMsg = java.lang.String var errMsg = '' + java.lang.String.format("require() failed to find matching file for module '%s' " +
.format("require() failed to find matching file for module '%s' " + "in working directory '%s' ", [path, parentFile.canonicalPath]);
"while searching directory '%s' and paths %s.", if (! ( (''+path).match(/^\./) )){
[path, parentFile.canonicalPath, JSON.stringify(modulePaths)]); errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths);
console.warn(errMsg); }
logger.warning(errMsg);
throw new Error(errMsg); throw new Error(errMsg);
} }
var canonizedFilename = _canonize(file); var canonizedFilename = _canonize(file);
@ -242,7 +243,7 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
.apply(moduleInfo.exports, /* this */ .apply(moduleInfo.exports, /* this */
parameters); parameters);
} catch (e){ } catch (e){
console.error("Error:" + e + " while executing module " + canonizedFilename); logger.severe('Error:' + e + ' while executing module ' + canonizedFilename);
throw e; throw e;
} }
if (verbose) if (verbose)

View file

@ -422,7 +422,9 @@ function __onEnable (__engine, __plugin, __script)
return ; return ;
var File = java.io.File var File = java.io.File
,FileReader = java.io.FileReader ,FileReader = java.io.FileReader
,BufferedReader = java.io.BufferedReader; ,BufferedReader = java.io.BufferedReader
,PrintWriter = java.io.PrintWriter
,FileWriter = java.io.FileWriter;
var _canonize = function(file){ var _canonize = function(file){
return "" + file.getCanonicalPath().replaceAll("\\\\","/"); return "" + file.getCanonicalPath().replaceAll("\\\\","/");
@ -432,6 +434,23 @@ function __onEnable (__engine, __plugin, __script)
var jsPluginsRootDir = libDir.parentFile; // scriptcraft var jsPluginsRootDir = libDir.parentFile; // scriptcraft
var jsPluginsRootDirName = _canonize(jsPluginsRootDir); var jsPluginsRootDirName = _canonize(jsPluginsRootDir);
/*
Save a javascript object to a file (saves using JSON notation)
*/
var _save = function(object, filename){
var objectToStr = null;
try{
objectToStr = JSON.stringify(object,null,2);
}catch(e){
print("ERROR: " + e.getMessage() + " while saving " + filename);
return;
}
var f = (filename instanceof File) ? filename : new File(filename);
var out = new PrintWriter(new FileWriter(f));
out.println( objectToStr );
out.close();
};
var _loaded = {}; var _loaded = {};
/* /*
Load the contents of the file and evaluate as javascript Load the contents of the file and evaluate as javascript
@ -487,7 +506,7 @@ function __onEnable (__engine, __plugin, __script)
/* /*
now that load is defined, use it to load a global config object now that load is defined, use it to load a global config object
*/ */
var config = _load(new File(jsPluginsRootDir, "data/global-config.json" )); var config = _load(new File(jsPluginsRootDir, 'data/global-config.json' ));
if (!config) if (!config)
config = {verbose: false}; config = {verbose: false};
global.config = config; global.config = config;
@ -510,27 +529,6 @@ function __onEnable (__engine, __plugin, __script)
} }
}; };
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;
};
global.clearTimeout = function(bukkitTask){
bukkitTask.cancel();
};
global.setInterval = function(callback, intervalInMillis){
var delay = intervalInMillis/ 50;
var bukkitTask = server.scheduler.runTaskTimer(__plugin, callback, delay, delay);
return bukkitTask;
};
global.clearInterval = function(bukkitTask){
bukkitTask.cancel();
};
global.refresh = function(){ global.refresh = function(){
__plugin.pluginLoader.disablePlugin(__plugin); __plugin.pluginLoader.disablePlugin(__plugin);
__plugin.pluginLoader.enablePlugin(__plugin); __plugin.pluginLoader.enablePlugin(__plugin);
@ -547,6 +545,7 @@ function __onEnable (__engine, __plugin, __script)
global.echo = _echo; global.echo = _echo;
global.alert = _echo; global.alert = _echo;
global.load = _load; global.load = _load;
global.save = _save;
global.addUnloadHandler = _addUnloadHandler; global.addUnloadHandler = _addUnloadHandler;
@ -556,21 +555,30 @@ function __onEnable (__engine, __plugin, __script)
*/ */
var modulePaths = [jsPluginsRootDirName + '/lib/', var modulePaths = [jsPluginsRootDirName + '/lib/',
jsPluginsRootDirName + '/modules/']; jsPluginsRootDirName + '/modules/'];
global.require = fnRequire(__plugin.logger, __engine, config.verbose, jsPluginsRootDirName, modulePaths); global.require = fnRequire(__plugin.logger,
__engine,
config.verbose,
jsPluginsRootDirName,
modulePaths);
require('js-patch')(global);
global.console = require('console'); global.console = require('console');
/*
setup persistence
*/
require('persistence')(jsPluginsRootDir,global);
global.command = require('command').command; global.command = require('command').command;
var plugins = require('plugin'); var plugins = require('plugin');
global.__onTabComplete = require('tabcomplete'); global.__onTabComplete = require('tabcomplete');
global.plugin = plugins.plugin; global.plugin = plugins.plugin;
global.save = plugins.save;
var events = require('events'); var events = require('events');
events.on('server.PluginDisableEvent',function(l,e){ events.on('server.PluginDisableEvent',function(l,e){
// save config // save config
plugins.save(global.config, new File(jsPluginsRootDir, "data/global-config.json" )); save(global.config, new File(jsPluginsRootDir, "data/global-config.json" ));
_runUnloadHandlers(); _runUnloadHandlers();
org.bukkit.event.HandlerList["unregisterAll(org.bukkit.plugin.Plugin)"](__plugin); org.bukkit.event.HandlerList["unregisterAll(org.bukkit.plugin.Plugin)"](__plugin);
@ -625,10 +633,12 @@ function __onEnable (__engine, __plugin, __script)
for (var i = 0; i < legacyDirs.length; i++){ for (var i = 0; i < legacyDirs.length; i++){
if (legacyDirs[i].exists() && legacyDirs[i].isDirectory()){ if (legacyDirs[i].exists() && legacyDirs[i].isDirectory()){
legacyExists = true; legacyExists = true;
console.warn('Legacy ScriptCraft directory ' + legacyDirs[i].canonicalPath + ' was found. This directory is no longer used.'); console.warn('Legacy ScriptCraft directory %s was found. This directory is no longer used.',
legacyDirs[i].canonicalPath);
} }
} }
if (legacyExists){ if (legacyExists){
console.info('Please note that the working directory for ' + __plugin + ' is ' + jsPluginsRootDir.canonicalPath); console.info('Please note that the working directory for %s is %s',
__plugin, jsPluginsRootDir.canonicalPath);
} }
} }

View file

@ -77,9 +77,3 @@ for (var method in formattingCodes){
return function(){return c+this;}; return function(){return c+this;};
}(formattingCodes[method]); }(formattingCodes[method]);
} }
// wph 20140105 trim not availabe in String on Mac OS.
if (!String.prototype.trim){
String.prototype.trim = function(){
return this.replace(/^\s+|\s+$/g,'');
}
}

View file

@ -47,6 +47,7 @@ var classroom = plugin("classroom", {
allowScripting: function (/* boolean: true or false */ canScript, sender) { allowScripting: function (/* boolean: true or false */ canScript, sender) {
if (typeof sender == 'undefined'){ if (typeof sender == 'undefined'){
console.log("Attempt to set classroom scripting without credentials"); console.log("Attempt to set classroom scripting without credentials");
console.log("classroom.allowScripting(boolean, sender)");
return; return;
} }
if (!sender.op){ if (!sender.op){

View file

@ -15,8 +15,20 @@ Once the game begins, guess a number by typing the `/` character
followed by a number between 1 and 10. followed by a number between 1 and 10.
***/ ***/
var sb = function(cmd){
org.bukkit.Bukkit.dispatchCommand(server.consoleSender, 'scoreboard ' + cmd)
};
exports.Game_NumberGuess = { exports.Game_NumberGuess = {
start: function(sender) { start: function(sender) {
var guesses = 0;
sb('objectives add NumberGuess dummy Guesses');
sb('players set ' + sender.name + ' NumberGuess ' + guesses);
sb('objectives setdisplay sidebar NumberGuess');
importPackage(org.bukkit.conversations); importPackage(org.bukkit.conversations);
var number = Math.ceil(Math.random() * 10); var number = Math.ceil(Math.random() * 10);
@ -38,6 +50,7 @@ exports.Game_NumberGuess = {
if (s == number){ if (s == number){
setTimeout(function(){ setTimeout(function(){
ctx.forWhom.sendRawMessage("You guessed Correct!"); ctx.forWhom.sendRawMessage("You guessed Correct!");
sb('objectives remove NumberGuess');
},100); },100);
return null; return null;
}else{ }else{
@ -45,6 +58,9 @@ exports.Game_NumberGuess = {
ctx.setSessionData("hint","too low\n"); ctx.setSessionData("hint","too low\n");
if (s > number) if (s > number)
ctx.setSessionData("hint","too high\n"); ctx.setSessionData("hint","too high\n");
guesses++;
sb('players set ' + sender.name + ' NumberGuess ' + guesses);
return prompt; return prompt;
} }
}, },

View file

@ -0,0 +1,225 @@
/*************************************************************************
## Cow Clicker Mini-Game
### How to Play
At the in-game prompt type `jsp cowclicker` to start or stop
playing. Right-Click on Cows to score points. No points for killing
cows (hint: use the same keyboard keys you'd use for opening doors).
Every time you click a cow your score increases by 1 point. Your score
is displayed in a side-bar along the right edge of of the screen.
![cow clicker](img/cowclicker.png)
### Rules
* You can join and leave the Cow Clicker game at any time by typing
`/jsp cowclicker` at the in-game prompt.
* Once you leave the game, your score is reset to zero.
* You can disconnect from the server and your score will be saved for
the next time you join.
### Gameplay Mechanics
This is meant as a trivially simple use of the [Bukkit Scoreboard
API][bukscore]. There are many things you'll want to consider when constructing
your own mini-game...
* Is the game itself a long-lived game - that is - should players and
scores be persisted (stored) between server restarts?
* What should happen when a player quits the server - should this also be
understood as quitting the mini-game?
* What should happen when a player who was previously playing the
mini-game, joins the server - should they automatically resume the
mini-game?
[bukscore]: http://jd.bukkit.org/beta/apidocs/org/bukkit/scoreboard/package-summary.html
***/
var store = {};
var scoreboardConfig = {
cowclicker: {SIDEBAR: 'Cows Clicked'}
};
/*
The scoreboard is a simple wrapper around the Bukkit Scoreboard API.
It's only concerned with display of scores, not maintaining them - that's the game's job.
*/
var scoreboard = (function(options){
var temp = {};
var ccScoreboard;
var DisplaySlot = org.bukkit.scoreboard.DisplaySlot;
return {
start: function(){
var objective, slot;
ccScoreboard = server.scoreboardManager.getNewScoreboard();
for (objective in options){
var ccObj = ccScoreboard.registerNewObjective(objective,'dummy');
for (slot in options[objective]){
ccObj.displaySlot = DisplaySlot[slot];
ccObj.displayName = options[objective][slot];
}
}
},
stop: function(){
var objective, slot;
for (objective in options){
ccScoreboard.getObjective(objective).unregister();
for (slot in options[objective]){
ccScoreboard.clearSlot(DisplaySlot[slot]);
}
}
},
update: function(objective,player,score){
if (player.scoreboard && player.scoreboard != ccScoreboard)
{
temp[player.name] = player.scoreboard;
player.scoreboard = ccScoreboard;
}
ccScoreboard.getObjective(objective).getScore(player).score = score;
},
restore: function(player){
// offlineplayers don't have a scoreboard
if (player.scoreboard)
player.scoreboard = temp[player.name];
}
};
}(scoreboardConfig));
var _onPlayerInteract = function(listener, event){
var player = event.player;
var Bukkit = org.bukkit.Bukkit;
if (!store[player.name])
return;
var clickedEntity = event.rightClicked;
var loc = clickedEntity.location;
var sound = function(snd,vol,pitch){
loc.world.playSound(loc,snd,vol,pitch);
};
if (clickedEntity instanceof org.bukkit.entity.Cow)
{
store[player.name].score++;
scoreboard.update('cowclicker', player, store[player.name].score);
Bukkit.dispatchCommand(player, 'me clicked a cow!');
sound(org.bukkit.Sound.CLICK,1,1);
setTimeout(function(){
sound(org.bukkit.Sound.COW_HURT,10,0.85)
},200);
}
};
var _onPlayerQuit = function(listener, event){
_removePlayer(event.player)
};
var _onPlayerJoin = function(listener, event){
var gamePlayer = store[event.player.name];
if (gamePlayer)
_addPlayer(event.player, gamePlayer.score);
};
var _startGame = function(){
if (config.verbose)
console.log('Staring game: Cow Clicker');
events.on('player.PlayerQuitEvent', _onPlayerQuit);
events.on('player.PlayerJoinEvent', _onPlayerJoin);
events.on('player.PlayerInteractEntityEvent', _onPlayerInteract);
scoreboard.start();
store = persist('cowclicker',store);
for (var p in store){
var player = server.getPlayer(p);
if (player){
/*
only add online players
*/
var score = store[p].score;
_addPlayer(player, score);
}
}
};
var _addPlayer = function(player,score){
if (config.verbose)
console.log('Adding player %s to Cow Clicker game',player);
if (typeof score == 'undefined')
score = 0;
store[player.name] = {score: score};
scoreboard.update('cowclicker', player,store[player.name].score);
player.sendMessage('Go forth and click some cows!');
};
var _removePlayer = function(player,notify){
if (player instanceof org.bukkit.OfflinePlayer && player.player)
player = player.player;
if (!store[player.name])
return;
if (config.verbose)
console.log('Removing player %s from Cow Clicker', player);
var playerScore = store[player.name].score;
scoreboard.restore(player);
delete store[player.name];
if (notify && player){
player.sendMessage('You clicked ' + playerScore + ' cows! ' +
'You must be tired after all that clicking.');
}
};
var _removeAllPlayers = function(notify){
if (typeof notify == 'undefined')
notify = false;
for (var p in store){
var player = server.getOfflinePlayer(p);
if (player)
_removePlayer(player,notify);
delete store[p];
}
}
var _stopGame = function(removePlayers){
if (typeof removePlayers == 'undefined')
removePlayers = true;
if (config.verbose)
console.log('Stopping game: Cow Clicker');
scoreboard.stop();
if (!removePlayers)
return;
_removeAllPlayers(false);
persist('cowclicker',store.pers,'w');
};
/*
start the game automatically when this module is loaded.
*/
_startGame();
/*
players can join and leave the game by typing `jsp cowclicker`
*/
command('cowclicker', function(params, sender){
if (!store[sender.name])
_addPlayer(sender);
else
_removePlayer(sender);
});
/*
stop the game when ScriptCraft is unloaded.
*/
addUnloadHandler(function(){
_stopGame(false);
});

View file

@ -0,0 +1,35 @@
/*************************************************************************
## Spawn Plugin
Allows in-game operators to easily spawn creatures at current location.
### Usage
/jsp spawn cow
/jsp spawn sheep
/jsp spawn wolf
This command supports TAB completion so to see a list of possible
entitities, type `/jsp spawn ' at the in-game command prompt, then
press TAB. Visit
<http://jd.bukkit.org/beta/apidocs/org/bukkit/entity/EntityType.html>
for a list of possible entities (creatures) which can be spawned.
***/
var entities = [];
var Types = org.bukkit.entity.EntityType
for (var t in Types){
if (Types[t] && Types[t].ordinal){
entities.push(t);
}
}
command('spawn', function(parameters, sender){
if (!sender.op){
sender.sendMessage('Only operators can perform this command');
return;
}
var location = sender.location;
var world = location.world;
var type = ('' + parameters[0]).toUpperCase();
world.spawnEntity(location, org.bukkit.entity.EntityType[type]);
},entities);

View file

@ -1,4 +1,4 @@
name: ScriptCraftPlugin name: scriptcraft
main: net.walterhiggins.scriptcraft.ScriptCraftPlugin main: net.walterhiggins.scriptcraft.ScriptCraftPlugin
version: [[version]] version: [[version]]
commands: commands: