var fs = require('fs-extra'); var https = require('follow-redirects').https; var needle = require("needle"); var child_process = require('child_process'); var path = require('path'); var syncRequest = require('sync-request'); var request = require("request") var ncp = require('ncp').ncp; var Rcon = require('simple-rcon'); var hashFiles = require('hash-files'); // require config.json var config = require('./config'); // Functions function deleteFolderRecursive(path) { if (fs.existsSync(path)) { fs.readdirSync(path).forEach(function (file, index) { var curPath = path + "/" + file; if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); } else { // delete file fs.unlinkSync(curPath); } }); fs.rmdirSync(path); } }; // function to handle sending commands into the game function messageInterface(command, callback) { // try to save us if you send a buffer instead of string if(typeof command == "object") { command = command.toString('utf8'); } if(typeof command == "string" && client && client.exec && typeof client.exec == "function") { try { client.exec(command, callback); } catch (err) { console.log(err); callback(err); } } } // load plugins and execute onLoad event let pluginDirectories = getDirectories("./sharedPlugins/"); let plugins = []; for(i=0; i { log("Stdout: " + data); messageInterface(data.toString('utf8')); }); plugins[i].stderr.on("data", (data) => { log("STDERR: " + data); }) plugins[i].on('close', (code) => { console.log(`child process exited with code ${code}`); }); } function triggerPluginEvent(event, plugins, data) { // string, array, object // data = {messageInterface} if(!data || typeof data != "object") { data = {}; console.log("WARN: client.js triggerPluginEvent argument 3 has to be object, defaulting to " + JSON.stringify(data)); } if(typeof event == "string" && typeof plugins == "object") { for(i=0;i { console.log(`child process exited with code ${code}`); process.exit(); }); serverprocess.stdout.on('data', (chunk) => { console.log('OUT: ' + chunk); }) serverprocess.stderr.on('data', (chunk) => { console.log('ERR: ' + chunk); }) // connect to the server with rcon // IP, port, password var client = new Rcon({ host: 'localhost', port: instanceconfig.clientPort, password: instanceconfig.clientPassword, timeout: 0 }); // clean old log file to avoid crash // file exists, delete so we don't get in trouble try { fs.unlinkSync(instancedirectory+'/factorio-current.log') } catch (err){ if(err){ console.log(err); } else { console.log("Clusterio | Deleting old logs...") } } // check the logfile to see if the RCON interface is running as there is no way to continue without it // we read the log every 2 seconds and stop looping when we start connecting to factorio function checkRcon() { fs.readFile(instancedirectory+"/factorio-current.log", function (err, data) { // if (err) console.log(err); if(data && data.indexOf('Starting RCON interface') > 0){ client.connect(); } else { setTimeout(function(){ checkRcon(); },2000); } }); } checkRcon(); client.on('authenticated', function () { console.log('Clusterio | Authenticated!'); // tell all plugins we have RCON and they can start using it triggerPluginEvent("onRcon", plugins, client.exec); instanceManagement(); // start using rcons }).on('connected', function () { console.log('Clusterio | Connected!'); // getID(); }).on('disconnected', function () { console.log('Clusterio | Disconnected!'); // now reconnect client.connect(); }); // set some globals confirmedOrders = []; lastSignalCheck = Date.now(); } function instanceManagement() { // world IDs ------------------------------------------------------------------ hashMods(instance, function(modHashes){ setInterval(getID, 10000); getID() function getID() { var payload = { time: Date.now(), rconPort: instanceconfig.clientPort, rconPassword: instanceconfig.clientPassword, serverPort: instanceconfig.factorioPort, unique: instanceconfig.clientPassword.hashCode(), publicIP: config.publicIP, // IP of the server should be global for all instances, so we pull that straight from the config mods:modHashes } require('getmac').getMac(function (err, mac) { if (err) throw err payload.mac = mac console.log(payload) needle.post(config.masterIP + ":" + config.masterPort + '/getID', payload, function (err, response, body) { 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) } }); }) } }) // Mod uploading and management ----------------------------------------------- // get mod names and hashes // string: instance, function: callback setTimeout(function(){hashMods(instance, uploadMods)}, 5000); function uploadMods(modHashes) { // [{modName:string,hash:string}, ... ] for(i=0;i