This repository has been archived on 2021-07-14. You can view files and clone it, but cannot push or open issues or pull requests.
scriptcraft/src/main/javascript/core/_scriptcraft.js

534 lines
18 KiB
JavaScript
Raw Normal View History

2013-02-10 14:42:32 +01:00
/************************************************************************
ScriptCraft API Reference
=========================
Walter Higgins
[walter.higgins@gmail.com][email]
[email]: mailto:walter.higgins@gmail.com?subject=ScriptCraft_API_Reference
2013-02-10 20:36:39 +01:00
Module Loading
==============
At server startup the ScriptCraft Java plugin is loaded and once
loaded the Java plugin will in turn begin loading all of the
javascript (.js) files it finds in the js-plugins directory (in the
current working directory). If this is the first time the ScriptCraft
plugin is loaded, then the js-plugins directory will not yet exist, it
will be created and all of the bundled javascript files will be
unzipped into it from a bundled resource within the Java plugin. The
very first javascript file to load will always be
js-plugins/core/_scriptcraft.js. Then all other javascript files are
loaded.
Directory structure
-------------------
The js-plugins directory is loosely organised into subdirectories -
one for each module. Each subdirectory in turn can contain one or more
javascript files. Within each directory, a javascript file with the
same filename as the directory will always be loaded before all other
files in the same directory. So for example, drone/drone.js will
always load before any other files in the drone/ directory. Similarly
utils/utils.js will always load before any other files in the utils/
directory.
Directories
-----------
As of February 10 2013, the js-plugins directory has the following sub-directories...
* core - Contains javascript files containing Core functionality crucial to ScriptCraft and modules which use it.
* drone - Contains the drone module and drone extensions. Drone was the first scriptcraft module.
* ext - Contains external 3rd party javascript libraries (e.g. json2.js - the JSON lib)
* mini-games - Contains mini-games
* arrows - The arrows module
* signs - The signs module
* chat - The chat plugin/module
* alias - The alias plugin/module
2013-02-10 14:42:32 +01:00
Core Module
===========
This module defines commonly used functions by all plugins...
* load (filename,warnOnFileNotFound) - loads and evaluates a javascript file, returning the evaluated object.
2013-02-10 14:42:32 +01:00
* save (object, filename) - saves an object to a file.
* plugin (name, interface, isPersistent) - defines a new plugin. If
isPersistent is true then the plugin doesn't have to worry about
loading and saving state - that will be done by the framework. Just
make sure that anything you want to save (and restore) is in the
'store' property - this will be created automatically if not
already defined. (its type is object {} )
2013-01-23 01:02:57 +01:00
2013-02-10 14:42:32 +01:00
* ready (function) - specifies code to be executed only when all the plugins have loaded.
2013-02-10 14:42:32 +01:00
* command (name, function) - defines a command that can be used by non-operators.
2013-02-10 20:36:39 +01:00
load() function
---------------
The load() function is used by ScriptCraft at startup to load all of the javascript modules and data.
Parameters
----------
* filenames - An array of file names or a single file name.
* warnOnFileNotFound (optional - default: false) - warn if the file was not found.
Return
------
load() will return the result of the last statement evaluated in the file.
Example
-------
load(__folder + "myFile.js"); // loads a javascript file and evaluates it.
var myData = load("myData.json"); // loads a javascript file and evaluates it - eval'd contents are returned.
myData.json contents...
__data = {players:{
walterh:{
h: ["jsp home {1}"],
sunny:["time set 0",
"weather clear"]
}
}
}
2013-02-10 14:42:32 +01:00
Core Module - Special Variables
===============================
There are a couple of special javascript variables available in ScriptCraft...
2013-02-10 20:36:39 +01:00
* __folder - The current working directory - this variable is only to be used within the main body of a .js file.
2013-02-10 14:42:32 +01:00
* _plugin - The ScriptCraft JavaPlugin object.
* server - The Minecraft Server object.
* self - the current player.
***/
2013-01-08 00:59:42 +01:00
var global = this;
2013-01-18 00:39:43 +01:00
var verbose = verbose || false;
2013-01-25 00:47:36 +01:00
/*
wph 20130124 - make self, plugin and server public - these are far more useful now that tab-complete works.
2013-01-25 00:47:36 +01:00
*/
2013-01-26 14:47:16 +01:00
var server = org.bukkit.Bukkit.server;
2013-01-08 00:59:42 +01:00
//
// private implementation
//
(function(){
2013-01-13 22:06:46 +01:00
//
// don't execute this more than once
//
if (typeof load == "function")
return ;
var _canonize = function(file){ return file.getCanonicalPath().replaceAll("\\\\","/"); };
2013-01-13 22:06:46 +01:00
var _originalScript = __script;
2013-01-18 00:28:12 +01:00
var parentFileObj = new java.io.File(__script).getParentFile();
var jsPluginsRootDir = parentFileObj.getParentFile();
var jsPluginsRootDirName = _canonize(jsPluginsRootDir);
var _loaded = {};
2013-01-18 00:28:12 +01:00
/*
Load the contents of the file and evaluate as javascript
*/
var _load = function(filename,warnOnFileNotFound)
2013-01-18 00:28:12 +01:00
{
var filenames = [];
if (filename.constructor == Array)
filenames = filename;
else
filenames = [filename];
2013-01-18 00:28:12 +01:00
var result = null;
for (var i =0;i < filenames.length; i++) {
var file = new java.io.File(filenames[0]);
var canonizedFilename = _canonize(file);
//
// wph 20130123 don't load the same file more than once.
//
if (_loaded[canonizedFilename])
continue;
if (verbose)
print("loading " + canonizedFilename);
if (file.exists()) {
var parent = file.getParentFile();
var reader = new java.io.FileReader(file);
__engine.put("__script",canonizedFilename);
__engine.put("__folder",(parent?_canonize(parent):"")+"/");
try{
result = __engine.eval(reader);
_loaded[canonizedFilename] = true;
}catch (e){
__plugin.logger.severe("Error evaluating " + canonizedFilename + ", " + e );
}
}else{
if (warnOnFileNotFound)
__plugin.logger.warning(canonizedFilename + " not found");
}
2013-01-13 22:06:46 +01:00
}
2013-01-18 00:28:12 +01:00
return result;
2013-01-13 22:06:46 +01:00
};
2013-01-18 00:28:12 +01:00
/*
recursively walk the given directory and return a list of all .js files
*/
2013-01-13 22:06:46 +01:00
var _listJsFiles = function(store,dir)
{
if (typeof dir == "undefined"){
dir = new java.io.File(_originalScript).getParentFile().getParentFile();
2013-01-13 22:06:46 +01:00
}
var files = dir.listFiles();
for (var i = 0;i < files.length; i++){
2013-01-18 00:28:12 +01:00
var file = files[i];
if (file.isDirectory()){
_listJsFiles(store,file);
2013-01-13 22:06:46 +01:00
}else{
if (file.getCanonicalPath().endsWith(".js") &&
!(file.getName().startsWith("_")) &&
2013-01-18 00:28:12 +01:00
file.exists())
2013-01-13 22:06:46 +01:00
{
store.push(file);
2013-01-13 22:06:46 +01:00
}
}
}
};
/*
sort so that .js files with same name as parent directory appear before
other files in the same directory
*/
var sortByModule = function(a,b){
var aparts = (""+a).split(/\//);
var bparts = (""+b).split(/\//);
var adir = aparts[aparts.length-2];
var afile = aparts[aparts.length-1];
var bdir = bparts[bparts.length-2];
var bfile = bparts[bparts.length-1];
if(adir<bdir) return -1;
if(adir>bdir) return 1;
if (afile.indexOf(adir) == 0)
return -1;
else
return 1;
};
2013-01-18 00:28:12 +01:00
/*
Reload all of the .js files in the given directory
*/
2013-01-13 22:06:46 +01:00
var _reload = function(pluginDir)
{
_loaded = [];
2013-01-13 22:06:46 +01:00
var jsFiles = [];
_listJsFiles(jsFiles,pluginDir);
jsFiles.sort(sortByModule);
2013-01-13 22:06:46 +01:00
//
// script files whose name begins with _ (underscore)
// will not be loaded automatically at startup.
// These files are assumed to be dependencies/private to plugins
//
// E.g. If you have a plugin called myMiniGame.js in the myMiniGame directory
// and which in addition to myMiniGame.js also includes _myMiniGame_currency.js _myMiniGame_events.js etc.
// then it's assumed that _myMiniGame_currency.js and _myMiniGame_events.js will be loaded
// as dependencies by myMiniGame.js and do not need to be loaded via js reload
//
var len = jsFiles.length;
for (var i = 0;i < len; i++){
2013-01-23 01:02:57 +01:00
load(_canonize(jsFiles[i]),true);
2013-01-13 22:06:46 +01:00
}
};
2013-01-18 00:28:12 +01:00
/*
Save a javascript object to a file (saves using JSON notation)
*/
var _save = function(object, filename){
2013-01-23 01:02:57 +01:00
var objectToStr = null;
try{
objectToStr = JSON.stringify(object);
}catch(e){
print("ERROR: " + e.getMessage() + " while saving " + filename);
return;
}
2013-01-18 00:28:12 +01:00
var f = new java.io.File(filename);
var out = new java.io.PrintWriter(new java.io.FileWriter(f));
out.println("__data = " + objectToStr);
out.close();
};
/*
plugin management
*/
var _plugins = {};
var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPersistent)
{
//
// don't load plugin more than once
//
if (typeof _plugins[moduleName] != "undefined")
return;
2013-01-18 00:28:12 +01:00
var pluginData = {persistent: isPersistent, module: moduleObject};
moduleObject.store = moduleObject.store || {};
_plugins[moduleName] = pluginData;
2013-01-18 00:28:12 +01:00
if (isPersistent)
moduleObject.store = load(jsPluginsRootDirName + "/" + moduleName + "-store.txt") || {};
global[moduleName] = moduleObject;
return moduleObject;
};
/*
allow for deferred execution (once all modules have loaded)
*/
var _deferred = [];
var _ready = function( func ){
_deferred.push(func);
};
2013-01-23 01:02:57 +01:00
var _cmdInterceptors = [];
2013-01-18 00:28:12 +01:00
/*
command management - allow for non-ops to execute approved javascript code.
*/
var _commands = {};
var _command = function(name,func,options,intercepts)
{
2013-01-18 00:28:12 +01:00
if (typeof name == "undefined"){
// it's an invocation from the Java Plugin!
if (__cmdArgs.length === 0)
throw new Error("Usage: jsp command-name command-parameters");
var name = __cmdArgs[0];
2013-01-23 01:02:57 +01:00
var cmd = _commands[name];
if (typeof cmd === "undefined"){
2013-01-23 01:02:57 +01:00
// it's not a global command - pass it on to interceptors
var intercepted = false;
for (var i = 0;i < _cmdInterceptors.length;i++){
if (_cmdInterceptors[i](__cmdArgs))
intercepted = true;
}
if (!intercepted)
2013-01-25 00:47:36 +01:00
self.sendMessage("Command '" + name + "' is not recognised");
2013-01-23 01:02:57 +01:00
}else{
func = cmd.callback;
var params = [];
for (var i =1; i < __cmdArgs.length;i++){
params.push("" + __cmdArgs[i]);
}
2013-01-18 00:28:12 +01:00
return func(params);
2013-01-23 01:02:57 +01:00
}
2013-01-18 00:28:12 +01:00
}else{
2013-01-23 01:02:57 +01:00
if (typeof options == "undefined")
options = [];
_commands[name] = {callback: func, options: options};
2013-01-23 01:02:57 +01:00
if (intercepts)
_cmdInterceptors.push(func);
2013-01-18 00:28:12 +01:00
return func;
}
};
2013-01-23 01:02:57 +01:00
var _rmCommand = function(name){
delete _commands[name];
};
2013-01-18 00:28:12 +01:00
/*
Tab Completion of the /js and /jsp commands
*/
2013-01-13 22:06:46 +01:00
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))
2013-01-18 00:28:12 +01:00
{
2013-01-13 22:06:46 +01:00
propertyLoop:
for (var i in o)
2013-01-18 00:28:12 +01:00
{
2013-01-13 22:06:46 +01:00
//
// don't include standard Object methods
//
var isObjectMethod = false;
for (var j = 0;j < _javaLangObjectMethods.length; j++)
if (_javaLangObjectMethods[j] == i)
continue propertyLoop;
2013-01-26 14:47:16 +01:00
if (typeof o[i] == "function" )
2013-01-13 22:06:46 +01:00
result.push(i+"()");
else
result.push(i);
}
}else{
for (var i in o){
if (i.match(/^[^_]/)){
if (typeof o[i] == "function")
result.push(i+"()");
else
result.push(i);
}
}
}
return result.sort();
};
2013-01-18 00:28:12 +01:00
/*
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);
}
}
}
}
2013-01-18 00:28:12 +01:00
return result;
};
/*
Tab completion for the /js command
*/
var __onTabCompleteJS = function()
2013-01-13 22:06:46 +01:00
{
2013-01-18 00:28:12 +01:00
if (__onTC_cmd.name == "jsp")
return __onTabCompleteJSP()
2013-01-13 22:06:46 +01:00
var _globalSymbols = _getProperties(global)
var result = global.__onTC_result;
var args = global.__onTC_args;
var propsOfLastArg = [];
var statement = args.join(" ");
2013-01-18 00:28:12 +01:00
statement = statement.replace(/^\s+/,"").replace(/\s+$/,"");
if (statement.length == 0)
2013-01-18 00:28:12 +01:00
propsOfLastArg = _globalSymbols;
else{
var statementSyms = statement.split(/[^\$a-zA-Z0-9_\.]/);
2013-01-13 22:06:46 +01:00
var lastSymbol = statementSyms[statementSyms.length-1];
//
// 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;
}
if (typeof symbol == "undefined"){
2013-01-18 00:28:12 +01:00
//
2013-01-13 22:06:46 +01:00
// look up partial matches against last good symbol
//
var objectProps = _getProperties(lastGoodSymbol);
if (name == ""){
2013-01-18 00:28:12 +01:00
// if the last symbol looks like this..
// ScriptCraft.
//
2013-01-13 22:06:46 +01:00
for (var i =0;i < objectProps.length;i++)
propsOfLastArg.push(statement+objectProps[i]);
2013-01-18 00:28:12 +01:00
}else{
// it looks like this..
// ScriptCraft.co
//
var li = statement.lastIndexOf(name);
statement = statement.substring(0,li);
2013-01-08 00:59:42 +01:00
for (var i = 0; i < objectProps.length;i++)
if (objectProps[i].indexOf(name) == 0)
2013-01-13 22:06:46 +01:00
propsOfLastArg.push(statement + objectProps[i]);
2013-01-18 00:28:12 +01:00
2013-01-13 22:06:46 +01:00
}
}else{
var objectProps = _getProperties(symbol);
for (var i = 0; i < objectProps.length; i++){
propsOfLastArg.push(statement + "." + objectProps[i]);
}
2013-01-13 22:06:46 +01:00
}
}else{
// loop thru globalSymbols looking for a good match
for (var i = 0;i < _globalSymbols.length; i++)
if (_globalSymbols[i].indexOf(lastSymbol) == 0)
2013-01-13 22:06:46 +01:00
propsOfLastArg.push(statement.replace(lastSymbol,_globalSymbols[i]));
2013-01-18 00:28:12 +01:00
2013-01-13 22:06:46 +01:00
}
}
for (var i = 0;i < propsOfLastArg.length; i++)
2013-01-13 22:06:46 +01:00
result.add(propsOfLastArg[i]);
};
2013-01-18 00:28:12 +01:00
global.load = _load;
global.save = _save;
global.plugin = _plugin;
global.ready = _ready;
global.command = _command;
global._onTabComplete = __onTabCompleteJS;
2013-01-13 22:06:46 +01:00
//
// assumes this was loaded from js-plugins/core/
2013-01-18 00:28:12 +01:00
// load all of the plugins.
2013-01-13 22:06:46 +01:00
//
2013-01-24 21:22:22 +01:00
_reload(jsPluginsRootDir);
2013-01-13 22:06:46 +01:00
2013-01-18 00:28:12 +01:00
//
// all modules have loaded
//
for (var i =0;i < _deferred.length;i++)
_deferred[i]();
events.on("server.PluginDisableEvent",function(l,e){
//
// save all plugins which have persistent data
//
for (var moduleName in _plugins){
var pluginData = _plugins[moduleName];
if (pluginData.persistent)
save(pluginData.module.store, jsPluginsRootDirName + "/" + moduleName + "-store.txt");
}
});
2013-01-13 22:06:46 +01:00
}());
2013-01-08 00:59:42 +01:00