Updated alias command to create aliases without 'jsp' prefix and added 'console' global variable

This commit is contained in:
walterhiggins 2013-12-27 22:52:16 +00:00
parent a7a4bf79a1
commit e0f8f0dc0f
6 changed files with 341 additions and 291 deletions

View File

@ -530,6 +530,30 @@ To unregister a listener *outside* of the listener function...
[buk2]: http://wiki.bukkit.org/Event_API_Reference
[buk]: http://jd.bukkit.org/dev/apidocs/index.html?org/bukkit/event/Event.html
## 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.
### Example
console.log('Hello %s', 'world');
Basic variable substitution is supported (ScriptCraft's implementation
of console uses the Bukkit Plugin [Logger][lgr] under the hood and
uses [java.lang.String.format()][strfmt] for variable
substitution. All output will be sent to the server console (not
in-game).
[lgr]: http://jd.bukkit.org/beta/apidocs/org/bukkit/plugin/PluginLogger.html
[strfmt]: http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)
http.request() function
====================
The http.request() function will fetch a web address asynchronously (on a
@ -1677,6 +1701,56 @@ The arrows mod adds fancy arrows to the game. Arrows which...
All of the above functions can take an optional player object or name as
a parameter. For example: `/js arrows.explosive('player23')` makes player23's arrows explosive.
## alias Module
The alias module lets players and server admins create their own
per-player or global custom in-game command aliases.
### Examples
To set a command alias which is only visible to the current player
(per-player alias)...
/jsp alias set cw = time set {1} ; weather {2}
... Creates a new custom command only usable by the player who set
it called `cw` (short for set Clock and Weather) which when invoked...
/cw 4000 sun
... will perform the following commands...
/time set 4000
/weather sun
Aliases can use paramters as above. On the right hand side of the `=`, the
`{1}` refers to the first parameter provided with the `cw` alias, `{2}`
refers to the second parameter and so on. So `cw 4000 sun` is converted to
`time set 4000` and `weather sun`.
To set a global command alias usable by all (only operators can create
such an alias)...
/jsp alias global stormy = time 18000; weather storm
To delete an alias ...
/jsp alias delete cw
... deletes the 'cw' alias from the appropriate alias map.
To get a list of aliases currently defined...
/jsp alias list
To get help on the `jsp alias` command:
/jsp alias help
Aliases can be used at the in-game prompt by players or in the server
console. Aliases will not be able to avail of command autocompletion
(pressing the TAB key will have no effect).
Classroom Module
================
The `classroom` object contains a couple of utility functions for use
@ -1718,9 +1792,9 @@ To disallow scripting (and prevent players who join the server from using the co
Only ops users can run the classroom.allowScripting() function - this is so that students
don't try to bar themselves and each other from scripting.
### Commando Plugin
# Commando Plugin
#### Description
## Description
commando is a plugin which can be used to add completely new commands
to Minecraft. Normally ScriptCraft only allows for provision of new
@ -1751,9 +1825,8 @@ configuration file. It makes approving plugins easier and ensures that
craftbukkit plugins behave well together. While it is possible to
override other plugins' commands, the CraftBukkit team do not
recommend this. However, as ScriptCraft users have suggested, it
should be at the discretion of server administrators and plugin
authors as to when overriding or adding new commands to the global
namespace is good.
should be at the discretion of server administrators as to when
overriding or adding new commands to the global namespace is good.
So this is where `commando()` comes in. It uses the exact same
signature as the core `command()` function but will also make the
@ -1762,7 +1835,7 @@ type `/jsp hi` for the above command example, players simply type
`/hi` . This functionality is provided as a plugin rather than as part
of the ScriptCraft core.
#### Example hi-command.js
## Example hi-command.js
var commando = require('../commando');
commando('hi', function(){
@ -1771,17 +1844,17 @@ of the ScriptCraft core.
...Displays a greeting to any player who issues the `/hi` command.
#### Example - timeofday-command.js
## Example - timeofday-command.js
var times = {Dawn: 0, Midday: 6000, Dusk: 12000, Midnight:18000};
commando('timeofday', function(params){
self.location.world.setTime(times[params[0]]);
}
},
['Dawn','Midday','Dusk','Midnight']);
... changes the time of day using a new `/timeofday` command (options are Dawn, Midday, Dusk, Midnight)
#### Caveats
## Caveats
Since commands registered using commando are really just appendages to
the `/jsp` command and are not actually registered globally (it just

View File

@ -1,3 +1,14 @@
# 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.
## 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.
# 2013 12 26
Made the `events` variable global because it is use by modules and

View File

@ -84,7 +84,14 @@ var _command = function(name,func,options,intercepts)
for (var i =1; i < __cmdArgs.length;i++){
params.push("" + __cmdArgs[i]);
}
return func(params);
var result = null;
try {
result = func(params);
}catch (e){
logger.severe("Error while trying to execute command: " + JSON.stringify(params));
throw e;
}
return result;
}
}else{
if (typeof options == "undefined")

View File

@ -416,207 +416,7 @@ var server = org.bukkit.Bukkit.server;
if (!config)
config = {verbose: false};
global.config = config;
/*
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;
var typeofProperty = null;
try {
typeofProperty = typeof o[i];
}catch( e ){
if (e.message == "java.lang.IllegalStateException: Entity not leashed"){
// wph 20131020 fail silently for Entity leashing in craftbukkit
}else{
throw e;
}
}
if (typeofProperty == "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;
};
var _commands;
/*
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
@ -736,7 +536,7 @@ See [issue #69][issue69] for more information.
global.alert = _echo;
global.load = _load;
global.logger = __plugin.logger;
global._onTabComplete = __onTabCompleteJS;
global.addUnloadHandler = _addUnloadHandler;
var fnRequire = load(jsPluginsRootDirName + '/lib/require.js',true);
@ -751,12 +551,15 @@ See [issue #69][issue69] for more information.
jsPluginsRootDirName,
modulePaths);
var plugins = require('plugin');
_commands = plugins.commands;
global._onTabComplete = require('tabcomplete');
global.plugin = plugins.plugin;
global.command = plugins.command;
global.save = plugins.save;
global.console = require('console');
var events = require('events');
events.on('server.PluginDisableEvent',function(l,e){

View File

@ -1,80 +1,230 @@
/*************************************************************************
## alias Module
var _store = {players: {}};
The alias module lets players and server admins create their own
per-player or global custom in-game command aliases.
var alias = 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 = _store.players;
var name = player.name;
if (typeof aliases[name] == "undefined")
aliases[name] = {};
aliases[name][alias] = commands;
},
remove: function(player, alias){
var aliases = _store.players;
if (aliases[player.name])
delete aliases[player.name][alias];
},
list: function(player){
var result = [];
var aliases = _store.players[player.name];
for (var a in aliases)
result.push(a + " = " + aliases[a].join(";"));
return result;
},
store: _store
},true);
### Examples
exports.alias = alias;
To set a command alias which is only visible to the current player
(per-player alias)...
command("alias", function ( params ) {
/*
this function also intercepts command options for /jsp
*/
if (params[0] === "help"){
self.sendMessage(alias.help());
/jsp alias set cw = time set {1} ; weather {2}
... Creates a new custom command only usable by the player who set
it called `cw` (short for set Clock and Weather) which when invoked...
/cw 4000 sun
... will perform the following commands...
/time set 4000
/weather sun
Aliases can use paramters as above. On the right hand side of the `=`, the
`{1}` refers to the first parameter provided with the `cw` alias, `{2}`
refers to the second parameter and so on. So `cw 4000 sun` is converted to
`time set 4000` and `weather sun`.
To set a global command alias usable by all (only operators can create
such an alias)...
/jsp alias global stormy = time 18000; weather storm
To delete an alias ...
/jsp alias delete cw
... deletes the 'cw' alias from the appropriate alias map.
To get a list of aliases currently defined...
/jsp alias list
To get help on the `jsp alias` command:
/jsp alias help
Aliases can be used at the in-game prompt by players or in the server
console. Aliases will not be able to avail of command autocompletion
(pressing the TAB key will have no effect).
***/
var _usage = "\
/jsp alias set {alias} = {comand-1} ;{command-2}\n \
/jsp alias global {alias} = {command-1} ; {command-2}\n \
/jsp alias list\n \
/jsp alias delete {alias}\n \
Create a new alias : \n \
/jsp alias set cw = time set {1} ; weather {2}\n \
Execute the alias : \n \
/cw 4000 sun \n \
...is the same as '/time set 4000' and '/weather sun'";
/*
persist aliases
*/
var _store = {
players: {},
global: {}
};
/*
turns 'cw = time set {1} ; weather {2}' into {cmd: 'cw', aliases: ['time set {1}', 'weather {2}']}
_processParams is a private function which takes an array of parameters
used for the 'set' and 'global' options.
*/
var _processParams = function(params){
var paramStr = params.join(' ');
var eqPos = paramStr.indexOf('=');
var aliasCmd = paramStr.substring(0,eqPos).trim();
var aliasValue = paramStr.substring(eqPos+1).trim();
return { cmd: aliasCmd, aliases: aliasValue.split(/\s*;\s*/) };
};
var _set = function(player, params){
var playerAliases = _store.players[player.name];
if (!playerAliases){
playerAliases = {};
}
var o = _processParams(params);
playerAliases[o.cmd] = o.aliases;
_store.players[player.name] = playerAliases;
player.sendMessage("Alias '" + o.cmd + "' created.");
};
var _delete = function(player, params){
if (_store.players[player.name] &&
_store.players[player.name][params[0]]){
delete _store.players[player.name][params[0]];
player.sendMessage("Alias '" + params[0] + "' deleted.");
}
else{
player.sendMessage("Alias '" + params[0] + "' does not exist.");
}
if (player.op){
if (_store.global[params[0]])
delete _store.global[params[0]];
}
};
var _global = function(player, params){
if (!player.op){
player.sendMessage("Only operators can set global aliases. " +
"You need to be an operator to perform this command.");
return;
}
if (params[0] === "set"){
var aliasCmd = params[1];
var cmdStr = params.slice(2).join(' ');
var cmds = cmdStr.split(';');
alias.set(self,aliasCmd,cmds);
var o = _processParams(params);
_store.global[o.cmd] = o.aliases;
player.sendMessage("Global alias '" + o.cmd + "' created.");
};
var _list = function(player){
try {
var alias = 0;
if (_store.players[player.name]){
player.sendMessage("Your aliases:");
for (alias in _store.players[player.name]){
player.sendMessage(alias + " = " +
JSON.stringify(_store.players[player.name][alias]));
}
}else{
player.sendMessage("You have no player-specific aliases.");
}
player.sendMessage("Global aliases:");
for (alias in _store.global){
player.sendMessage(alias + " = " + JSON.stringify(_store.global[alias]) );
}
}catch(e){
logger.severe("Error in list function: " + e.message);
throw e;
}
};
var alias = plugin('alias', {
"store": _store,
"set": _set,
"global": _global,
"delete": _delete,
"list": _list,
"help": function(player){ player.sendMessage("Usage:\n" + _usage);}
}, true );
var aliasCmd = command('alias', function(params){
var operation = params[0];
if (!operation){
self.sendMessage("Usage:\n" + _usage);
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());
if (alias[operation])
alias[operation](self, params.slice(1));
else
self.sendMessage("Usage:\n" + _usage);
});
var _intercept = function( msg, invoker, exec)
{
var msgParts = msg.split(' ');
var command = msg.match(/^\/*([^\s]+)/)[1];
var template = [], isAlias = false, cmds = [];
var playerHasAliases = _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] ? params[index] : "";})
self.performCommand(cmd);
if (_store.global[command]){
template = _store.global[command];
isAlias = true;
}else{
if (config.verbose){
logger.info("No global alias found for command: " + command);
}
}
return true;
/*
allows player-specific aliases to override global aliases
*/
if (_store.players[invoker] &&
_store.players[invoker][command])
{
template = _store.players[invoker][command];
isAlias = true;
}else{
if (config.verbose){
logger.info("No player alias found for command: " + command);
}
}
for (var i = 0;i < template.length; i++)
{
var filledinCommand = template[i].replace(/{([0-9]+)}/g, function (match,index){
index = parseInt(index,10);
if (msgParts[index])
return msgParts[index]
else
return match;
});
cmds.push(filledinCommand);
}
for (var i = 0; i< cmds.length; i++){
exec(cmds[i]);
}
return isAlias;
},["help","set","delete","list"],true);
};
/*
Intercept all command processing and replace with aliased commands if the
command about to be issued matches an alias.
*/
events.on('player.PlayerCommandPreprocessEvent', function(listener,evt){
var invoker = evt.player;
var exec = function(cmd){ invoker.performCommand(cmd);};
var isAlias = _intercept(''+evt.message, ''+invoker.name, exec);
if (isAlias)
evt.cancelled = true;
});
/* define a 'void' command because ServerCommandEvent can't be canceled */
command('void',function(){});
events.on('server.ServerCommandEvent', function(listener,evt){
var invoker = evt.sender;
var exec = function(cmd){ invoker.server.dispatchCommand(invoker, cmd); };
var isAlias = _intercept(''+evt.command, ''+ invoker.name, exec);
if (isAlias)
evt.command = "jsp void";
});

View File

@ -1,7 +1,7 @@
/*************************************************************************
### Commando Plugin
# Commando Plugin
#### Description
## Description
commando is a plugin which can be used to add completely new commands
to Minecraft. Normally ScriptCraft only allows for provision of new
@ -32,9 +32,8 @@ configuration file. It makes approving plugins easier and ensures that
craftbukkit plugins behave well together. While it is possible to
override other plugins' commands, the CraftBukkit team do not
recommend this. However, as ScriptCraft users have suggested, it
should be at the discretion of server administrators and plugin
authors as to when overriding or adding new commands to the global
namespace is good.
should be at the discretion of server administrators as to when
overriding or adding new commands to the global namespace is good.
So this is where `commando()` comes in. It uses the exact same
signature as the core `command()` function but will also make the
@ -43,7 +42,7 @@ type `/jsp hi` for the above command example, players simply type
`/hi` . This functionality is provided as a plugin rather than as part
of the ScriptCraft core.
#### Example hi-command.js
## Example hi-command.js
var commando = require('../commando');
commando('hi', function(){
@ -52,17 +51,17 @@ of the ScriptCraft core.
...Displays a greeting to any player who issues the `/hi` command.
#### Example - timeofday-command.js
## Example - timeofday-command.js
var times = {Dawn: 0, Midday: 6000, Dusk: 12000, Midnight:18000};
commando('timeofday', function(params){
self.location.world.setTime(times[params[0]]);
}
},
['Dawn','Midday','Dusk','Midnight']);
... changes the time of day using a new `/timeofday` command (options are Dawn, Midday, Dusk, Midnight)
#### Caveats
## Caveats
Since commands registered using commando are really just appendages to
the `/jsp` command and are not actually registered globally (it just
@ -77,8 +76,8 @@ global commands for a plugin, please let me know.
[pcppevt]: http://jd.bukkit.org/dev/apidocs/org/bukkit/event/player/PlayerCommandPreprocessEvent.html
***/
var commands = {};
exports.commando = function(name, func, options, intercepts){
var result = command(name, func, options, intercepts);
commands[name] = result;
@ -92,3 +91,10 @@ events.on('player.PlayerCommandPreprocessEvent', function(l,e){
e.message = "/jsp " + msg.substring(1);
}
});
events.on('server.ServerCommandEvent', function(l,e){
var msg = "" + e.command;
var command = msg.match(/^\/*([^\s]+)/)[1];
if (commands[command]){
e.command = "/jsp " + msg.substring(1);
}
});