diff --git a/packages/host/src/server.ts b/packages/host/src/server.ts index b7e426be..0057446d 100644 --- a/packages/host/src/server.ts +++ b/packages/host/src/server.ts @@ -1318,11 +1318,11 @@ export class FactorioServer extends events.EventEmitter { } - // Not using events.once here to avoid throwing on error events, as they are - // not emitted when the process exits while stopping. Returns the error + // Not using events.once here as it rejects on error events, which are not + // emitted when the process exits while stopping. Rejects with the error // emitted with the exit, if there was one, for the caller to report. async _waitForRconReadyOrExit() { - return await new Promise(resolve => { + return await new Promise((resolve, reject) => { let exitError: Error | undefined; const cleanup = () => { this.off("error", onError); @@ -1334,11 +1334,11 @@ export class FactorioServer extends events.EventEmitter { }; const onRconReady = () => { cleanup(); - resolve(undefined); + resolve(); }; const onExit = () => { cleanup(); - resolve(exitError); + reject(exitError ?? new Error("RCON connection lost")); }; this.on("error", onError); this.once("rcon-ready", onRconReady); @@ -1361,11 +1361,8 @@ export class FactorioServer extends events.EventEmitter { async sendRcon(message: string, expectEmpty?: boolean) { this._check(["running", "stopping"]); if (!this._rconReady) { - // Prefer the reason the server gave for exiting over the flat message below. - const exitError = await this._waitForRconReadyOrExit(); - if (exitError) { - throw exitError; - } + // Rejects with the reason the server gave for exiting, if there was one. + await this._waitForRconReadyOrExit(); } if (!this._rconClient) { throw new Error("RCON connection lost"); @@ -1437,7 +1434,7 @@ export class FactorioServer extends events.EventEmitter { // complete before the RCON connection can be used if (!this._rconReady) { // An exit error is not reported here as stopping is done once the process is gone. - await this._waitForRconReadyOrExit(); + await this._waitForRconReadyOrExit().catch(() => {}); } // The Factorio server may have decided to get ahead of us and diff --git a/test/host/server_stop.js b/test/host/server_stop.js index 26f52936..8cae2ac4 100644 --- a/test/host/server_stop.js +++ b/test/host/server_stop.js @@ -61,6 +61,19 @@ describe("host/src/server", function() { assert.equal(server.listenerCount("error"), 1); }); + it("does not reject if the process exits with an error before RCON is ready", async function() { + const server = createStartedServer(); + const stopped = server.stop(); + // The wait rejects with this error, stopping succeeds regardless. + server.emit("error", new lib.EnvironmentError("Factorio server was killed")); + server._server.emit("exit", null, "SIGKILL"); + await assert.doesNotReject(withTimeout(stopped, "stop() did not resolve")); + assert.equal(server._state, "init"); + assert.equal(server.listenerCount("rcon-ready"), 0); + assert.equal(server.listenerCount("exit"), 0); + assert.equal(server.listenerCount("error"), 1); + }); + it("stops the server if RCON becomes ready", async function() { const server = createStartedServer(); const stopped = server.stop();