From 56ee1fe955b0461a48e7029b68f4f5aec2a48f39 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Mon, 6 Apr 2020 16:42:27 +0200 Subject: [PATCH] Rework save logic with regard to start instance Ensure that notifyExit is called if errors occur during startInstance and createSave request, and rework startInstance save logic to create a new savegame if no save is present and allow specifying the save to load. --- README.md | 6 ++-- clusterctl.js | 2 ++ lib/link/messages.js | 2 +- slave.js | 76 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 116f9b94..afdb59cc 100644 --- a/README.md +++ b/README.md @@ -243,15 +243,13 @@ The basic operations to start a new instance is the following node clusterctl create-instance --name "My instance" node clusterctl assign-instance --instance "My instance" --slave "Local" - node clusterctl create-save --instance "My instance" node clusterctl start-instance --instance "My instance" The first line creates the instance configuration on the master server. The second assigns the instance to a slave which creates the instance directory and files needed to run the instance on the given slave. The -third line creates a new savegame for the instance. You could also -upload your own save to the instance directory instead. And finally the -fourth line starts the instance. +third line starts the instance, which creates a new save if there are no +save games present. There are many more commands available with clusterctl. See `node clusterctl --help` for a full list of them. diff --git a/clusterctl.js b/clusterctl.js index 89a0509a..f3e22c67 100644 --- a/clusterctl.js +++ b/clusterctl.js @@ -251,6 +251,7 @@ commands.push(new Command({ definition: ['start-instance', "Start instance", (yargs) => { yargs.options({ 'instance': { describe: "Instance to start", nargs: 1, type: 'string', demandOption: true }, + 'save': { describe: "Save load, defaults to latest", nargs: 1, type: 'string' }, }); }], handler: async function(args, control) { @@ -258,6 +259,7 @@ commands.push(new Command({ await link.messages.setInstanceOutputSubscriptions.send(control, { instance_ids: [instanceId] }); let response = await link.messages.startInstance.send(control, { instance_id: instanceId, + save: args.save || null, }); }, })); diff --git a/lib/link/messages.js b/lib/link/messages.js index 05305053..dc34b1a0 100644 --- a/lib/link/messages.js +++ b/lib/link/messages.js @@ -298,7 +298,7 @@ messages.startInstance = new Request({ type: 'start_instance', links: ['control-master', 'master-slave', 'slave-instance'], requestProperties: { - "instance_id": { type: "integer" }, + "save": { type: ["string", "null"] }, }, forwardTo: 'instance', }); diff --git a/slave.js b/slave.js index ce5859e2..6e466615 100644 --- a/slave.js +++ b/slave.js @@ -141,7 +141,6 @@ class Instance extends link.Link{ * @param {String} instanceDir - * Directory to create the new instance into. * @param {String} factorioDir - Path to factorio installation. - * @param {Object} options - Options for new instance. */ static async create(instanceConfig, instanceDir, factorioDir) { console.log(`Clusterio | Creating ${instanceDir}`); @@ -150,7 +149,18 @@ class Instance extends link.Link{ await fs.ensureDir(path.join(instanceDir, "saves")); } - async start(saveName, slaveConfig, socket) { + /** + * Prepare a save for starting + * + * Writes server settings, links mods, creates a new save if no save is + * passed, and patches it with modules. + * + * @param {String|null} saveName - + * Save to prepare from the instance saves directory. Creates a new + * save if null. + * @returns {String} Name of the save prepared. + */ + async prepare(saveName) { console.log("Clusterio | Writing server-settings.json"); await this.writeServerSettings(); @@ -168,6 +178,20 @@ class Instance extends link.Link{ await symlinkMods(this, "sharedMods", console); + // Use latest save if no save was specified + if (saveName === null) { + saveName = await fileOps.getNewestFile( + this.path("saves"), (name) => !name.endsWith('.tmp.zip') + ); + } + + // Create save if no save was found. + if (saveName === null) { + console.log("Clusterio | Creating new save") + await this.server.create("world.zip"); + saveName = "world.zip"; + } + // Patch save with lua modules from plugins console.log("Clusterio | Patching save"); @@ -228,7 +252,17 @@ class Instance extends link.Link{ } await factorio.patch(this.path("saves", saveName), [...modules.values()]); + return saveName; + } + /** + * Start Factorio server + * + * Launches the Factorio server for this instance with the given save. + * + * @param {String} saveName - Name of save game to load. + */ + async start(saveName) { this.server.on('rcon-ready', () => { console.log("Clusterio | RCON connection established"); }); @@ -264,7 +298,6 @@ class Instance extends link.Link{ await this.server.stop(); } - } async getMetricsRequestHandler() { @@ -281,26 +314,35 @@ class Instance extends link.Link{ return { results }; } - async startInstanceRequestHandler() { - // Find save to start - let latestSave = await fileOps.getNewestFile( - this.path("saves"), (name) => !name.endsWith('.tmp.zip') - ); - if (latestSave === null) { - throw new errors.RequestError( - "No savefile was found to start the instance with." - ); + async startInstanceRequestHandler(message) { + let saveName = message.data.save; + try { + saveName = await this.prepare(saveName) + } catch (err) { + this.notifyExit(); + throw err; } - await this.start(latestSave, this.config, this.socket); + try { + await this.start(saveName, this.config, this.socket); + } catch (err) { + await this.stop(); + throw err; + } } async createSaveRequestHandler() { - console.log("Clusterio | Writing server-settings.json"); - await this.writeServerSettings(); + try { + console.log("Clusterio | Writing server-settings.json"); + await this.writeServerSettings(); - console.log("Creating save ....."); - await symlinkMods(this, "sharedMods", console); + console.log("Creating save ....."); + await symlinkMods(this, "sharedMods", console); + + } catch (err) { + this.notifyExit(); + throw err; + } this.server.on("exit", () => this.notifyExit()); await this.server.create("world");