Skip to content

Commit 4de0eda

Browse files
committed
Arma Reforger support
1 parent 2415302 commit 4de0eda

File tree

21 files changed

+707
-13
lines changed

21 files changed

+707
-13
lines changed

Diff for: app.js

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var express = require('express')
22
var bodyParser = require('body-parser')
3+
var fs = require('fs.extra')
34
var morgan = require('morgan')
45
var path = require('path')
56
var serveStatic = require('serve-static')
@@ -15,6 +16,15 @@ var Mods = require('./lib/mods')
1516
var Logs = require('./lib/logs')
1617
var Settings = require('./lib/settings')
1718

19+
if (config.game === 'reforger') {
20+
Logs = require('./lib/reforger/logs')
21+
Manager = require('./lib/reforger/manager')
22+
Missions = require('./lib/reforger/missions')
23+
Mods = require('./lib/reforger/mods')
24+
25+
fs.mkdirp(config.reforger.profiles) // Needs to be created in advance or game will store profiles elsewhere
26+
}
27+
1828
var app = express()
1929
var server = require('http').Server(app)
2030
var io = require('socket.io')(server)

Diff for: config.js.example

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ module.exports = {
99
'-noSound',
1010
'-world=empty'
1111
],
12+
reforger: {
13+
configs: 'path-to-configs-folder', // Folder where server config files will be stored
14+
profiles: 'path-to-profiles-folder', // Folder where logs and persistence data will be stored. Each server will create a subfolder.
15+
region: 'EU', // Region in which servers will be hosted, https://community.bistudio.com/wiki/Arma_Reforger:Server_Hosting#region
16+
workshop: 'path-to-workshop-folder' // Folder where workshop mods will be stored
17+
},
1218
serverMods: [ // Mods used exclusively by server and not shared with clients
1319
'@mod1',
1420
'@mod2',

Diff for: lib/mods/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Mods.prototype.resolveModData = function (modPath, cb) {
7070
}
7171

7272
cb(null, {
73+
id: modPath,
7374
name: modPath,
7475
size: results.folderSize,
7576
formattedSize: filesize(results.folderSize),

Diff for: lib/reforger/logs.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
var async = require('async')
2+
var filesize = require('filesize')
3+
var fs = require('fs.extra')
4+
var glob = require('glob')
5+
var path = require('path')
6+
7+
var Logs = function (config) {
8+
this.config = config
9+
}
10+
11+
Logs.prototype.delete = function (filename, callback) {
12+
callback = callback || function () {}
13+
14+
this.getLogFile(filename, function (err, logFile) {
15+
if (err) {
16+
return callback(err)
17+
} else {
18+
if (logFile && logFile.path) {
19+
fs.unlink(logFile.path, callback)
20+
} else {
21+
return callback(new Error('File not found'))
22+
}
23+
}
24+
})
25+
}
26+
27+
Logs.prototype.logsPath = function () {
28+
return this.config.reforger.profiles
29+
}
30+
31+
Logs.prototype.logFiles = function (callback) {
32+
var directory = this.logsPath()
33+
34+
if (directory === null) {
35+
return callback(null, [])
36+
}
37+
38+
glob('**/*.log', { cwd: directory }, function (err, files) {
39+
if (err) {
40+
callback(err)
41+
return
42+
}
43+
44+
files = files.map(function (file) {
45+
return {
46+
name: file,
47+
path: path.join(directory, file)
48+
}
49+
})
50+
51+
async.filter(files, function (file, cb) {
52+
fs.stat(file.path, function (err, stat) {
53+
file.created = stat.birthtime.toISOString()
54+
file.modified = stat.mtime.toISOString()
55+
file.formattedSize = filesize(stat.size)
56+
file.size = stat.size
57+
cb(!err && stat.isFile())
58+
})
59+
}, function (files) {
60+
files.sort(function (a, b) {
61+
return b.created.localeCompare(a.created) // Descending order
62+
})
63+
64+
callback(null, files)
65+
})
66+
})
67+
}
68+
69+
Logs.prototype.getLogFile = function (filename, callback) {
70+
this.logFiles(function (err, files) {
71+
if (err) {
72+
callback(err)
73+
} else {
74+
var validLogs = files.filter(function (file) {
75+
return file.name === filename
76+
})
77+
78+
if (validLogs.length > 0) {
79+
callback(null, validLogs[0])
80+
} else {
81+
callback(null, null)
82+
}
83+
}
84+
})
85+
}
86+
87+
Logs.prototype.readLogFile = function (filename, callback) {
88+
fs.readFile(filename, callback)
89+
}
90+
91+
module.exports = Logs

Diff for: lib/reforger/manager.js

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
var events = require('events')
2+
var fs = require('fs')
3+
4+
var Server = require('./server')
5+
6+
var filePath = 'servers.json'
7+
8+
var Manager = function (config, logs) {
9+
this.config = config
10+
this.logs = logs
11+
this.serversArr = []
12+
this.serversHash = {}
13+
}
14+
15+
Manager.prototype = new events.EventEmitter()
16+
17+
Manager.prototype.addServer = function (options) {
18+
var server = this._addServer(options)
19+
this.save()
20+
return server
21+
}
22+
23+
Manager.prototype.removeServer = function (id) {
24+
var server = this.serversHash[id]
25+
26+
if (!server) {
27+
return {}
28+
}
29+
30+
var index = this.serversArr.indexOf(server)
31+
if (index > -1) {
32+
this.serversArr.splice(index, 1)
33+
}
34+
this.save()
35+
36+
if (server.pid) {
37+
server.stop()
38+
}
39+
40+
return server
41+
}
42+
43+
Manager.prototype._addServer = function (data) {
44+
var server = new Server(this.config, this.logs, data)
45+
this.serversArr.push(server)
46+
this.serversArr.sort(function (a, b) {
47+
return a.title.localeCompare(b.title)
48+
})
49+
this.serversHash[server.id] = server
50+
51+
var self = this
52+
var save = function () {
53+
self.save()
54+
}
55+
var statusChanged = function () {
56+
self.emit('servers')
57+
}
58+
server.on('save', save)
59+
server.on('state', statusChanged)
60+
61+
return server
62+
}
63+
64+
Manager.prototype.getServer = function (id) {
65+
return this.serversHash[id]
66+
}
67+
68+
Manager.prototype.getServers = function () {
69+
return this.serversArr
70+
}
71+
72+
Manager.prototype.load = function () {
73+
var self = this
74+
75+
fs.readFile(filePath, function (err, data) {
76+
if (err) {
77+
console.log('Could not load any existing servers configuration, starting fresh')
78+
return
79+
}
80+
81+
try {
82+
JSON.parse(data).forEach(function (server) {
83+
self._addServer(server)
84+
})
85+
} catch (e) {
86+
console.error('Manager load error: ' + e)
87+
}
88+
89+
self.getServers().map(function (server) {
90+
if (server.auto_start) {
91+
server.start()
92+
}
93+
})
94+
})
95+
}
96+
97+
Manager.prototype.save = function () {
98+
var data = []
99+
var self = this
100+
101+
this.serversArr.sort(function (a, b) {
102+
return a.title.toLowerCase().localeCompare(b.title.toLowerCase())
103+
})
104+
105+
this.serversHash = {}
106+
this.serversArr.forEach(function (server) {
107+
data.push({
108+
admin_password: server.admin_password,
109+
auto_start: server.auto_start,
110+
battle_eye: server.battle_eye,
111+
dedicatedServerId: server.dedicatedServerId,
112+
max_players: server.max_players,
113+
missions: server.missions,
114+
mods: server.mods,
115+
password: server.password,
116+
port: server.port,
117+
title: server.title
118+
})
119+
120+
self.serversHash[server.id] = server
121+
})
122+
123+
fs.writeFile(filePath, JSON.stringify(data), function (err) {
124+
if (err) {
125+
console.error('Manager save error: ' + err)
126+
} else {
127+
self.emit('servers')
128+
}
129+
})
130+
}
131+
132+
module.exports = Manager

Diff for: lib/reforger/missions.js

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
var async = require('async')
2+
var events = require('events')
3+
var fs = require('fs.extra')
4+
var glob = require('glob')
5+
var path = require('path')
6+
7+
function stripBOM (data) {
8+
if (data.charCodeAt(0) === 0xFEFF) {
9+
return data.slice(1)
10+
}
11+
12+
return data
13+
}
14+
15+
var Missions = function (config) {
16+
this.config = config
17+
this.missions = []
18+
19+
this.updateMissions()
20+
}
21+
22+
Missions.prototype = new events.EventEmitter()
23+
24+
Missions.prototype.missionsPath = function () {
25+
return this.config.reforger.workshop
26+
}
27+
28+
Missions.prototype.updateMissions = function (cb) {
29+
var self = this
30+
var workshopFolder = this.missionsPath()
31+
32+
glob('**/ServerData.json', { cwd: workshopFolder }, function (err, files) {
33+
if (err) {
34+
console.log(err)
35+
return
36+
}
37+
38+
async.map(files, function (filename, cb) {
39+
var serverDataFile = path.join(workshopFolder, filename)
40+
fs.readFile(serverDataFile, 'utf-8', function (err, data) {
41+
if (err) {
42+
console.log(err)
43+
return cb(err)
44+
}
45+
46+
try {
47+
var serverData = JSON.parse(stripBOM(data))
48+
fs.stat(serverDataFile, function (err, stat) {
49+
if (err) {
50+
console.log(err)
51+
return cb(err)
52+
}
53+
54+
var missions = serverData.revision.scenarios.map(function (scenario) {
55+
return {
56+
dateCreated: new Date(stat.ctime),
57+
dateModified: new Date(stat.mtime),
58+
missionName: scenario.name,
59+
name: scenario.gameId,
60+
size: 0,
61+
sizeFormatted: '',
62+
worldName: ''
63+
}
64+
})
65+
66+
cb(null, missions)
67+
})
68+
} catch (err) {
69+
console.log(err)
70+
return cb(err)
71+
}
72+
})
73+
}, function (err, missions) {
74+
if (!err) {
75+
missions = missions.flat()
76+
self.missions = missions
77+
self.emit('missions', missions)
78+
}
79+
80+
if (cb) {
81+
cb(err, missions)
82+
}
83+
})
84+
})
85+
}
86+
87+
Missions.prototype.handleUpload = function (uploadedFile, cb) {
88+
cb(new Error('Not implemented'))
89+
}
90+
91+
Missions.prototype.delete = function (missionName, cb) {
92+
cb(new Error('Not implemented'))
93+
}
94+
95+
Missions.prototype.downloadSteamWorkshop = function (id, cb) {
96+
cb(new Error('Not implemented'))
97+
}
98+
99+
module.exports = Missions

0 commit comments

Comments
 (0)