Skip to content

Commit e1a587b

Browse files
committed
dont hang if wql up fails to start children
1 parent ff054d9 commit e1a587b

4 files changed

Lines changed: 90 additions & 8 deletions

File tree

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
"init-local-env": "deno run --no-lock -A ./scripts/init-local-env.ts",
99
"init-project": "deno run --no-lock -A ./scripts/init-project.ts",
10-
"run-server": "cd multiplayer && deno task start --spawn-dir",
10+
"run-server": "cd multiplayer && deno task start --spawn-fail --spawn-dir",
1111
"run-editor": "cd editor && deno task build && deno run -A --no-lock jsr:@std/http/file-server --port=5173 web"
1212
},
1313
"imports": {

multiplayer/server-host/instance.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ export class GameInstance {
6666
#state: GameInstanceState = GameInstanceState.Idle;
6767
#status: string = "Idle";
6868
#statusDetail: string | undefined;
69+
#statusChangeListeners: ((
70+
state: GameInstanceState,
71+
status: string,
72+
detail?: string,
73+
) => void)[] = [];
6974
// prettier-ignore
7075
get state() { return this.#state; }
7176
// prettier-ignore
@@ -78,9 +83,26 @@ export class GameInstance {
7883
this.#statusDetail = detail;
7984

8085
this.logs.debug("Status updated", { ...{ status }, ...(detail ? { detail } : {}) });
86+
for (const listener of this.#statusChangeListeners) {
87+
listener(this.#state, this.#status, this.#statusDetail);
88+
}
89+
8190
this.bumpIdleTime();
8291
}
8392

93+
onStatusChange(
94+
listener: (state: GameInstanceState, status: string, detail?: string) => void,
95+
): { unsubscribe: () => void } {
96+
this.#statusChangeListeners.push(listener);
97+
return {
98+
unsubscribe: () => {
99+
const idx = this.#statusChangeListeners.indexOf(listener);
100+
if (idx === -1) return;
101+
this.#statusChangeListeners.splice(idx, 1);
102+
},
103+
};
104+
}
105+
84106
#notifyBooted: (() => void) | undefined;
85107
// deno-lint-ignore no-explicit-any
86108
#notifyBootFail: ((reason?: any) => void) | undefined;

multiplayer/server-host/main.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as path from "@std/path";
44
import { NIL_UUID } from "@std/uuid/constants";
55
import { CONFIG } from "./config.ts";
66
import { startInstanceCollector } from "./instance-collector.ts";
7-
import { createInstance, GameInstance } from "./instance.ts";
7+
import { createInstance, GameInstance, GameInstanceState } from "./instance.ts";
88
import { report } from "./metrics.ts";
99
import { setupWeb } from "./web/setup.ts";
1010
import { fetchWorld } from "./world-fetch.ts";
@@ -65,7 +65,7 @@ try {
6565

6666
const args = cli.parseArgs(Deno.args, {
6767
string: ["spawn", "spawn-dir", "spawn-repo", "clone"],
68-
boolean: ["play-mode"],
68+
boolean: ["play-mode", "spawn-fail"],
6969
});
7070

7171
if (args.clone !== undefined) {
@@ -101,6 +101,14 @@ await Promise.all([
101101
inspect: "127.0.0.1:9229",
102102
});
103103

104+
instance.onStatusChange(state => {
105+
if (!args["spawn-fail"]) return;
106+
if (state !== GameInstanceState.Errored) return;
107+
108+
shutdown();
109+
Deno.exit(1);
110+
});
111+
104112
await instance.waitForSessionBoot();
105113
} else if (args["spawn-dir"]) {
106114
const worldDirectory = args["spawn-dir"];
@@ -114,6 +122,14 @@ await Promise.all([
114122
inspect: "127.0.0.1:9229",
115123
});
116124

125+
instance.onStatusChange(state => {
126+
if (!args["spawn-fail"]) return;
127+
if (state !== GameInstanceState.Errored) return;
128+
129+
shutdown();
130+
Deno.exit(1);
131+
});
132+
117133
await instance.waitForSessionBoot();
118134
}
119135
})(),

scripts/wql.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,24 @@ const cli = new Command()
6060
const serverHandle = serverCmd.spawn();
6161
const editorHandle = editorCmd.spawn();
6262

63+
const shutdown = (code?: number) => {
64+
try {
65+
serverHandle.kill();
66+
} catch {
67+
// pass
68+
}
69+
70+
try {
71+
editorHandle.kill();
72+
} catch {
73+
// pass
74+
}
75+
76+
Deno.exit(code);
77+
};
78+
6379
Deno.addSignalListener("SIGINT", () => {
64-
serverHandle.kill();
65-
editorHandle.kill();
66-
Deno.exit();
80+
shutdown();
6781
});
6882

6983
const serverStarted = Promise.withResolvers<void>();
@@ -79,6 +93,16 @@ const cli = new Command()
7993
}
8094
})();
8195

96+
serverHandle.status.then(({ success }) => {
97+
if (!success) {
98+
serverStarted.reject();
99+
return;
100+
}
101+
102+
console.log("Dreamlab server exited");
103+
shutdown();
104+
});
105+
82106
const editorStarted = Promise.withResolvers<void>();
83107
void (async () => {
84108
const decoder = new TextDecoder();
@@ -92,12 +116,32 @@ const cli = new Command()
92116
}
93117
})();
94118

119+
editorHandle.status.then(({ success }) => {
120+
if (!success) {
121+
editorStarted.reject();
122+
return;
123+
}
124+
125+
console.log("Dreamlab Editor exited");
126+
shutdown();
127+
});
128+
95129
const s1 = spinner();
96130
s1.start("Starting Dreamlab server");
97-
await serverStarted.promise;
131+
try {
132+
await serverStarted.promise;
133+
} catch {
134+
s1.stop("Failed to start Dreamlab server", 2);
135+
shutdown(1);
136+
}
98137
s1.stop("Dreamlab server ready");
99138

100-
await editorStarted.promise;
139+
try {
140+
await editorStarted.promise;
141+
} catch {
142+
log.error("Failed to start Editor");
143+
shutdown(1);
144+
}
101145

102146
outro(`Open Dreamlab @ http://localhost:5173`);
103147

0 commit comments

Comments
 (0)