* More WIP on door support. Proof of concept mostly functional-ish for at least Pimpwars :)
This commit is contained in:
parent
d16beca341
commit
144aa6b351
|
@ -1,18 +0,0 @@
|
||||||
/* jslint node: true */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var MenuModule = require('../core/menu_module.js').MenuModule;
|
|
||||||
var DropFile = require('./door.js').DropFile;
|
|
||||||
|
|
||||||
exports.moduleInfo = {
|
|
||||||
name : 'Abracadabra',
|
|
||||||
desc : 'External BBS Door Module',
|
|
||||||
author : 'NuSkooler',
|
|
||||||
};
|
|
||||||
|
|
||||||
function AbracadabraModule(options) {
|
|
||||||
MenuModule.call(this, options);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
require('util').inherits(AbracadabraModule, MenuModule);
|
|
32
core/bbs.js
32
core/bbs.js
|
@ -112,6 +112,38 @@ function initialize(cb) {
|
||||||
logger.log.info({ themeCount : themeCount }, 'Themes initialized');
|
logger.log.info({ themeCount : themeCount }, 'Themes initialized');
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
function loadSysOpInformation(callback) {
|
||||||
|
//
|
||||||
|
// If user 1 has been created, we have a SysOp. Cache some information
|
||||||
|
// into Config.
|
||||||
|
//
|
||||||
|
var user = require('./user.js'); // must late load
|
||||||
|
|
||||||
|
user.getUserName(1, function unLoaded(err, sysOpUsername) {
|
||||||
|
if(err) {
|
||||||
|
callback(null); // non-fatal here
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// Load some select properties to cache
|
||||||
|
//
|
||||||
|
var propLoadOpts = {
|
||||||
|
userId : 1,
|
||||||
|
names : [ 'real_name', 'sex', 'email_address' ],
|
||||||
|
}
|
||||||
|
user.loadProperties(propLoadOpts, function propsLoaded(err, props) {
|
||||||
|
if(!err) {
|
||||||
|
conf.config.general.sysOp = {
|
||||||
|
username : sysOpUsername,
|
||||||
|
properties : props,
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.log.info( { sysOp : conf.config.general.sysOp }, 'System Operator information cached');
|
||||||
|
}
|
||||||
|
callback(null); // any error is again, non-fatal here
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
function onComplete(err) {
|
function onComplete(err) {
|
||||||
|
|
|
@ -115,6 +115,7 @@ function getDefaultConfig() {
|
||||||
themes : paths.join(__dirname, './../mods/themes/'),
|
themes : paths.join(__dirname, './../mods/themes/'),
|
||||||
logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such
|
logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such
|
||||||
db : paths.join(__dirname, './../db/'),
|
db : paths.join(__dirname, './../db/'),
|
||||||
|
dropFiles : paths.join(__dirname, './../dropfiles/'), // + "/node<x>/
|
||||||
},
|
},
|
||||||
|
|
||||||
servers : {
|
servers : {
|
||||||
|
|
132
core/door.js
132
core/door.js
|
@ -1,89 +1,73 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Config = require('./config.js').config;
|
var spawn = require('child_process').spawn;
|
||||||
|
var events = require('events');
|
||||||
|
|
||||||
var _ = require('lodash');
|
var pty = require('pty');
|
||||||
|
|
||||||
exports.DropFile = DropFile;
|
exports.Door = Door;
|
||||||
|
|
||||||
//
|
function Door(client, exeInfo) {
|
||||||
// Resources
|
events.EventEmitter.call(this);
|
||||||
// * http://goldfndr.home.mindspring.com/dropfile/
|
|
||||||
// * https://en.wikipedia.org/wiki/Talk%3ADropfile
|
|
||||||
// * http://thoughtproject.com/libraries/bbs/Sysop/Doors/DropFiles/index.htm
|
|
||||||
|
|
||||||
// http://lord.lordlegacy.com/dosemu/
|
this.client = client;
|
||||||
|
this.exeInfo = exeInfo;
|
||||||
|
|
||||||
function DropFile(options) {
|
// exeInfo.cmd
|
||||||
|
// exeInfo.args[]
|
||||||
|
// exeInfo.env{}
|
||||||
|
// exeInfo.cwd
|
||||||
|
// exeInfo.encoding
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
require('util').inherits(Door, events.EventEmitter);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Door.prototype.run = function() {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.client = options.client;
|
|
||||||
|
|
||||||
this.fileType = options.fileType || 'DORINFO';
|
var doorProc = spawn(this.exeInfo.cmd, this.exeInfo.args);
|
||||||
this.exe = options.exe || 'dosemu';
|
|
||||||
this.exeParams = options.exeParams || [];
|
|
||||||
|
|
||||||
|
/*
|
||||||
Object.defineProperty(this, 'fileName', {
|
doorProc.stderr.pipe(self.client.term.output);
|
||||||
get : function() {
|
doorProc.stdout.pipe(self.client.term.output);
|
||||||
return {
|
doorProc.stdout.on('data', function stdOutData(data) {
|
||||||
DOOR : 'DOOR.SYS', // GAP BBS, many others
|
console.log('got data')
|
||||||
DOOR32 : 'DOOR32.SYS', // EleBBS / Mystic, Syncronet, Maximus, Telegard, AdeptXBBS, ...
|
self.client.term.write(data);
|
||||||
CALLINFO : 'CALLINFO.BBS', // Citadel?
|
|
||||||
DORINFO : self.getDoorInfoXFileName(), // RBBS, RemoteAccess, QBBS, ...
|
|
||||||
CHAIN : 'CHAIN.TXT', // WWIV
|
|
||||||
CURRUSER : 'CURRUSER.BBS', // RyBBS
|
|
||||||
SFDOORS : 'SFDOORS.DAT', // Spitfire
|
|
||||||
PCBOARD : 'PCBOARD.SYS', // PCBoard
|
|
||||||
TRIBBS : 'TRIBBS.SYS', // TriBBS
|
|
||||||
USERINFO : 'USERINFO.DAT', // Wildcat! 3.0+
|
|
||||||
JUMPER : 'JUMPER.DAT', // 2AM BBS
|
|
||||||
SXDOOR : // System/X, dESiRE
|
|
||||||
'SXDOOR.' + _.pad(self.client.node.toString(), 3, '0'),
|
|
||||||
INFO : 'INFO.BBS', // Phoenix BBS
|
|
||||||
}[self.fileType.toUpperCase()];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getDoorInfoXFileName = function() {
|
doorProc.stderr.on('data', function stdErrData(data) {
|
||||||
var x;
|
console.log('got error data')
|
||||||
var node = self.client.node;
|
self.client.term.write(data);
|
||||||
if(10 === node) {
|
});
|
||||||
x = 0;
|
|
||||||
} else if(node < 10) {
|
doorProc.on('close', function closed(exitCode) {
|
||||||
x = node;
|
console.log('closed')
|
||||||
} else {
|
self.emit('closed', exitCode); // just fwd on
|
||||||
x = String.fromCharCode('a'.charCodeAt(0) + (node - 11));
|
});
|
||||||
}
|
*/
|
||||||
return 'DORINFO' + x + '.DEF';
|
var door = pty.spawn(this.exeInfo.cmd, this.exeInfo.args, {
|
||||||
|
cols : self.client.term.termWidth,
|
||||||
|
rows : self.client.term.termHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
//door.pipe(self.client.term.output);
|
||||||
|
self.client.term.output.pipe(door);
|
||||||
|
|
||||||
|
// :TODO: do this with pluggable pipe/filter classes
|
||||||
|
|
||||||
|
door.setEncoding('cp437');
|
||||||
|
door.on('data', function doorData(data) {
|
||||||
|
self.client.term.write(data);
|
||||||
|
//console.log(data);
|
||||||
|
});
|
||||||
|
//*/
|
||||||
|
|
||||||
|
door.on('close', function closed() {
|
||||||
|
console.log('closed...')
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getDoorInfoXContents = function() {
|
|
||||||
// :TODO: fix sysop first name, last name (load @ system load if avail)
|
|
||||||
// :TODO: fix time remaining
|
|
||||||
|
|
||||||
//
|
|
||||||
// Resources:
|
|
||||||
// * http://goldfndr.home.mindspring.com/dropfile/dorinfo.htm
|
|
||||||
//
|
|
||||||
return [
|
|
||||||
Config.general.boardName, // "The name of the system."
|
|
||||||
'SysOp First', // "The sysop's name up to the first space."
|
|
||||||
'SysOp Last', // "The sysop's name following the first space."
|
|
||||||
'COM1', // "The serial port the modem is connected to, or 0 if logged in on console."
|
|
||||||
'57600', // "The current port (DTE) rate."
|
|
||||||
'0', // "The number "0""
|
|
||||||
/[^\s]*/.exec(self.client.user.username)[0], // "The current user's name, up to the first space."
|
|
||||||
/[^\s]*/.exec(self.client.user.username)[0], // "The current user's name, following the first space."
|
|
||||||
self.client.user.properties.location || '', // "Where the user lives, or a blank line if unknown."
|
|
||||||
'1', // "The number "0" if TTY, or "1" if ANSI."
|
|
||||||
self.client.user.isSysOp() ? '100' : '30', // "The number 5 for problem users, 30 for regular users, 80 for Aides, and 100 for Sysops."
|
|
||||||
'546', // "The number of minutes left in the current user's account, limited to 546 to keep from overflowing other software."
|
|
||||||
'-1' // "The number "-1" if using an external serial driver or "0" if using internal serial routines."
|
|
||||||
].join('\r\n') + '\r\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DropFile.fileTypes = [ 'DORINFO' ];
|
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Config = require('./config.js').config;
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var paths = require('path');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
exports.DropFile = DropFile;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resources
|
||||||
|
// * http://goldfndr.home.mindspring.com/dropfile/
|
||||||
|
// * https://en.wikipedia.org/wiki/Talk%3ADropfile
|
||||||
|
// * http://thoughtproject.com/libraries/bbs/Sysop/Doors/DropFiles/index.htm
|
||||||
|
// * http://thebbs.org/bbsfaq/ch06.02.htm
|
||||||
|
|
||||||
|
// http://lord.lordlegacy.com/dosemu/
|
||||||
|
|
||||||
|
function DropFile(client, fileType) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.client = client;
|
||||||
|
this.fileType = (fileType || 'DORINFO').toUpperCase();
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'fullPath', {
|
||||||
|
get : function() {
|
||||||
|
return paths.join(Config.paths.dropFiles, ('node' + self.client.node), self.fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'fileName', {
|
||||||
|
get : function() {
|
||||||
|
return {
|
||||||
|
DOOR : 'DOOR.SYS', // GAP BBS, many others
|
||||||
|
DOOR32 : 'DOOR32.SYS', // EleBBS / Mystic, Syncronet, Maximus, Telegard, AdeptXBBS, ...
|
||||||
|
CALLINFO : 'CALLINFO.BBS', // Citadel?
|
||||||
|
DORINFO : self.getDoorInfoFileName(), // RBBS, RemoteAccess, QBBS, ...
|
||||||
|
CHAIN : 'CHAIN.TXT', // WWIV
|
||||||
|
CURRUSER : 'CURRUSER.BBS', // RyBBS
|
||||||
|
SFDOORS : 'SFDOORS.DAT', // Spitfire
|
||||||
|
PCBOARD : 'PCBOARD.SYS', // PCBoard
|
||||||
|
TRIBBS : 'TRIBBS.SYS', // TriBBS
|
||||||
|
USERINFO : 'USERINFO.DAT', // Wildcat! 3.0+
|
||||||
|
JUMPER : 'JUMPER.DAT', // 2AM BBS
|
||||||
|
SXDOOR : // System/X, dESiRE
|
||||||
|
'SXDOOR.' + _.pad(self.client.node.toString(), 3, '0'),
|
||||||
|
INFO : 'INFO.BBS', // Phoenix BBS
|
||||||
|
}[self.fileType];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'dropFileContents', {
|
||||||
|
get : function() {
|
||||||
|
return {
|
||||||
|
DORINFO : self.getDoorInfoBuffer(),
|
||||||
|
}[self.fileType];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.getDoorInfoFileName = function() {
|
||||||
|
var x;
|
||||||
|
var node = self.client.node;
|
||||||
|
if(10 === node) {
|
||||||
|
x = 0;
|
||||||
|
} else if(node < 10) {
|
||||||
|
x = node;
|
||||||
|
} else {
|
||||||
|
x = String.fromCharCode('a'.charCodeAt(0) + (node - 11));
|
||||||
|
}
|
||||||
|
return 'DORINFO' + x + '.DEF';
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getDoorInfoBuffer = function() {
|
||||||
|
// :TODO: fix time remaining
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resources:
|
||||||
|
// * http://goldfndr.home.mindspring.com/dropfile/dorinfo.htm
|
||||||
|
//
|
||||||
|
// Note that usernames are just used for first/last names here
|
||||||
|
//
|
||||||
|
var opUn = /[^\s]*/.exec(Config.general.sysOp.username)[0];
|
||||||
|
var un = /[^\s]*/.exec(self.client.user.username)[0];
|
||||||
|
return new Buffer([
|
||||||
|
Config.general.boardName, // "The name of the system."
|
||||||
|
opUn, // "The sysop's name up to the first space."
|
||||||
|
opUn, // "The sysop's name following the first space."
|
||||||
|
'COM1', // "The serial port the modem is connected to, or 0 if logged in on console."
|
||||||
|
'57600', // "The current port (DTE) rate."
|
||||||
|
'0', // "The number "0""
|
||||||
|
un, // "The current user's name, up to the first space."
|
||||||
|
un, // "The current user's name, following the first space."
|
||||||
|
self.client.user.properties.location || '', // "Where the user lives, or a blank line if unknown."
|
||||||
|
'1', // "The number "0" if TTY, or "1" if ANSI."
|
||||||
|
self.client.user.isSysOp() ? '100' : '30', // "The number 5 for problem users, 30 for regular users, 80 for Aides, and 100 for Sysops."
|
||||||
|
'546', // "The number of minutes left in the current user's account, limited to 546 to keep from overflowing other software."
|
||||||
|
'-1' // "The number "-1" if using an external serial driver or "0" if using internal serial routines."
|
||||||
|
].join('\r\n') + '\r\n', 'cp437');
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DropFile.fileTypes = [ 'DORINFO' ];
|
||||||
|
|
||||||
|
DropFile.prototype.createFile = function(cb) {
|
||||||
|
fs.writeFile(this.fullPath, this.dropFileContents, function written(err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ function MenuModule(options) {
|
||||||
this.initSequence = function() {
|
this.initSequence = function() {
|
||||||
var mciData = { };
|
var mciData = { };
|
||||||
|
|
||||||
|
// :TODO: This could be .series() currently
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
function beforeDisplayArt(callback) {
|
function beforeDisplayArt(callback) {
|
||||||
|
|
25
core/user.js
25
core/user.js
|
@ -13,6 +13,8 @@ var moment = require('moment');
|
||||||
|
|
||||||
exports.User = User;
|
exports.User = User;
|
||||||
exports.getUserIdAndName = getUserIdAndName;
|
exports.getUserIdAndName = getUserIdAndName;
|
||||||
|
exports.getUserName = getUserName;
|
||||||
|
exports.loadProperties = loadProperties;
|
||||||
|
|
||||||
function User() {
|
function User() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -67,6 +69,10 @@ User.AccountStatus = {
|
||||||
active : 1,
|
active : 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
User.prototype.load = function(userId, cb) {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
User.prototype.authenticate = function(username, password, cb) {
|
User.prototype.authenticate = function(username, password, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
@ -361,6 +367,25 @@ function getUserIdAndName(username, cb) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUserName(userId, cb) {
|
||||||
|
userDb.get(
|
||||||
|
'SELECT user_name ' +
|
||||||
|
'FROM user ' +
|
||||||
|
'WHERE id=?;', [ userId ],
|
||||||
|
function got(err, row) {
|
||||||
|
if(err) {
|
||||||
|
cb(err);
|
||||||
|
} else {
|
||||||
|
if(row) {
|
||||||
|
cb(null, row.user_name);
|
||||||
|
} else {
|
||||||
|
cb(new Error('No matching user ID'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Internal utility methods
|
// Internal utility methods
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var MenuModule = require('../core/menu_module.js').MenuModule;
|
||||||
|
var DropFile = require('../core/dropfile.js').DropFile;
|
||||||
|
var door = require('../core/door.js');
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
var assert = require('assert');
|
||||||
|
var mkdirp = require('mkdirp');
|
||||||
|
var paths = require('path');
|
||||||
|
|
||||||
|
// :TODO: This should really be a system module... needs a little work to allow for such
|
||||||
|
|
||||||
|
exports.getModule = AbracadabraModule;
|
||||||
|
|
||||||
|
exports.moduleInfo = {
|
||||||
|
name : 'Abracadabra',
|
||||||
|
desc : 'External BBS Door Module',
|
||||||
|
author : 'NuSkooler',
|
||||||
|
};
|
||||||
|
|
||||||
|
function AbracadabraModule(options) {
|
||||||
|
MenuModule.call(this, options);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.config = options.menuConfig.config || {
|
||||||
|
dropFileType : 'DORINFO',
|
||||||
|
};
|
||||||
|
|
||||||
|
this.config.args = this.config.args || [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"config" : {
|
||||||
|
"name" : "LORD",
|
||||||
|
"cmd" : "...",
|
||||||
|
"args" : [ ... ],
|
||||||
|
"dropFileType" : "dorinfo",
|
||||||
|
"maxNodes" : 32, default=unlimited
|
||||||
|
"tooManyArt" : "..." (optional); default = "Too many active" message
|
||||||
|
...
|
||||||
|
"dropFilePath" : "/.../LORD/", || Config.paths.dropFiles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.initSequence = function() {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function validateNodeCount(callback) {
|
||||||
|
// :TODO: Check that node count for this door has not been reached
|
||||||
|
callback(null);
|
||||||
|
},
|
||||||
|
function generateDropfile(callback) {
|
||||||
|
self.dropFile = new DropFile(self.client, self.config.dropFileType);
|
||||||
|
var fullPath = self.dropFile.fullPath;
|
||||||
|
|
||||||
|
mkdirp(paths.dirname(fullPath), function dirCreated(err) {
|
||||||
|
if(err) {
|
||||||
|
callback(err);
|
||||||
|
} else {
|
||||||
|
self.dropFile.createFile(function created(err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
function complete(err) {
|
||||||
|
self.finishedLoading();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.runDOSEmuDoor = function() {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
require('util').inherits(AbracadabraModule, MenuModule);
|
||||||
|
|
||||||
|
AbracadabraModule.prototype.enter = function(client) {
|
||||||
|
AbracadabraModule.super_.prototype.enter.call(this, client);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AbracadabraModule.prototype.leave = function() {
|
||||||
|
Abracadabra.super_.prototype.leave.call(this);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AbracadabraModule.prototype.finishedLoading = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var exeInfo = {
|
||||||
|
cmd : this.config.cmd,
|
||||||
|
args : this.config.args,
|
||||||
|
};
|
||||||
|
|
||||||
|
// :TODO: this system should probably be generic
|
||||||
|
for(var i = 0; i < exeInfo.args.length; ++i) {
|
||||||
|
exeInfo.args[i] = exeInfo.args[i].replace(/\{dropfile\}/g, self.dropFile.fileName);
|
||||||
|
exeInfo.args[i] = exeInfo.args[i].replace(/\{node\}/g, self.client.node.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
var doorInstance = new door.Door(this.client, exeInfo);
|
||||||
|
doorInstance.run();
|
||||||
|
};
|
|
@ -24,7 +24,7 @@ function FullScreenEditorModule(options) {
|
||||||
MenuModule.call(this, options);
|
MenuModule.call(this, options);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.menuConfig = options.menuConfig;
|
this.menuConfig = options.menuConfig; // :TODO: MenuModule does this...
|
||||||
|
|
||||||
//
|
//
|
||||||
// Editor Type ('editorType'):
|
// Editor Type ('editorType'):
|
||||||
|
@ -208,7 +208,7 @@ function FullScreenEditorModule(options) {
|
||||||
assert(_.isString(art.body));
|
assert(_.isString(art.body));
|
||||||
|
|
||||||
async.eachSeries( [ 'header', 'body' ], function dispArt(n, next) {
|
async.eachSeries( [ 'header', 'body' ], function dispArt(n, next) {
|
||||||
theme.displayThemedAsset1(
|
theme.displayThemedAsset(
|
||||||
art[n],
|
art[n],
|
||||||
self.client,
|
self.client,
|
||||||
{ font : self.menuConfig.font },
|
{ font : self.menuConfig.font },
|
||||||
|
|
|
@ -235,6 +235,10 @@
|
||||||
"value" : { "1" : "g" },
|
"value" : { "1" : "g" },
|
||||||
"action" : "@menu:logoff"
|
"action" : "@menu:logoff"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"value" : { "1" : "d" },
|
||||||
|
"action" : "@menu:doorPimpWars"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"value" : 1,
|
"value" : 1,
|
||||||
"action" : "@menu:mainMenu"
|
"action" : "@menu:mainMenu"
|
||||||
|
@ -245,6 +249,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"doorPimpWars" : {
|
||||||
|
"module" : "abracadabra",
|
||||||
|
"config" : {
|
||||||
|
"name" : "PimpWars",
|
||||||
|
"cmd" : "/usr/bin/dosemu",
|
||||||
|
"args" : [ "-quiet", "-f", "/home/nuskooler/DOS/X/LORD/dosemu.conf", "X:\\PW\\START.BAT {dropfile} {node}" ]//, "X:\\PW\\PIMPWARS.EXE", "X:\\DROP\\{dropfile}", "{node}" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
// Mods
|
// Mods
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
"gapbuffer" : "0.0.2",
|
"gapbuffer" : "0.0.2",
|
||||||
"node-uuid" : "1.4.x",
|
"node-uuid" : "1.4.x",
|
||||||
"moment" : "2.10.x",
|
"moment" : "2.10.x",
|
||||||
"gaze" : "0.5.x"
|
"gaze" : "0.5.x",
|
||||||
|
"mkdirp" : "0.5.x"
|
||||||
},
|
},
|
||||||
"engine" : "node >= 0.12.2"
|
"engine" : "node >= 0.12.2"
|
||||||
}
|
}
|
Loading…
Reference in New Issue