Reject the RCON wait instead of resolving with an error

_waitForRconReadyOrExit resolved with an Error to signal the process
exited before RCON came up, which is an odd shape for a promise.  It now
resolves when RCON is ready and rejects otherwise, with the error emitted
with the exit, or "RCON connection lost" when there was none.

Observable behaviour is unchanged.  sendRcon rejects with the same
messages as before and stop ignores the rejection, as stopping is done
once the process is gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5 committed 2026-09-13 17:29:41 +00:00
1 parent 0273558edc
commit 2a967e84c5
2 files changed
+21 -11

No files matched your search

+8 -11
View File
@@ -1318,11 +1318,11 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
}
// 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<Error | undefined>(resolve => {
return await new Promise<void>((resolve, reject) => {
let exitError: Error | undefined;
const cleanup = () => {
this.off("error", onError);
@@ -1334,11 +1334,11 @@ export class FactorioServer extends events.EventEmitter<FactorioServerEvents> {
};
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<FactorioServerEvents> {
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<FactorioServerEvents> {
// 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
+13
View File
@@ -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();