diff --git a/Dockerfile b/Dockerfile index 4f68ff19..949a4018 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,19 @@ FROM node:latest -RUN apt-get update && apt install git -y && git clone https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install && curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 && tar -xvzf factorio.tar.gz +RUN apt-get update && apt install git -y + +RUN mkdir factorioClusterio +RUN git clone -b dev https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install && curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 && tar -xvzf factorio.tar.gz + +WORKDIR factorioClusterio +RUN mkdir instances sharedMods sharedPlugins + +RUN node client.js download LABEL maintainer "danielv@live.no" EXPOSE 8080 34167 - VOLUME /factorioClusterio/instances +VOLUME /factorioClusterio/sharedMods +VOLUME /factorioClusterio/sharedPlugins -WORKDIR factorioClusterio -CMD node $MODE\.js start $INSTANCE \ No newline at end of file +CMD RCONPORT="$RCONPORT" FACTORIOPORT="$FACTORIOPORT" MODE="$MODE" node $MODE\.js start $INSTANCE \ No newline at end of file diff --git a/client.js b/client.js index eca83790..1ebe80fd 100644 --- a/client.js +++ b/client.js @@ -4,10 +4,11 @@ var needle = require("needle"); var child_process = require('child_process'); var path = require('path'); var syncRequest = require('sync-request'); -var request = require("request") +var request = require("request"); var ncp = require('ncp').ncp; var Rcon = require('simple-rcon'); var hashFiles = require('hash-files'); +var _ = require('underscore'); var libomega = require("./libomega.js"); // require config.json @@ -62,7 +63,16 @@ function messageInterface(command, callback) { command = command.toString('utf8'); } - if(typeof command == "string" && client && client.exec && typeof client.exec == "function") { + if(process.platform == "linux" && typeof command == "string" && serverprocess) { + /* + to send to stdin, use: + serverprocess.stdin.write("/c command;\n") + */ + serverprocess.stdin.write(command+"\n"); + if(typeof callback == "function"){ + callback(); + } + } else if(typeof command == "string" && client && client.exec && typeof client.exec == "function") { try { client.exec(command, callback); } catch (err) { @@ -130,8 +140,8 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ fs.copySync('sharedMods', instancedirectory + "/mods") let instconf = { - "factorioPort": Math.floor(Math.random() * 65535), - "clientPort": Math.floor(Math.random() * 65535), + "factorioPort": process.env.FACTORIOPORT | Math.floor(Math.random() * 65535), + "clientPort": process.env.RCONPORT | Math.floor(Math.random() * 65535), "clientPassword": Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 8), } console.log("Clusterio | Moving shared mods...") @@ -175,6 +185,12 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ if(instancedirectory != "./instances/undefined"){ var instanceconfig = require(instancedirectory + '/config'); instanceconfig.unique = instanceconfig.clientPassword.hashCode(); + if(process.env.FACTORIOPORT){ + instanceconfig.factorioPort = process.env.FACTORIOPORT; + } + if(process.env.RCONPORT){ + instanceconfig.rconPort = process.env.RCONPORT; + } } else { process.exit(1) } @@ -185,7 +201,9 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ fs.unlinkSync(instancedirectory + "/saves/" + savefiles[i]); } } - + /*setInterval(function(){ + console.log(process.env); + },5000);*/ console.log("Deleting logs"); // clean old log file to avoid crash // file exists, delete so we don't get in trouble @@ -211,14 +229,15 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ // Spawn factorio server //var serverprocess = child_process.exec(commandline) getNewestFile(instancedirectory + "/saves/", fs.readdirSync(instancedirectory + "/saves/"),function(latestSave) { - var serverprocess = child_process.spawn( + // implicit global + serverprocess = child_process.spawn( './' + config.factorioDirectory + '/bin/x64/factorio', [ '-c', instancedirectory + '/config.ini', '--start-server', latestSave.file, - '--rcon-port', instanceconfig.clientPort, + '--rcon-port', Number(process.env.RCONPORT) || instanceconfig.clientPort, '--rcon-password', instanceconfig.clientPassword, '--server-settings', instancedirectory + '/server-settings.json', - '--port', instanceconfig.factorioPort + '--port', Number(process.env.FACTORIOPORT) || instanceconfig.factorioPort ], { 'stdio': ['pipe', 'pipe', 'pipe'] } @@ -228,6 +247,15 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ console.log(`child process exited with code ${code}`); process.exit(); }); + serverprocess.stdout.on("data", (data) => { + // log("Stdout: " + data); + if(data.toString('utf8').includes("Couldn't parse RCON data: Maximum payload size exceeded")){ + console.error("ERROR: RCON CONNECTION BROKE DUE TO TOO LARGE PACKET!"); + console.error("Attempting reconnect..."); + client.close(); + client.connect(); + } + }); serverprocess.stdout.on('data', (chunk) => { if(process.platform == "linux"){ console.log('Factorio: ' + chunk); @@ -242,7 +270,7 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ // IP, port, password client = new Rcon({ host: 'localhost', - port: instanceconfig.clientPort, + port: Number(process.env.RCONPORT) || instanceconfig.clientPort, password: instanceconfig.clientPassword, timeout: 0 }); @@ -271,8 +299,7 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ // getID(); }).on('disconnected', function () { console.log('Clusterio | Disconnected!'); - // now reconnect - client.connect(); + process.exit(0); // exit because RCON disconnecting is undefined behaviour and we rather just wanna restart now }); // set some globals @@ -280,8 +307,9 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ lastSignalCheck = Date.now(); }) } - +_.once(instanceManagement); function instanceManagement() { + console.log("Started instanceManagement();") // load plugins and execute onLoad event let pluginDirectories = getDirectories("./sharedPlugins/"); let plugins = []; @@ -384,9 +412,12 @@ function instanceManagement() { require('getmac').getMac(function (err, mac) { if (err) throw err payload.mac = mac - // console.log(payload) + console.log("Registered our precense with master "+config.masterIP+" at " + payload.time); needle.post(config.masterIP + ":" + config.masterPort + '/api/getID', payload, function (err, response, body) { - if (response && response.body) { + if (err && err.code != "ECONNRESET"){ + console.error("We got problems, something went wrong when contacting master"); + console.error(err); + } else if (response && response.body) { // In the future we might be interested in whether or not we actually manage to send it, but honestly I don't care. console.log(response.body) } diff --git a/package.json b/package.json index 49ab47db..46f5a301 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "portfinder": "^1.0.9", "request": "^2.79.0", "simple-rcon": "^0.3.1", - "sync-request": "^4.0.1" + "sync-request": "^4.0.1", + "underscore": "^1.8.3" } } diff --git a/static/main.js b/static/main.js index 5f516598..70bd783f 100644 --- a/static/main.js +++ b/static/main.js @@ -2,6 +2,7 @@ (function (){ ensureSetting("Display offline slaves", "boolean", false); ensureSetting("Enable production graphs", "boolean", true); + ensureSetting("Periodically update storage screen", "boolean", true); }()) function ensureSetting(setting, type, defaultSetting) { diff --git a/static/slaveDisplay.js b/static/slaveDisplay.js index b473c8e9..0045fad7 100644 --- a/static/slaveDisplay.js +++ b/static/slaveDisplay.js @@ -19,7 +19,7 @@ setTimeout(function() { } HTML += "
"; HTML += '
'; - HTML += "

" + slaveData[key].instanceName + "

ID: " + slaveData[key].unique + "

Last seen: "+seenDate+"

IP: "+slaveData[key].publicIP +":"+ slaveData[key].serverPort+"

" + HTML += "

" + slaveData[key].instanceName + "

ID: " + slaveData[key].unique + "

Last seen: "+seenDate+"

Online players: "+slaveData[key].playerCount+"

IP: "+slaveData[key].publicIP +":"+ slaveData[key].serverPort+"

" HTML += "

Host: "+slaveData[key].mac+"

"; HTML += ""; HTML += ""; diff --git a/static/storage.js b/static/storage.js index 899125fc..83e5930f 100644 --- a/static/storage.js +++ b/static/storage.js @@ -1,7 +1,7 @@ // function to draw data we recieve from ajax requests function drawcontents(data) { - var data = sortByKey(data, "count"); - result = ""; + data = sortByKey(data, "count"); + let result = "
"; for(i = 0;i < data.length; i++) { var img = ""; if(imagedata[data[i].name]) { @@ -18,17 +18,22 @@ function drawcontents(data) { } // get cluster inventory from master -setInterval(function() { +function updateInventory() { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if(xmlhttp.readyState == 4 && xmlhttp.status == 200) { - data = JSON.parse(xmlhttp.responseText); + let data = JSON.parse(xmlhttp.responseText); drawcontents(data); } } xmlhttp.open("GET", "api/inventory", true); xmlhttp.send(); -}, 500) +} +if(JSON.parse(localStorage.settings)["Periodically update storage screen"]) { + setInterval(updateInventory, 500); +} else { + updateInventory(); +} // function to sort arrays of objects after a keys value function sortByKey(array, key) {