diff --git a/client.js b/client.js index 3e9ef1b8..fc68e4ae 100644 --- a/client.js +++ b/client.js @@ -546,7 +546,7 @@ write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n // ensure instancemanagement only ever runs once _.once(instanceManagement); -function instanceManagement() { +async function instanceManagement() { console.log("Started instanceManagement();"); /* Open websocket connection to master */ @@ -560,27 +560,27 @@ function instanceManagement() { setInterval(B=> socket.emit("heartbeat"), 10000); // load plugins and execute onLoad event - let pluginDirectories = fileOps.getDirectoriesSync("./sharedPlugins/"); + const pluginManager = require("./lib/manager/pluginManager.js")(config); + let pluginsToLoad = await pluginManager.getPlugins(); let plugins = []; - for(let i=0; i plugins[I].factorioOutput(data.toString())); + serverprocess.stdout.on("data", data => plugins[i].factorioOutput(data.toString())); } if(pluginConfig.scriptOutputFileSubscription && typeof pluginConfig.scriptOutputFileSubscription == "string"){ if(global.subscribedFiles[pluginConfig.scriptOutputFileSubscription]) { // please choose a unique file to subscribe to. If you need plugins to share this interface, set up a direct communication // between those plugins instead. - throw "FATAL ERROR IN " + pluginDirectories[i] + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; + throw "FATAL ERROR IN " + pluginConfig.name + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; } if (!fs.existsSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription)) { @@ -605,14 +605,13 @@ function instanceManagement() { } global.subscribedFiles[pluginConfig.scriptOutputFileSubscription] = true; console.log("Clusterio | Registered file subscription on "+instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription); + fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, fileChangeHandler); // run once in case a plugin wrote out information before the plugin loaded fully - // delay, so the socket got enough time to connect setTimeout(()=> { fileChangeHandler(false, pluginConfig.scriptOutputFileSubscription); }, 500); - // send file contents to plugin for processing function fileChangeHandler(eventType, filename) { if(filename != null){ @@ -625,10 +624,10 @@ function instanceManagement() { if (stuff[0]) { fs.writeFileSync(instancedirectory + "/script-output/" + filename, ""); } - for(let i = 0; i < stuff.length; i++) { - if(stuff[i] && !stuff[i].includes('\u0000\u0000')) { + for(let o = 0; o < stuff.length; o++) { + if(stuff[o] && !stuff[o].includes('\u0000\u0000')) { try{ - plugins[I].scriptOutput(stuff[i]); + plugins[i].scriptOutput(stuff[o]); }catch(e){console.error(e)} } } @@ -638,72 +637,11 @@ function instanceManagement() { } } } - console.log("Clusterio | Loaded plugin " + pluginDirectories[i]); - } else if(pluginConfig.binary != "nodePackage" && pluginConfig.binary){ - // handle as fragile executable plugin - let args = pluginConfig.args || []; - plugins[I]=child_process.spawn(pluginConfig.binary, args, { - cwd: "./sharedPlugins/"+pluginDirectories[i], - stdio: ['pipe', 'pipe', 'pipe'], - }); - - /* - to send to stdin, use: - spawn.stdin.write("text\n"); - */ - // If plugin has subscribed to a file, send any text appearing in that file to stdin - if(pluginConfig.scriptOutputFileSubscription && typeof pluginConfig.scriptOutputFileSubscription == "string") { - if(global.subscribedFiles[pluginConfig.scriptOutputFileSubscription]) { - // please choose a unique file to subscribe to. If you need plugins to share this interface, set up a direct communication - // between those plugins instead. - throw "FATAL ERROR IN " + pluginDirectories[i] + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; - } - - if (!fs.existsSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription)) { - // Do something - fs.writeFileSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, ""); - } - global.subscribedFiles[pluginConfig.scriptOutputFileSubscription] = true; - fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, function (eventType, filename) { - // get array of lines in file - // TODO async - let stuff = fs.readFileSync(instancedirectory + "/script-output/" + filename, "utf8").split("\n"); - // if you found anything, reset the file - if (stuff[0]) { - fs.writeFileSync(instancedirectory + "/script-output/" + filename, ""); - } - for (let i = 0; i < stuff.length; i++) { - if (stuff[i]) { - plugins[I].stdin.write(stuff[i]); - } - } - }); - } - // these are our two config files. We need to send these in case plugin - // wants to contact master or know something. - // send through script-output file, maybe more compat? - fs.writeFileSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, JSON.stringify(combinedConfig)+"\r\n"); - // send directly through stdin - // plugins[i].stdin.write(JSON.stringify(combinedConfig)+"\n"); - - console.log("Clusterio | Loaded plugin " + pluginDirectories[i]); - plugins[i].stdout.on("data", (data) => { - if(data.toString('utf8')[0] != "/") { - log("Stdout: " + data.toString('utf8')) - } else { - messageInterface(data.toString('utf8')); - } - }); - plugins[i].stderr.on("data", (data) => { - log("STDERR: " + data); - }); - plugins[i].on('close', (code) => { - log(`child process exited with code ${code}`); - }); + console.log(`Clusterio | Loaded plugin ${pluginsToLoad[i].name} in ${Date.now() - pluginLoadStarted}ms`); } else { - // This plugin doesn't have a client portion + // this plugin doesn't have a client portion. Maybe it runs on the master only? } - } + } // world IDs ------------------------------------------------------------------ hashMods(instance, function(modHashes){ diff --git a/master.js b/master.js index b396e865..709e74cd 100644 --- a/master.js +++ b/master.js @@ -1078,7 +1078,7 @@ io.on('connection', function (socket) { if(terminatedConnections > 0) console.log("SOCKET | There are currently "+currentConnections+" websocket connections, deleting "+terminatedConnections+" on timeout"); // tell our friend that we are listening - socket.emit('hello', { hello: 'world' }); + setTimeout(()=>socket.emit('hello', { hello: 'world' }), 1000); /* initial processing for remoteMap */ socket.on('registerSlaveMapper', function (data) { @@ -1120,29 +1120,27 @@ pluginManagement(); masterPlugins = []; async function pluginManagement(){ let startPluginLoad = Date.now(); - masterPlugins = await getPlugins("sharedPlugins"); + masterPlugins = await getPlugins(); console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms"); return; } -async function getPlugins(pluginDirectory){ +async function getPlugins(){ + const pluginManager = require("./lib/manager/pluginManager.js")(config); let plugins = []; - - let files = await fs.readdir(pluginDirectory); - for(let i in files){ - let file = files[i]; + let pluginsToLoad = await pluginManager.getPlugins(); + for(let i = 0; i < pluginsToLoad.length; i++){ let pluginStartedLoading = Date.now(); // just for logging - let pluginPath = path.join(pluginDirectory, file); - let stats = await fs.stat(pluginPath); + let stats = await fs.stat(pluginsToLoad[i].pluginPath); if(stats.isDirectory()){ - let pluginConfig = require(path.resolve(pluginPath, "config")); - if(pluginConfig.masterPlugin){ - let masterPlugin = require(path.resolve(pluginPath, pluginConfig.masterPlugin)); + let pluginConfig = pluginsToLoad[i]; + if(pluginConfig.masterPlugin && pluginConfig.enabled){ + let masterPlugin = require(path.resolve(pluginConfig.pluginPath, pluginConfig.masterPlugin)); plugins.push({ main:new masterPlugin({ config, pluginConfig, - path: pluginPath, + path: pluginConfig.pluginPath, socketio: io, express: app, }),