Improvements to classroom.js module (added players/ directory into which players can drop their custom scripts when classroom.allowScripting(true) is called.
This commit is contained in:
parent
39b459ab7f
commit
8453525da6
6 changed files with 327 additions and 87 deletions
|
@ -68,6 +68,8 @@ Walter Higgins
|
||||||
* [utils.nicely() function](#utilsnicely-function)
|
* [utils.nicely() function](#utilsnicely-function)
|
||||||
* [utils.at() function](#utilsat-function)
|
* [utils.at() function](#utilsat-function)
|
||||||
* [utils.find() function](#utilsfind-function)
|
* [utils.find() function](#utilsfind-function)
|
||||||
|
* [utils.serverAddress() function](#utilsserveraddress-function)
|
||||||
|
* [utils.watchFile() function](#utilswatchfile-function)
|
||||||
* [Drone Plugin](#drone-plugin)
|
* [Drone Plugin](#drone-plugin)
|
||||||
* [TLDNR; (Just read this if you're impatient)](#tldnr-just-read-this-if-youre-impatient)
|
* [TLDNR; (Just read this if you're impatient)](#tldnr-just-read-this-if-youre-impatient)
|
||||||
* [Constructing a Drone Object](#constructing-a-drone-object)
|
* [Constructing a Drone Object](#constructing-a-drone-object)
|
||||||
|
@ -1338,6 +1340,34 @@ var jsFiles = utils.find('./', function(dir,name){
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### utils.serverAddress() function
|
||||||
|
|
||||||
|
The utils.serverAddress() function returns the IP(v4) address of the server.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var utils = require('utils');
|
||||||
|
var serverAddress = utils.serverAddress();
|
||||||
|
console.log(serverAddress);
|
||||||
|
```
|
||||||
|
### utils.watchFile() function
|
||||||
|
|
||||||
|
Watches for changes to the given file or directory and calls the function provided
|
||||||
|
when the file changes.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* File - the file to watch (can be a file or directory)
|
||||||
|
* Callback - The callback to invoke when the file has changed. The callback takes the
|
||||||
|
changed file as a parameter.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var utils = require('utils');
|
||||||
|
utils.watchFile( 'test.txt', function( file ) {
|
||||||
|
console.log( file + ' has changed');
|
||||||
|
});
|
||||||
|
```
|
||||||
## Drone Plugin
|
## Drone Plugin
|
||||||
|
|
||||||
The Drone is a convenience class for building. It can be used for...
|
The Drone is a convenience class for building. It can be used for...
|
||||||
|
@ -2560,8 +2590,44 @@ quickly realise how to grant themselves and others operator privileges
|
||||||
once they have access to ScriptCraft).
|
once they have access to ScriptCraft).
|
||||||
|
|
||||||
The goal of this module is not so much to enforce restrictions
|
The goal of this module is not so much to enforce restrictions
|
||||||
(security or otherwise) but to make it easier for tutors to setup a shared server
|
(security or otherwise) but to make it easier for tutors to setup a
|
||||||
so students can learn Javascript.
|
shared server so students can learn Javascript. When scripting is
|
||||||
|
turned on, every player who joins the server will have a dedicated
|
||||||
|
directory into which they can save scripts. All scripts in such
|
||||||
|
directories are automatically watched and loaded into a global
|
||||||
|
variable named after the player.
|
||||||
|
|
||||||
|
So for example, if player 'walterh' joins the server, a `walterh`
|
||||||
|
global variable is created. If a file `greet.js` with the following
|
||||||
|
content is dropped into the `plugins/scriptcraft/players/walterh`
|
||||||
|
directory...
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
exports.hi = function( player ){
|
||||||
|
player.sendMessage('Hi ' + player.name);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
... then it can be invoked like this: `/js walterh.hi( self )` . This
|
||||||
|
lets every player/student create their own functions without having
|
||||||
|
naming collisions.
|
||||||
|
|
||||||
|
It's strongly recommended that the
|
||||||
|
`craftbukkit/plugins/scriptcraft/players/` directory is shared so that
|
||||||
|
others can connect to it and drop .js files into their student
|
||||||
|
directories. On Ubuntu, select the folder in Nautilus (the default
|
||||||
|
file browser) then right-click and choose *Sharing Options*, check the
|
||||||
|
*Share this folder* checkbox and the *Allow others to create and
|
||||||
|
delete files* and *Guest access* checkboxes. Click *Create Share*
|
||||||
|
button to close the sharing options dialog. Students can then access
|
||||||
|
the shared folder as follows...
|
||||||
|
|
||||||
|
* Windows: Open Explorer, Go to \\{serverAddress}\players\
|
||||||
|
* Macintosh: Open Finder, Go to smb://{serverAddress}/players/
|
||||||
|
* Linux: Open Nautilus, Go to smb://{serverAddress}/players/
|
||||||
|
|
||||||
|
... where {serverAddress} is the ip address of the server (this is
|
||||||
|
displayed to whoever invokes the classroom.allowScripting() function.)
|
||||||
|
|
||||||
### classroom.allowScripting() function
|
### classroom.allowScripting() function
|
||||||
|
|
||||||
|
@ -2569,6 +2635,11 @@ Allow or disallow anyone who connects to the server (or is already
|
||||||
connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
|
connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
|
||||||
to every student in a Minecraft classroom environment.
|
to every student in a Minecraft classroom environment.
|
||||||
|
|
||||||
|
Whenever any file is added/edited or removed from any of the players/
|
||||||
|
directories the contents are automatically reloaded. This is to
|
||||||
|
facilitate quick turnaround time for students getting to grips with
|
||||||
|
Javascript.
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
* canScript : true or false
|
* canScript : true or false
|
||||||
|
|
|
@ -31,15 +31,7 @@ var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPer
|
||||||
|
|
||||||
exports.plugin = _plugin;
|
exports.plugin = _plugin;
|
||||||
|
|
||||||
var scriptCraftDir = null;
|
exports.autoload = function( context, pluginDir, logger, options ) {
|
||||||
var pluginDir = null;
|
|
||||||
var dataDir = null;
|
|
||||||
|
|
||||||
exports.autoload = function( dir, logger ) {
|
|
||||||
|
|
||||||
scriptCraftDir = dir;
|
|
||||||
pluginDir = new File( dir, 'plugins' );
|
|
||||||
dataDir = new File( dir, 'data' );
|
|
||||||
|
|
||||||
var _canonize = function( file ) {
|
var _canonize = function( file ) {
|
||||||
return '' + file.canonicalPath.replaceAll('\\\\','/');
|
return '' + file.canonicalPath.replaceAll('\\\\','/');
|
||||||
|
@ -76,22 +68,26 @@ exports.autoload = function( dir, logger ) {
|
||||||
|
|
||||||
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 in ' + pluginDir );
|
||||||
}
|
}
|
||||||
for ( var i = 0; i < len; i++ ) {
|
for ( var i = 0; i < len; i++ ) {
|
||||||
pluginPath = _canonize( sourceFiles[i] );
|
pluginPath = _canonize( sourceFiles[i] );
|
||||||
module = {};
|
module = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
module = require( pluginPath );
|
module = require( pluginPath , options);
|
||||||
for ( property in module ) {
|
for ( property in module ) {
|
||||||
/*
|
/*
|
||||||
all exports in plugins become global
|
all exports in plugins become members of context object
|
||||||
*/
|
*/
|
||||||
global[property] = module[property];
|
context[property] = module[property];
|
||||||
}
|
}
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
logger.severe( 'Plugin ' + pluginPath + ' ' + e );
|
if ( typeof logger != 'undefined' ) {
|
||||||
|
logger.severe( 'Plugin ' + pluginPath + ' ' + e );
|
||||||
|
} else {
|
||||||
|
java.lang.System.out.println( 'Error: Plugin ' + pluginPath + ' ' + e );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(pluginDir));
|
}(pluginDir));
|
||||||
|
|
|
@ -136,57 +136,56 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
|
||||||
|
|
||||||
***/
|
***/
|
||||||
var resolveModuleToFile = function ( moduleName, parentDir ) {
|
var resolveModuleToFile = function ( moduleName, parentDir ) {
|
||||||
var file = new File(moduleName);
|
var file = new File(moduleName),
|
||||||
|
i = 0,
|
||||||
|
pathWithJSExt,
|
||||||
|
resolvedFile;
|
||||||
if ( file.exists() ) {
|
if ( file.exists() ) {
|
||||||
return fileExists(file);
|
return fileExists(file);
|
||||||
}
|
}
|
||||||
if ( moduleName.match( /^[^\.\/]/ ) ) {
|
if ( moduleName.match( /^[^\.\/]/ ) ) {
|
||||||
// it's a module named like so ... 'events' , 'net/http'
|
// it's a module named like so ... 'events' , 'net/http'
|
||||||
//
|
//
|
||||||
var resolvedFile;
|
for ( ; i < modulePaths.length; i++ ) {
|
||||||
for (var i = 0;i < modulePaths.length; i++){
|
|
||||||
resolvedFile = new File(modulePaths[i] + moduleName);
|
resolvedFile = new File(modulePaths[i] + moduleName);
|
||||||
if (resolvedFile.exists()){
|
if ( resolvedFile.exists() ) {
|
||||||
return fileExists(resolvedFile);
|
return fileExists(resolvedFile);
|
||||||
}else{
|
} else {
|
||||||
// try appending a .js to the end
|
// try appending a .js to the end
|
||||||
resolvedFile = new File(modulePaths[i] + moduleName + '.js');
|
resolvedFile = new File(modulePaths[i] + moduleName + '.js');
|
||||||
if (resolvedFile.exists())
|
if ( resolvedFile.exists() ) {
|
||||||
return resolvedFile;
|
return resolvedFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// it's of the form ./path
|
// it's of the form ./path
|
||||||
file = new File(parentDir, moduleName);
|
file = new File(parentDir, moduleName);
|
||||||
if (file.exists()){
|
if ( file.exists() ) {
|
||||||
return fileExists(file);
|
return fileExists(file);
|
||||||
}else {
|
} else {
|
||||||
|
|
||||||
// try appending a .js to the end
|
// try appending a .js to the end
|
||||||
var pathWithJSExt = file.canonicalPath + '.js';
|
pathWithJSExt = file.canonicalPath + '.js';
|
||||||
file = new File( parentDir, pathWithJSExt);
|
file = new File( parentDir, pathWithJSExt );
|
||||||
if (file.exists())
|
if (file.exists()) {
|
||||||
return file;
|
return file;
|
||||||
else{
|
} else {
|
||||||
file = new File(pathWithJSExt);
|
file = new File(pathWithJSExt);
|
||||||
if (file.exists())
|
if ( file.exists() ) {
|
||||||
return file;
|
return file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
/*
|
|
||||||
wph 20131215 Experimental
|
|
||||||
*/
|
|
||||||
var _loadedModules = {};
|
var _loadedModules = {};
|
||||||
var _format = java.lang.String.format;
|
var _format = java.lang.String.format;
|
||||||
/*
|
/*
|
||||||
require() function implementation
|
require() function implementation
|
||||||
*/
|
*/
|
||||||
var _require = function( parentFile, path ) {
|
var _require = function( parentFile, path, options ) {
|
||||||
var file,
|
var file,
|
||||||
canonizedFilename,
|
canonizedFilename,
|
||||||
moduleInfo,
|
moduleInfo,
|
||||||
|
@ -194,7 +193,15 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
|
||||||
head = '(function(exports,module,require,__filename,__dirname){ ',
|
head = '(function(exports,module,require,__filename,__dirname){ ',
|
||||||
code = '',
|
code = '',
|
||||||
line = null;
|
line = null;
|
||||||
|
|
||||||
|
if ( typeof options == 'undefined' ) {
|
||||||
|
options = { cache: true };
|
||||||
|
} else {
|
||||||
|
if ( typeof options.cache == 'undefined' ) {
|
||||||
|
options.cache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
file = resolveModuleToFile(path, parentFile);
|
file = resolveModuleToFile(path, parentFile);
|
||||||
if ( !file ) {
|
if ( !file ) {
|
||||||
var errMsg = '' + _format("require() failed to find matching file for module '%s' " +
|
var errMsg = '' + _format("require() failed to find matching file for module '%s' " +
|
||||||
|
@ -205,10 +212,12 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
|
||||||
throw errMsg;
|
throw errMsg;
|
||||||
}
|
}
|
||||||
canonizedFilename = _canonize(file);
|
canonizedFilename = _canonize(file);
|
||||||
|
|
||||||
moduleInfo = _loadedModules[canonizedFilename];
|
moduleInfo = _loadedModules[canonizedFilename];
|
||||||
if ( moduleInfo ) {
|
if ( moduleInfo ) {
|
||||||
return moduleInfo;
|
if ( options.cache ) {
|
||||||
|
return moduleInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( hooks ) {
|
if ( hooks ) {
|
||||||
hooks.loading( canonizedFilename );
|
hooks.loading( canonizedFilename );
|
||||||
|
@ -228,7 +237,9 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
|
||||||
var tail = '})';
|
var tail = '})';
|
||||||
code = head + code + tail;
|
code = head + code + tail;
|
||||||
|
|
||||||
_loadedModules[canonizedFilename] = moduleInfo;
|
if ( options.cache ) {
|
||||||
|
_loadedModules[canonizedFilename] = moduleInfo;
|
||||||
|
}
|
||||||
var compiledWrapper = null;
|
var compiledWrapper = null;
|
||||||
try {
|
try {
|
||||||
compiledWrapper = eval(code);
|
compiledWrapper = eval(code);
|
||||||
|
@ -258,8 +269,8 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
|
||||||
};
|
};
|
||||||
|
|
||||||
var _requireClosure = function( parent ) {
|
var _requireClosure = function( parent ) {
|
||||||
return function( path ) {
|
return function( path, options ) {
|
||||||
var module = _require( parent, path );
|
var module = _require( parent, path , options);
|
||||||
return module.exports;
|
return module.exports;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -491,7 +491,7 @@ function __onEnable ( __engine, __plugin, __script )
|
||||||
try {
|
try {
|
||||||
while ( (r = br.readLine()) !== null ) {
|
while ( (r = br.readLine()) !== null ) {
|
||||||
code += r + '\n';
|
code += r + '\n';
|
||||||
}
|
}
|
||||||
wrappedCode = '(' + code + ')';
|
wrappedCode = '(' + code + ')';
|
||||||
result = __engine.eval( wrappedCode );
|
result = __engine.eval( wrappedCode );
|
||||||
// issue #103 avoid side-effects of || operator on Mac Rhino
|
// issue #103 avoid side-effects of || operator on Mac Rhino
|
||||||
|
@ -566,7 +566,7 @@ function __onEnable ( __engine, __plugin, __script )
|
||||||
setup paths to search for modules
|
setup paths to search for modules
|
||||||
*/
|
*/
|
||||||
var modulePaths = [ jsPluginsRootDirName + '/lib/',
|
var modulePaths = [ jsPluginsRootDirName + '/lib/',
|
||||||
jsPluginsRootDirName + '/modules/' ];
|
jsPluginsRootDirName + '/modules/' ];
|
||||||
|
|
||||||
if ( config.verbose ) {
|
if ( config.verbose ) {
|
||||||
logger.info( 'Setting up CommonJS-style module system. Root Directory: ' + jsPluginsRootDirName );
|
logger.info( 'Setting up CommonJS-style module system. Root Directory: ' + jsPluginsRootDirName );
|
||||||
|
@ -612,27 +612,44 @@ function __onEnable ( __engine, __plugin, __script )
|
||||||
|
|
||||||
|
|
||||||
global.__onCommand = function( sender, cmd, label, args) {
|
global.__onCommand = function( sender, cmd, label, args) {
|
||||||
var jsArgs = [];
|
var jsArgs = [],
|
||||||
var i = 0;
|
i = 0,
|
||||||
|
jsResult,
|
||||||
|
result,
|
||||||
|
cmdName,
|
||||||
|
fnBody;
|
||||||
for ( ; i < args.length ; i++ ) {
|
for ( ; i < args.length ; i++ ) {
|
||||||
jsArgs.push( '' + args[i] );
|
jsArgs.push( '' + args[i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = false;
|
result = false;
|
||||||
var cmdName = ( '' + cmd.name ).toLowerCase();
|
cmdName = ( '' + cmd.name ).toLowerCase();
|
||||||
if (cmdName == 'js')
|
if (cmdName == 'js')
|
||||||
{
|
{
|
||||||
result = true;
|
result = true;
|
||||||
var fnBody = jsArgs.join(' ');
|
fnBody = jsArgs.join(' ');
|
||||||
global.self = sender;
|
global.self = sender;
|
||||||
global.__engine = __engine;
|
global.__engine = __engine;
|
||||||
try {
|
try {
|
||||||
var jsResult = __engine.eval(fnBody);
|
if ( typeof eval == 'undefined' ) {
|
||||||
if ( jsResult ) {
|
jsResult = __engine.eval(fnBody);
|
||||||
sender.sendMessage(jsResult);
|
} else {
|
||||||
}
|
/*
|
||||||
|
nashorn
|
||||||
|
https://bugs.openjdk.java.net/browse/JDK-8034055
|
||||||
|
*/
|
||||||
|
jsResult = eval( fnBody );
|
||||||
|
}
|
||||||
|
if ( typeof jsResult != 'undefined' ) {
|
||||||
|
if ( jsResult == null) {
|
||||||
|
sender.sendMessage('(null)');
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(jsResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch ( e ) {
|
} catch ( e ) {
|
||||||
logger.severe( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
|
logger.severe( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
|
||||||
|
sender.sendMessage( 'Error while trying to evaluate javascript: ' + fnBody + ', Error: '+ e );
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
delete global.self;
|
delete global.self;
|
||||||
|
@ -646,7 +663,7 @@ function __onEnable ( __engine, __plugin, __script )
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
plugins.autoload( jsPluginsRootDir, logger );
|
plugins.autoload( global, new File(jsPluginsRootDir,'plugins'), logger );
|
||||||
/*
|
/*
|
||||||
wph 20140102 - warn if legacy 'craftbukkit/js-plugins' or 'craftbukkit/scriptcraft' directories are present
|
wph 20140102 - warn if legacy 'craftbukkit/js-plugins' or 'craftbukkit/scriptcraft' directories are present
|
||||||
*/
|
*/
|
||||||
|
@ -655,21 +672,21 @@ function __onEnable ( __engine, __plugin, __script )
|
||||||
cbDir = new File(cbPluginsDir.canonicalPath).parentFile,
|
cbDir = new File(cbPluginsDir.canonicalPath).parentFile,
|
||||||
legacyExists = false,
|
legacyExists = false,
|
||||||
legacyDirs = [new File( cbDir, 'js-plugins' ),
|
legacyDirs = [new File( cbDir, 'js-plugins' ),
|
||||||
new File( cbDir, 'scriptcraft' )];
|
new File( cbDir, 'scriptcraft' )];
|
||||||
|
|
||||||
for ( var i = 0; i < legacyDirs.length; i++ ) {
|
for ( var i = 0; i < legacyDirs.length; i++ ) {
|
||||||
if ( legacyDirs[i].exists()
|
if ( legacyDirs[i].exists()
|
||||||
&& legacyDirs[i].isDirectory() ) {
|
&& legacyDirs[i].isDirectory() ) {
|
||||||
|
|
||||||
legacyExists = true;
|
legacyExists = true;
|
||||||
|
|
||||||
console.warn('Legacy ScriptCraft directory %s 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);
|
legacyDirs[i].canonicalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( legacyExists ) {
|
if ( legacyExists ) {
|
||||||
console.info( 'Please note that the working directory for %s is %s',
|
console.info( 'Please note that the working directory for %s is %s',
|
||||||
__plugin, jsPluginsRootDir.canonicalPath );
|
__plugin, jsPluginsRootDir.canonicalPath );
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -459,3 +459,79 @@ exports.find = function( dir , filter ) {
|
||||||
recurse( dir, result );
|
recurse( dir, result );
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
/************************************************************************
|
||||||
|
### utils.serverAddress() function
|
||||||
|
|
||||||
|
The utils.serverAddress() function returns the IP(v4) address of the server.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var utils = require('utils');
|
||||||
|
var serverAddress = utils.serverAddress();
|
||||||
|
console.log(serverAddress);
|
||||||
|
```
|
||||||
|
***/
|
||||||
|
exports.serverAddress = function() {
|
||||||
|
var interfaces = java.net.NetworkInterface.getNetworkInterfaces();
|
||||||
|
var current,
|
||||||
|
addresses,
|
||||||
|
current_addr;
|
||||||
|
while ( interfaces.hasMoreElements() ) {
|
||||||
|
current = interfaces.nextElement();
|
||||||
|
if ( ! current.isUp() || current.isLoopback() || current.isVirtual() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addresses = current.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
current_addr = addresses.nextElement();
|
||||||
|
if ( current_addr.isLoopbackAddress() )
|
||||||
|
continue;
|
||||||
|
if ( current_addr instanceof java.net.Inet4Address)
|
||||||
|
return current_addr.getHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
/************************************************************************
|
||||||
|
### utils.watchFile() function
|
||||||
|
|
||||||
|
Watches for changes to the given file or directory and calls the function provided
|
||||||
|
when the file changes.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* File - the file to watch (can be a file or directory)
|
||||||
|
* Callback - The callback to invoke when the file has changed. The callback takes the
|
||||||
|
changed file as a parameter.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var utils = require('utils');
|
||||||
|
utils.watchFile( 'test.txt', function( file ) {
|
||||||
|
console.log( file + ' has changed');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
***/
|
||||||
|
var filesWatched = {};
|
||||||
|
exports.watchFile = function( file, callback ) {
|
||||||
|
var File = java.io.File;
|
||||||
|
if ( typeof file == 'string' ) {
|
||||||
|
file = new File(file);
|
||||||
|
}
|
||||||
|
filesWatched[file.canonicalPath] = {
|
||||||
|
callback: callback,
|
||||||
|
lastModified: file.lastModified()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
function fileWatcher() {
|
||||||
|
for (var file in filesWatched) {
|
||||||
|
var fileObject = new File(file);
|
||||||
|
var lm = fileObject.lastModified();
|
||||||
|
if ( lm != filesWatched[file].lastModified ) {
|
||||||
|
filesWatched[file].lastModified = lm;
|
||||||
|
filesWatched[file].callback(fileObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout( fileWatcher, 5000 );
|
||||||
|
};
|
||||||
|
setTimeout( fileWatcher, 5000 );
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
var foreach = require('utils').foreach;
|
var utils = require('utils'),
|
||||||
|
autoload = require('plugin').autoload,
|
||||||
|
logger = __plugin.logger,
|
||||||
|
foreach = utils.foreach,
|
||||||
|
watchFile = utils.watchFile,
|
||||||
|
playersDir = __dirname + '/../../players/',
|
||||||
|
serverAddress = utils.serverAddress();
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
## Classroom Plugin
|
## Classroom Plugin
|
||||||
|
@ -14,8 +20,44 @@ quickly realise how to grant themselves and others operator privileges
|
||||||
once they have access to ScriptCraft).
|
once they have access to ScriptCraft).
|
||||||
|
|
||||||
The goal of this module is not so much to enforce restrictions
|
The goal of this module is not so much to enforce restrictions
|
||||||
(security or otherwise) but to make it easier for tutors to setup a shared server
|
(security or otherwise) but to make it easier for tutors to setup a
|
||||||
so students can learn Javascript.
|
shared server so students can learn Javascript. When scripting is
|
||||||
|
turned on, every player who joins the server will have a dedicated
|
||||||
|
directory into which they can save scripts. All scripts in such
|
||||||
|
directories are automatically watched and loaded into a global
|
||||||
|
variable named after the player.
|
||||||
|
|
||||||
|
So for example, if player 'walterh' joins the server, a `walterh`
|
||||||
|
global variable is created. If a file `greet.js` with the following
|
||||||
|
content is dropped into the `plugins/scriptcraft/players/walterh`
|
||||||
|
directory...
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
exports.hi = function( player ){
|
||||||
|
player.sendMessage('Hi ' + player.name);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
... then it can be invoked like this: `/js walterh.hi( self )` . This
|
||||||
|
lets every player/student create their own functions without having
|
||||||
|
naming collisions.
|
||||||
|
|
||||||
|
It's strongly recommended that the
|
||||||
|
`craftbukkit/plugins/scriptcraft/players/` directory is shared so that
|
||||||
|
others can connect to it and drop .js files into their student
|
||||||
|
directories. On Ubuntu, select the folder in Nautilus (the default
|
||||||
|
file browser) then right-click and choose *Sharing Options*, check the
|
||||||
|
*Share this folder* checkbox and the *Allow others to create and
|
||||||
|
delete files* and *Guest access* checkboxes. Click *Create Share*
|
||||||
|
button to close the sharing options dialog. Students can then access
|
||||||
|
the shared folder as follows...
|
||||||
|
|
||||||
|
* Windows: Open Explorer, Go to \\{serverAddress}\players\
|
||||||
|
* Macintosh: Open Finder, Go to smb://{serverAddress}/players/
|
||||||
|
* Linux: Open Nautilus, Go to smb://{serverAddress}/players/
|
||||||
|
|
||||||
|
... where {serverAddress} is the ip address of the server (this is
|
||||||
|
displayed to whoever invokes the classroom.allowScripting() function.)
|
||||||
|
|
||||||
### classroom.allowScripting() function
|
### classroom.allowScripting() function
|
||||||
|
|
||||||
|
@ -23,6 +65,11 @@ Allow or disallow anyone who connects to the server (or is already
|
||||||
connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
|
connected) to use ScriptCraft. This function is preferable to granting 'ops' privileges
|
||||||
to every student in a Minecraft classroom environment.
|
to every student in a Minecraft classroom environment.
|
||||||
|
|
||||||
|
Whenever any file is added/edited or removed from any of the players/
|
||||||
|
directories the contents are automatically reloaded. This is to
|
||||||
|
facilitate quick turnaround time for students getting to grips with
|
||||||
|
Javascript.
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
* canScript : true or false
|
* canScript : true or false
|
||||||
|
@ -42,51 +89,73 @@ Only ops users can run the classroom.allowScripting() function - this is so that
|
||||||
don't try to bar themselves and each other from scripting.
|
don't try to bar themselves and each other from scripting.
|
||||||
|
|
||||||
***/
|
***/
|
||||||
var _store = { enableScripting: false };
|
var _store = { enableScripting: false },
|
||||||
|
File = java.io.File;
|
||||||
|
|
||||||
|
function revokeScripting ( player ) {
|
||||||
|
foreach( player.getEffectivePermissions(), function( perm ) {
|
||||||
|
if ( (''+perm.permission).indexOf( 'scriptcraft.' ) == 0 ) {
|
||||||
|
if ( perm.attachment ) {
|
||||||
|
perm.attachment.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function grantScripting( player ) {
|
||||||
|
console.log('Enabling scripting for player ' + player.name);
|
||||||
|
var playerName = '' + player.name;
|
||||||
|
playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,'');
|
||||||
|
var playerDir = new File( playersDir + playerName );
|
||||||
|
playerDir.mkdirs();
|
||||||
|
player.addAttachment( __plugin, 'scriptcraft.*', true );
|
||||||
|
var playerContext = {};
|
||||||
|
autoload( playerContext, playerDir, logger, { cache: false });
|
||||||
|
global[playerName] = playerContext;
|
||||||
|
|
||||||
|
watchFile( playerDir, function( changedDir ){
|
||||||
|
autoload(playerContext, playerDir, logger, { cache: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
player.sendMessage('Create your own minecraft mods by adding javascript (.js) files');
|
||||||
|
player.sendMessage(' Windows: Open Explorer, go to \\\\' + serverAddress + '\\players\\' + player.name);
|
||||||
|
player.sendMessage(' Macintosh: Open Finder, Go to smb://' + serverAddress + '/players/' + player.name);
|
||||||
|
player.sendMessage(' Linux: Open Nautilus, Go to smb://' + serverAddress + '/players/' + player.name);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var classroom = plugin('classroom', {
|
var classroom = plugin('classroom', {
|
||||||
allowScripting: function (/* boolean: true or false */ canScript, sender ) {
|
allowScripting: function (/* boolean: true or false */ canScript, sender ) {
|
||||||
if ( typeof sender == 'undefined' ) {
|
sender = utils.player(sender);
|
||||||
|
if ( !sender ) {
|
||||||
console.log( 'Attempt to set classroom scripting without credentials' );
|
console.log( 'Attempt to set classroom scripting without credentials' );
|
||||||
console.log( 'classroom.allowScripting(boolean, sender)' );
|
console.log( 'classroom.allowScripting(boolean, sender)' );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( !sender.op ) {
|
|
||||||
console.log( 'Attempt to set classroom scripting without credentials: ' + sender.name );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
only operators should be allowed run this function
|
only operators should be allowed run this function
|
||||||
*/
|
*/
|
||||||
if ( !sender.isOp() ) {
|
if ( !sender.op ) {
|
||||||
|
console.log( 'Attempt to set classroom scripting without credentials: ' + sender.name );
|
||||||
|
sender.sendMessage('Only operators can use this function');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( canScript ) {
|
foreach( server.onlinePlayers, canScript ? grantScripting : revokeScripting);
|
||||||
foreach( server.onlinePlayers, function( player ) {
|
|
||||||
player.addAttachment( __plugin, 'scriptcraft.*', true );
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
foreach( server.onlinePlayers, function( player ) {
|
|
||||||
foreach( player.getEffectivePermissions(), function( perm ) {
|
|
||||||
if ( (''+perm.permission).indexOf( 'scriptcraft.' ) == 0 ) {
|
|
||||||
if ( perm.attachment ) {
|
|
||||||
perm.attachment.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_store.enableScripting = canScript;
|
_store.enableScripting = canScript;
|
||||||
|
|
||||||
|
sender.sendMessage('Scripting turned ' + ( canScript ? 'on' : 'off' ) +
|
||||||
|
' for all players on server ' + serverAddress);
|
||||||
},
|
},
|
||||||
store: _store
|
store: _store
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
exports.classroom = classroom;
|
exports.classroom = classroom;
|
||||||
|
|
||||||
events.on( 'player.PlayerLoginEvent', function( listener, event ) {
|
events.on( 'player.PlayerJoinEvent', function( listener, event ) {
|
||||||
var player = event.player;
|
|
||||||
if ( _store.enableScripting ) {
|
if ( _store.enableScripting ) {
|
||||||
player.addAttachment( __plugin, 'scriptcraft.*', true );
|
grantScripting(event.player);
|
||||||
}
|
}
|
||||||
}, 'HIGHEST');
|
}, 'HIGHEST');
|
||||||
|
|
||||||
|
|
Reference in a new issue