gdharness 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +29 -17
- package/build/index.js +29 -17
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -16409,7 +16409,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16409
16409
|
},
|
|
16410
16410
|
{
|
|
16411
16411
|
name: "editor_status",
|
|
16412
|
-
description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it.",
|
|
16412
|
+
description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved, which is that it was held when the bridge started, most often by the server a reconnect replaced.",
|
|
16413
16413
|
parameters: {},
|
|
16414
16414
|
requires: []
|
|
16415
16415
|
},
|
|
@@ -36256,7 +36256,8 @@ var init_godot_bridge = __esm(() => {
|
|
|
36256
36256
|
syncsBreakpoints: this.connectionInfo?.syncsBreakpoints,
|
|
36257
36257
|
pendingRequests: this.pendingRequests.size,
|
|
36258
36258
|
queuedResources: this.resourceQueues.size,
|
|
36259
|
-
listeningSince: this.listeningSince ?? undefined
|
|
36259
|
+
listeningSince: this.listeningSince ?? undefined,
|
|
36260
|
+
portWanted: this.wantedPort !== 0 && this.boundPort !== null && this.boundPort !== this.wantedPort ? this.wantedPort : undefined
|
|
36260
36261
|
};
|
|
36261
36262
|
}
|
|
36262
36263
|
invokeTool(toolName, args) {
|
|
@@ -38110,6 +38111,9 @@ function stillRunning(run) {
|
|
|
38110
38111
|
if (run?.exitCode !== null || run.endedUnwatched === true) {
|
|
38111
38112
|
return false;
|
|
38112
38113
|
}
|
|
38114
|
+
if (typeof run.endedHere === "string") {
|
|
38115
|
+
return false;
|
|
38116
|
+
}
|
|
38113
38117
|
if (!run.throughEditor && run.process === null && run.pid !== null) {
|
|
38114
38118
|
return alive(run.pid);
|
|
38115
38119
|
}
|
|
@@ -38122,6 +38126,9 @@ function runIsUp(run, editorSays, now = Date.now()) {
|
|
|
38122
38126
|
if (run?.throughEditor !== true) {
|
|
38123
38127
|
return stillRunning(run);
|
|
38124
38128
|
}
|
|
38129
|
+
if (typeof run.endedHere === "string") {
|
|
38130
|
+
return false;
|
|
38131
|
+
}
|
|
38125
38132
|
if (run.announcedPid !== undefined && !alive(run.announcedPid)) {
|
|
38126
38133
|
return false;
|
|
38127
38134
|
}
|
|
@@ -38135,9 +38142,12 @@ function noCodeWillCome(run) {
|
|
|
38135
38142
|
}
|
|
38136
38143
|
function endedWithoutACode(run) {
|
|
38137
38144
|
if (!run.throughEditor) {
|
|
38138
|
-
return "This run was started by another server, the one this server replaced or one running beside it, and it ended with nothing here waiting on it, so its exit code was never collected. Everything it printed is below, read back from its transcript.";
|
|
38145
|
+
return run.endedHere ? "This run was started by another server, so nothing here held it and its exit code was never collected. Everything it printed is below, read back from its transcript." : "This run was started by another server, the one this server replaced or one running beside it, and it ended with nothing here waiting on it, so its exit code was never collected. Everything it printed is below, read back from its transcript.";
|
|
38139
38146
|
}
|
|
38140
38147
|
const kept = "A game the editor plays is the editor's own child, so its exit code stays with the editor and none was collected here.";
|
|
38148
|
+
if (run.endedHere) {
|
|
38149
|
+
return `${kept} What it printed is below.`;
|
|
38150
|
+
}
|
|
38141
38151
|
if (!run.announced && run.couldAnnounce) {
|
|
38142
38152
|
return `The editor stopped playing this run before its game announced a runtime, so the game ended while it was starting or was closed in the editor. ${kept} What it printed is below, and editor_output with op "editor" has what the editor said about it.`;
|
|
38143
38153
|
}
|
|
@@ -39836,6 +39846,7 @@ class GodotServer {
|
|
|
39836
39846
|
awaitingLaunchedEditor: status.connected || !this.launchedEditorIsUp() ? undefined : this.launchedEditor?.pid ?? undefined,
|
|
39837
39847
|
...status.connected ? this.whatTheLaunchedEditorDropped() : {},
|
|
39838
39848
|
startupError: this.bridgeStartupError,
|
|
39849
|
+
portNote: status.portWanted === undefined ? undefined : `Port ${status.portWanted}, the one this server was configured with, was held by another process when the bridge started, so it took ${status.port} and announced that where the editor looks for it${this.announcedAt === null ? "" : `, ${this.announcedAt}`}. The editor finds it there; the next server started takes ${status.portWanted} again if it is free.`,
|
|
39839
39850
|
staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest()) : undefined,
|
|
39840
39851
|
...this.breakpointsAtRisk(),
|
|
39841
39852
|
retryingBridge: this.bridgeRetry === null ? undefined : true,
|
|
@@ -40140,7 +40151,7 @@ class GodotServer {
|
|
|
40140
40151
|
if (before !== null && await this.runStillGoing(before)) {
|
|
40141
40152
|
this.logDebug("Ending the running game before starting another");
|
|
40142
40153
|
ended = { pid: before.pid ?? this.announcedPidOf(before) ?? null };
|
|
40143
|
-
await this.endActiveGame("editor_run start, which ends the run that was going before it starts another");
|
|
40154
|
+
await this.endActiveGame("editor_run start, which ends the run that was going before it starts another", true);
|
|
40144
40155
|
}
|
|
40145
40156
|
const alreadyPlaying = new Set(discoverRuntimes().map((endpoint) => endpoint.pid));
|
|
40146
40157
|
const wereHereFirst = this.everyRuntimeOfOurs();
|
|
@@ -40381,12 +40392,11 @@ class GodotServer {
|
|
|
40381
40392
|
game.brokeOn = halt.text;
|
|
40382
40393
|
game.log.record("error", halt.text);
|
|
40383
40394
|
}
|
|
40384
|
-
async endActiveGame(reason) {
|
|
40395
|
+
async endActiveGame(reason, going) {
|
|
40385
40396
|
const running = this.activeProcess;
|
|
40386
|
-
this.activeProcess = null;
|
|
40387
40397
|
const recorded = running?.process === null ? readRunRecord() : null;
|
|
40388
40398
|
clearRunRecord((record) => this.couldBeOurs(record.projectPath));
|
|
40389
|
-
if (!running) {
|
|
40399
|
+
if (!running || !going) {
|
|
40390
40400
|
return;
|
|
40391
40401
|
}
|
|
40392
40402
|
running.endedHere = reason;
|
|
@@ -40550,7 +40560,8 @@ class GodotServer {
|
|
|
40550
40560
|
return;
|
|
40551
40561
|
}
|
|
40552
40562
|
const going = this.activeProcess;
|
|
40553
|
-
|
|
40563
|
+
const givesWay = going !== null && !stillRunning(going) && typeof going.endedHere === "string";
|
|
40564
|
+
if (going !== null && !givesWay) {
|
|
40554
40565
|
if (going.throughEditor && going.consoleLost === true && (await this.editorPlayingState())?.playing) {
|
|
40555
40566
|
try {
|
|
40556
40567
|
await this.dap().connect();
|
|
@@ -40957,11 +40968,17 @@ class GodotServer {
|
|
|
40957
40968
|
if (waitedMs !== undefined) {
|
|
40958
40969
|
notes.push(`The wait of ${waitedMs}ms ran out and the run is still going, so this is what it had printed by then rather than everything it will print. editor_run wait again to keep waiting.`);
|
|
40959
40970
|
}
|
|
40971
|
+
if (typeof run.endedHere === "string") {
|
|
40972
|
+
notes.push(`This run was ended here, by ${run.endedHere}.`);
|
|
40973
|
+
} else if (!stillRunning(run) && !run.throughEditor && run.exitCode !== null) {
|
|
40974
|
+
notes.push(run.exitCode === 0 ? "This run quit on its own, cleanly: exit code 0." : "Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
|
|
40975
|
+
}
|
|
40960
40976
|
if (run.endedUnwatched === true) {
|
|
40961
40977
|
notes.push(endedWithoutACode({
|
|
40962
40978
|
throughEditor: run.throughEditor,
|
|
40963
40979
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
40964
|
-
couldAnnounce: run.projectPath !== null && existsSync15(join23(run.projectPath, RUNTIME_AUTOLOAD2.path))
|
|
40980
|
+
couldAnnounce: run.projectPath !== null && existsSync15(join23(run.projectPath, RUNTIME_AUTOLOAD2.path)),
|
|
40981
|
+
endedHere: typeof run.endedHere === "string"
|
|
40965
40982
|
}));
|
|
40966
40983
|
}
|
|
40967
40984
|
if (run.pickedUpPlaying === true) {
|
|
@@ -40971,11 +40988,6 @@ class GodotServer {
|
|
|
40971
40988
|
const back = this.dapClient?.isConnected() === true;
|
|
40972
40989
|
notes.push(`The debug adapter this run's console arrives over went away while the run was going, so what is below has a hole in it and the counts are of what was heard rather than of what was printed. ${back ? "It is connected again, and the line marking where is in the log." : "It is still gone, so nothing further will arrive here."} The project's own user://logs/godot.log has the rest, and "no entries" here means "not heard" rather than a quiet run.`);
|
|
40973
40990
|
}
|
|
40974
|
-
if (typeof run.endedHere === "string") {
|
|
40975
|
-
notes.push(`This run was ended here, by ${run.endedHere}.`);
|
|
40976
|
-
} else if (!stillRunning(run) && !run.throughEditor && run.exitCode !== null) {
|
|
40977
|
-
notes.push(run.exitCode === 0 ? "This run quit on its own, cleanly: exit code 0." : "Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
|
|
40978
|
-
}
|
|
40979
40991
|
if (run.transcript !== null && selected.omitted > 0) {
|
|
40980
40992
|
notes.push(`Everything this run has printed is in ${run.transcript}, uncapped and still being written. Read it for output, not for whether the run is alive: a transcript that stops growing is a run between prints. running above is asked of the operating system while there is a process to ask about, and for an editor-played run that announced no runtime it is the editor's answer instead, which can lag after the game has gone.`);
|
|
40981
40993
|
}
|
|
@@ -41014,7 +41026,7 @@ class GodotServer {
|
|
|
41014
41026
|
const endedPid = stopped.pid ?? announcedPid;
|
|
41015
41027
|
const children = readBoolean(args, "andChildren") === true && wasRunning ? await this.whatTheRunStarted(stopped) : null;
|
|
41016
41028
|
this.logDebug("Stopping the running game");
|
|
41017
|
-
await this.endActiveGame("editor_run stop");
|
|
41029
|
+
await this.endActiveGame("editor_run stop", wasRunning);
|
|
41018
41030
|
const ended = children === null ? null : endChildrenAmong(children);
|
|
41019
41031
|
if (wasRunning) {
|
|
41020
41032
|
for (const pid of new Set([endedPid, announcedPid])) {
|
|
@@ -41033,12 +41045,12 @@ class GodotServer {
|
|
|
41033
41045
|
childrenLeft: ended.left.length === 0 ? undefined : ended.left,
|
|
41034
41046
|
childrenUnknown: ended.unknown === false ? undefined : true
|
|
41035
41047
|
},
|
|
41036
|
-
exitedBeforeStop:
|
|
41048
|
+
exitedBeforeStop: !wasRunning,
|
|
41037
41049
|
exitCode: stopped.exitCode,
|
|
41038
41050
|
errors: stopped.log.count("error"),
|
|
41039
41051
|
warnings: stopped.log.count("warning"),
|
|
41040
41052
|
clean: stopped.log.count("error") === 0,
|
|
41041
|
-
note: !wasRunning ? "This run was over before the stop, so nothing was ended here: editor_output has what it printed and how it ended. Anything that game started for itself, with OS.create_process or otherwise, is a separate process and may still be running." : `${stopped.throughEditor ? "The editor was asked to stop the scene it is playing." : "The process named under endedPid was ended."} ${aboutTheChildren(ended, stopped.throughEditor)}`,
|
|
41053
|
+
note: !wasRunning ? "This run was over before the stop, so nothing was ended here: editor_output has what it printed and how it ended. Anything that game started for itself, with OS.create_process or otherwise, is a separate process and may still be running." : `${stopped.throughEditor ? endedPid === null ? "The editor was asked to stop the scene it is playing. Its game had not announced a runtime, so no process was named under endedPid, and its exit code stays with the editor." : "The editor was asked to stop the scene it is playing." : "The process named under endedPid was ended."} ${aboutTheChildren(ended, stopped.throughEditor)}`,
|
|
41042
41054
|
entries: forAnswer(stopped.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries)
|
|
41043
41055
|
});
|
|
41044
41056
|
}
|
package/build/index.js
CHANGED
|
@@ -27218,7 +27218,8 @@ class GodotBridge extends EventEmitter {
|
|
|
27218
27218
|
syncsBreakpoints: this.connectionInfo?.syncsBreakpoints,
|
|
27219
27219
|
pendingRequests: this.pendingRequests.size,
|
|
27220
27220
|
queuedResources: this.resourceQueues.size,
|
|
27221
|
-
listeningSince: this.listeningSince ?? undefined
|
|
27221
|
+
listeningSince: this.listeningSince ?? undefined,
|
|
27222
|
+
portWanted: this.wantedPort !== 0 && this.boundPort !== null && this.boundPort !== this.wantedPort ? this.wantedPort : undefined
|
|
27222
27223
|
};
|
|
27223
27224
|
}
|
|
27224
27225
|
invokeTool(toolName, args) {
|
|
@@ -30280,7 +30281,7 @@ var TOOL_SPECS = [
|
|
|
30280
30281
|
},
|
|
30281
30282
|
{
|
|
30282
30283
|
name: "editor_status",
|
|
30283
|
-
description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it.",
|
|
30284
|
+
description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable: runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved, which is that it was held when the bridge started, most often by the server a reconnect replaced.",
|
|
30284
30285
|
parameters: {},
|
|
30285
30286
|
requires: []
|
|
30286
30287
|
},
|
|
@@ -30797,6 +30798,9 @@ function stillRunning(run) {
|
|
|
30797
30798
|
if (run?.exitCode !== null || run.endedUnwatched === true) {
|
|
30798
30799
|
return false;
|
|
30799
30800
|
}
|
|
30801
|
+
if (typeof run.endedHere === "string") {
|
|
30802
|
+
return false;
|
|
30803
|
+
}
|
|
30800
30804
|
if (!run.throughEditor && run.process === null && run.pid !== null) {
|
|
30801
30805
|
return alive(run.pid);
|
|
30802
30806
|
}
|
|
@@ -30810,6 +30814,9 @@ function runIsUp(run, editorSays, now = Date.now()) {
|
|
|
30810
30814
|
if (run?.throughEditor !== true) {
|
|
30811
30815
|
return stillRunning(run);
|
|
30812
30816
|
}
|
|
30817
|
+
if (typeof run.endedHere === "string") {
|
|
30818
|
+
return false;
|
|
30819
|
+
}
|
|
30813
30820
|
if (run.announcedPid !== undefined && !alive(run.announcedPid)) {
|
|
30814
30821
|
return false;
|
|
30815
30822
|
}
|
|
@@ -30823,9 +30830,12 @@ function noCodeWillCome(run) {
|
|
|
30823
30830
|
}
|
|
30824
30831
|
function endedWithoutACode(run) {
|
|
30825
30832
|
if (!run.throughEditor) {
|
|
30826
|
-
return "This run was started by another server, the one this server replaced or one running beside it, and it ended with nothing here waiting on it, so its exit code was never collected. Everything it printed is below, read back from its transcript.";
|
|
30833
|
+
return run.endedHere ? "This run was started by another server, so nothing here held it and its exit code was never collected. Everything it printed is below, read back from its transcript." : "This run was started by another server, the one this server replaced or one running beside it, and it ended with nothing here waiting on it, so its exit code was never collected. Everything it printed is below, read back from its transcript.";
|
|
30827
30834
|
}
|
|
30828
30835
|
const kept = "A game the editor plays is the editor's own child, so its exit code stays with the editor and none was collected here.";
|
|
30836
|
+
if (run.endedHere) {
|
|
30837
|
+
return `${kept} What it printed is below.`;
|
|
30838
|
+
}
|
|
30829
30839
|
if (!run.announced && run.couldAnnounce) {
|
|
30830
30840
|
return `The editor stopped playing this run before its game announced a runtime, so the game ended while it was starting or was closed in the editor. ${kept} What it printed is below, and editor_output with op "editor" has what the editor said about it.`;
|
|
30831
30841
|
}
|
|
@@ -32569,6 +32579,7 @@ class GodotServer {
|
|
|
32569
32579
|
awaitingLaunchedEditor: status.connected || !this.launchedEditorIsUp() ? undefined : this.launchedEditor?.pid ?? undefined,
|
|
32570
32580
|
...status.connected ? this.whatTheLaunchedEditorDropped() : {},
|
|
32571
32581
|
startupError: this.bridgeStartupError,
|
|
32582
|
+
portNote: status.portWanted === undefined ? undefined : `Port ${status.portWanted}, the one this server was configured with, was held by another process when the bridge started, so it took ${status.port} and announced that where the editor looks for it${this.announcedAt === null ? "" : `, ${this.announcedAt}`}. The editor finds it there; the next server started takes ${status.portWanted} again if it is free.`,
|
|
32572
32583
|
staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest()) : undefined,
|
|
32573
32584
|
...this.breakpointsAtRisk(),
|
|
32574
32585
|
retryingBridge: this.bridgeRetry === null ? undefined : true,
|
|
@@ -32873,7 +32884,7 @@ class GodotServer {
|
|
|
32873
32884
|
if (before !== null && await this.runStillGoing(before)) {
|
|
32874
32885
|
this.logDebug("Ending the running game before starting another");
|
|
32875
32886
|
ended = { pid: before.pid ?? this.announcedPidOf(before) ?? null };
|
|
32876
|
-
await this.endActiveGame("editor_run start, which ends the run that was going before it starts another");
|
|
32887
|
+
await this.endActiveGame("editor_run start, which ends the run that was going before it starts another", true);
|
|
32877
32888
|
}
|
|
32878
32889
|
const alreadyPlaying = new Set(discoverRuntimes().map((endpoint) => endpoint.pid));
|
|
32879
32890
|
const wereHereFirst = this.everyRuntimeOfOurs();
|
|
@@ -33114,12 +33125,11 @@ class GodotServer {
|
|
|
33114
33125
|
game.brokeOn = halt.text;
|
|
33115
33126
|
game.log.record("error", halt.text);
|
|
33116
33127
|
}
|
|
33117
|
-
async endActiveGame(reason) {
|
|
33128
|
+
async endActiveGame(reason, going) {
|
|
33118
33129
|
const running = this.activeProcess;
|
|
33119
|
-
this.activeProcess = null;
|
|
33120
33130
|
const recorded = running?.process === null ? readRunRecord() : null;
|
|
33121
33131
|
clearRunRecord((record) => this.couldBeOurs(record.projectPath));
|
|
33122
|
-
if (!running) {
|
|
33132
|
+
if (!running || !going) {
|
|
33123
33133
|
return;
|
|
33124
33134
|
}
|
|
33125
33135
|
running.endedHere = reason;
|
|
@@ -33283,7 +33293,8 @@ class GodotServer {
|
|
|
33283
33293
|
return;
|
|
33284
33294
|
}
|
|
33285
33295
|
const going = this.activeProcess;
|
|
33286
|
-
|
|
33296
|
+
const givesWay = going !== null && !stillRunning(going) && typeof going.endedHere === "string";
|
|
33297
|
+
if (going !== null && !givesWay) {
|
|
33287
33298
|
if (going.throughEditor && going.consoleLost === true && (await this.editorPlayingState())?.playing) {
|
|
33288
33299
|
try {
|
|
33289
33300
|
await this.dap().connect();
|
|
@@ -33690,11 +33701,17 @@ class GodotServer {
|
|
|
33690
33701
|
if (waitedMs !== undefined) {
|
|
33691
33702
|
notes.push(`The wait of ${waitedMs}ms ran out and the run is still going, so this is what it had printed by then rather than everything it will print. editor_run wait again to keep waiting.`);
|
|
33692
33703
|
}
|
|
33704
|
+
if (typeof run.endedHere === "string") {
|
|
33705
|
+
notes.push(`This run was ended here, by ${run.endedHere}.`);
|
|
33706
|
+
} else if (!stillRunning(run) && !run.throughEditor && run.exitCode !== null) {
|
|
33707
|
+
notes.push(run.exitCode === 0 ? "This run quit on its own, cleanly: exit code 0." : "Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
|
|
33708
|
+
}
|
|
33693
33709
|
if (run.endedUnwatched === true) {
|
|
33694
33710
|
notes.push(endedWithoutACode({
|
|
33695
33711
|
throughEditor: run.throughEditor,
|
|
33696
33712
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
33697
|
-
couldAnnounce: run.projectPath !== null && existsSync10(join16(run.projectPath, RUNTIME_AUTOLOAD.path))
|
|
33713
|
+
couldAnnounce: run.projectPath !== null && existsSync10(join16(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
33714
|
+
endedHere: typeof run.endedHere === "string"
|
|
33698
33715
|
}));
|
|
33699
33716
|
}
|
|
33700
33717
|
if (run.pickedUpPlaying === true) {
|
|
@@ -33704,11 +33721,6 @@ class GodotServer {
|
|
|
33704
33721
|
const back = this.dapClient?.isConnected() === true;
|
|
33705
33722
|
notes.push(`The debug adapter this run's console arrives over went away while the run was going, so what is below has a hole in it and the counts are of what was heard rather than of what was printed. ${back ? "It is connected again, and the line marking where is in the log." : "It is still gone, so nothing further will arrive here."} The project's own user://logs/godot.log has the rest, and "no entries" here means "not heard" rather than a quiet run.`);
|
|
33706
33723
|
}
|
|
33707
|
-
if (typeof run.endedHere === "string") {
|
|
33708
|
-
notes.push(`This run was ended here, by ${run.endedHere}.`);
|
|
33709
|
-
} else if (!stillRunning(run) && !run.throughEditor && run.exitCode !== null) {
|
|
33710
|
-
notes.push(run.exitCode === 0 ? "This run quit on its own, cleanly: exit code 0." : "Nothing here ended this run: it stopped on its own or something outside this server stopped it.");
|
|
33711
|
-
}
|
|
33712
33724
|
if (run.transcript !== null && selected.omitted > 0) {
|
|
33713
33725
|
notes.push(`Everything this run has printed is in ${run.transcript}, uncapped and still being written. Read it for output, not for whether the run is alive: a transcript that stops growing is a run between prints. running above is asked of the operating system while there is a process to ask about, and for an editor-played run that announced no runtime it is the editor's answer instead, which can lag after the game has gone.`);
|
|
33714
33726
|
}
|
|
@@ -33747,7 +33759,7 @@ class GodotServer {
|
|
|
33747
33759
|
const endedPid = stopped.pid ?? announcedPid;
|
|
33748
33760
|
const children = readBoolean(args, "andChildren") === true && wasRunning ? await this.whatTheRunStarted(stopped) : null;
|
|
33749
33761
|
this.logDebug("Stopping the running game");
|
|
33750
|
-
await this.endActiveGame("editor_run stop");
|
|
33762
|
+
await this.endActiveGame("editor_run stop", wasRunning);
|
|
33751
33763
|
const ended = children === null ? null : endChildrenAmong(children);
|
|
33752
33764
|
if (wasRunning) {
|
|
33753
33765
|
for (const pid of new Set([endedPid, announcedPid])) {
|
|
@@ -33766,12 +33778,12 @@ class GodotServer {
|
|
|
33766
33778
|
childrenLeft: ended.left.length === 0 ? undefined : ended.left,
|
|
33767
33779
|
childrenUnknown: ended.unknown === false ? undefined : true
|
|
33768
33780
|
},
|
|
33769
|
-
exitedBeforeStop:
|
|
33781
|
+
exitedBeforeStop: !wasRunning,
|
|
33770
33782
|
exitCode: stopped.exitCode,
|
|
33771
33783
|
errors: stopped.log.count("error"),
|
|
33772
33784
|
warnings: stopped.log.count("warning"),
|
|
33773
33785
|
clean: stopped.log.count("error") === 0,
|
|
33774
|
-
note: !wasRunning ? "This run was over before the stop, so nothing was ended here: editor_output has what it printed and how it ended. Anything that game started for itself, with OS.create_process or otherwise, is a separate process and may still be running." : `${stopped.throughEditor ? "The editor was asked to stop the scene it is playing." : "The process named under endedPid was ended."} ${aboutTheChildren(ended, stopped.throughEditor)}`,
|
|
33786
|
+
note: !wasRunning ? "This run was over before the stop, so nothing was ended here: editor_output has what it printed and how it ended. Anything that game started for itself, with OS.create_process or otherwise, is a separate process and may still be running." : `${stopped.throughEditor ? endedPid === null ? "The editor was asked to stop the scene it is playing. Its game had not announced a runtime, so no process was named under endedPid, and its exit code stays with the editor." : "The editor was asked to stop the scene it is playing." : "The process named under endedPid was ended."} ${aboutTheChildren(ended, stopped.throughEditor)}`,
|
|
33775
33787
|
entries: forAnswer(stopped.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries)
|
|
33776
33788
|
});
|
|
33777
33789
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|