diff --git a/README.md b/README.md index a6463c2a..27bce8d9 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ port | Web port to use host | IP or Hostname to listen on type | Which kind of server to use, can be 'linux', 'windows' or 'wine' additionalConfigurationOptions | Additional configuration options appended to server.cfg file +modsPath | Path where game mods are stored. Leave empty to use game directory parameters | Extra startup parameters added to servers and headless clients serverMods | Mods that always and only will be used by the game servers auth | If both username and password is set, HTTP Basic Auth will be used diff --git a/app.js b/app.js index e8ddd73a..03eee3da 100644 --- a/app.js +++ b/app.js @@ -31,13 +31,13 @@ app.use(serveStatic(path.join(__dirname, 'public'))) var logs = new Logs(config) -var manager = new Manager(config, logs) -manager.load() - var missions = new Missions(config) var mods = new Mods(config) mods.updateMods() +var manager = new Manager(config, logs, mods) +manager.load() + var settings = new Settings(config) app.use('/api/logs', require('./routes/logs')(logs)) diff --git a/config.js.example b/config.js.example index 2731db86..e2d0c80e 100644 --- a/config.js.example +++ b/config.js.example @@ -1,6 +1,7 @@ module.exports = { game: 'arma3', // arma3, arma2oa, arma2, arma1, cwa, ofpresistance, ofp path: 'path-to-arma3-directory', + modsPath: '', // optional: path to mod directories, relative to game directory port: 3000, host: '0.0.0.0', // Can be either an IP or a Hostname type: 'linux', // Can be either linux, windows or wine diff --git a/lib/manager.js b/lib/manager.js index b7edca7f..132727b8 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -5,9 +5,10 @@ var Server = require('./server') var filePath = 'servers.json' -var Manager = function (config, logs) { +var Manager = function (config, logs, mods) { this.config = config this.logs = logs + this.mods = mods this.serversArr = [] this.serversHash = {} } @@ -41,7 +42,7 @@ Manager.prototype.removeServer = function (id) { } Manager.prototype._addServer = function (data) { - var server = new Server(this.config, this.logs, data) + var server = new Server(this.config, this.logs, this.mods, data) this.serversArr.push(server) this.serversArr.sort(function (a, b) { return a.title.localeCompare(b.title) diff --git a/lib/mods.js b/lib/mods.js index 012bcf18..6c24face 100644 --- a/lib/mods.js +++ b/lib/mods.js @@ -8,11 +8,11 @@ var Mods = function (config) { this.mods = [] } -Mods.prototype = new events.EventEmitter() +Mods.prototype = Object.create(events.EventEmitter.prototype) Mods.prototype.delete = function (mod, cb) { var self = this - fs.rmrf(path.join(this.config.path, mod), function (err) { + fs.rmrf(self.getAbsoluteModPath(mod), function (err) { cb(err) if (!err) { @@ -23,7 +23,7 @@ Mods.prototype.delete = function (mod, cb) { Mods.prototype.updateMods = function () { var self = this - glob('**/{@*,gm,vn}/addons', { cwd: self.config.path }, function (err, files) { + glob('**/{@*,gm,vn}/addons', { cwd: self.getAbsoluteModsPath() }, function (err, files) { if (err) { console.log(err) } else { @@ -40,4 +40,25 @@ Mods.prototype.updateMods = function () { }) } +Mods.prototype.getAbsoluteModsPath = function () { + if (this.config.modsPath) { + if (path.isAbsolute(this.config.modsPath)) { + return this.config.modsPath + } + return path.join(this.config.path, this.config.modsPath) + } + return this.config.path +} + +Mods.prototype.getApplicableModPath = function (mod) { + if (!this.config.modsPath) { + return mod + } + return path.join(this.config.modsPath, mod) +} + +Mods.prototype.getAbsoluteModPath = function (mod) { + return path.join(this.getAbsoluteModsPath(), mod) +} + module.exports = Mods diff --git a/lib/server.js b/lib/server.js index 65f39166..f9387790 100644 --- a/lib/server.js +++ b/lib/server.js @@ -32,9 +32,10 @@ var createServerTitle = function (title) { return title } -var Server = function (config, logs, options) { +var Server = function (config, logs, mods, options) { this.config = config this.logs = logs + this.modsManager = mods this.update(options) } @@ -134,6 +135,7 @@ Server.prototype.start = function () { return this } + var self = this var parameters = this.getParameters() var server = new ArmaServer.Server({ additionalConfigurationOptions: this.getAdditionalConfigurationOptions(), @@ -149,7 +151,9 @@ Server.prototype.start = function () { hostname: createServerTitle(this.title), localClient: this.number_of_headless_clients > 0 ? ['127.0.0.1'] : null, missions: this.missions, - mods: this.mods, + mods: this.mods.map(function (mod) { + return self.modsManager.getApplicableModPath(mod) + }), motd: (this.motd && this.motd.split('\n')) || null, parameters: parameters, password: this.password, @@ -164,7 +168,6 @@ Server.prototype.start = function () { }) server.writeServerConfig() var instance = server.start() - var self = this var logStream = null if (this.config.type === 'linux') { @@ -225,7 +228,9 @@ Server.prototype.startHeadlessClients = function () { filePatching: self.file_patching, game: config.game, host: '127.0.0.1', - mods: self.mods, + mods: self.mods.map(function (mod) { + return self.modsManager.getApplicableModPath(mod) + }), parameters: parameters, password: self.password, path: self.config.path, diff --git a/test/lib/mods.js b/test/lib/mods.js new file mode 100644 index 00000000..e6bc9b1f --- /dev/null +++ b/test/lib/mods.js @@ -0,0 +1,82 @@ +var path = require('path') +var os = require('os') +require('should') + +function isWindows () { + return os.type() === 'Windows_NT' +} + +var Mods = require('../../lib/mods.js') + +describe('Mods', function () { + var rootPrefix = '/' + if (isWindows()) { + rootPrefix = 'C:' + } + var absoluteArmaPath = path.join(rootPrefix, 'foo', 'arma') + var absoluteModsPath = path.join(rootPrefix, 'foo', 'mods') + var relativeModsPath = path.join('..', 'mods') + + describe('getAbsoluteModsPath() / getAbsoluteModPath(mod)', function () { + describe('when no modsPath is given', function () { + it('returns path', function () { + var mods = new Mods({ path: absoluteArmaPath }) + mods.getAbsoluteModsPath().should.eql(absoluteArmaPath) + mods.getAbsoluteModPath('x').should.eql(path.join(absoluteArmaPath, 'x')) + }) + }) + describe('when modsPath is', function () { + it('relative, it returns absolute path', function () { + var mods = new Mods({ path: absoluteArmaPath, modsPath: relativeModsPath }) + mods.getAbsoluteModsPath().should.eql(absoluteModsPath) + mods.getAbsoluteModPath('x').should.eql(path.join(absoluteModsPath, 'x')) + }) + it('absolute, it returns absolute path', function () { + var mods = new Mods({ path: absoluteArmaPath, modsPath: absoluteModsPath }) + mods.getAbsoluteModsPath().should.eql(absoluteModsPath) + mods.getAbsoluteModPath('x').should.eql(path.join(absoluteModsPath, 'x')) + }) + }) + }) + describe('getApplicableModPath()', function () { + it('returns mod name if no modsPath is given', function () { + var mods = new Mods({ path: absoluteArmaPath }) + mods.getApplicableModPath('x').should.eql('x') + }) + it('returns relative path if relative modsPath is given', function () { + var mods = new Mods({ path: absoluteArmaPath, modsPath: relativeModsPath }) + mods.getApplicableModPath('x').should.eql(path.join(relativeModsPath, 'x')) + }) + it('returns absolute path if modsPath is absolute', function () { + var mods = new Mods({ path: absoluteArmaPath, modsPath: absoluteModsPath }) + mods.getApplicableModPath('x').should.eql(path.join(absoluteModsPath, 'x')) + }) + }) + + describe('updateMods()', function () { + it('finds mods in arma dir', function (done) { + var mods = new Mods({ path: path.join(__dirname, '..', 'resources', 'armaPath') }) + mods.on('mods', function (modList) { + modList.should.eql([{ name: '@armaMod' }]) + done() + }) + mods.updateMods() + }) + it('finds mods in modsPath dir', function (done) { + var mods = new Mods({ path: path.join(__dirname, '..', 'resources', 'armaPath'), modsPath: path.join(__dirname, '..', 'resources', 'modPath') }) + mods.on('mods', function (modList) { + modList.should.eql([{ name: '@modPathMod' }]) + done() + }) + mods.updateMods() + }) + it('finds mods in relative modsPath dir', function (done) { + var mods = new Mods({ path: path.join(__dirname, '..', 'resources', 'armaPath'), modsPath: path.join('..', 'modPath') }) + mods.on('mods', function (modList) { + modList.should.eql([{ name: '@modPathMod' }]) + done() + }) + mods.updateMods() + }) + }) +}) diff --git a/test/lib/server.js b/test/lib/server.js index 3dfa552b..9ab3b7b6 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -5,14 +5,14 @@ var Server = require('../../lib/server.js') describe('Server', function () { describe('generateId()', function () { it('should include title', function () { - var server = new Server(null, null, { title: 'title.with.lot.of.dots' }) + var server = new Server(null, null, null, { title: 'title.with.lot.of.dots' }) server.generateId().should.eql('title-with-lot-of-dots') }) }) describe('toJSON()', function () { it('should include title', function () { - var server = new Server(null, null, { title: 'test' }) + var server = new Server(null, null, null, { title: 'test' }) server.toJSON().should.have.property('title', 'test') }) }) diff --git a/test/resources/armaPath/@armaMod/addons/.keep b/test/resources/armaPath/@armaMod/addons/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/resources/modPath/@modPathMod/addons/.keep b/test/resources/modPath/@modPathMod/addons/.keep new file mode 100644 index 00000000..e69de29b