gdharness 0.13.42 → 0.13.44
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 +384 -62
- package/build/godot/addons/gdharness_runtime/error_report.gd +62 -0
- package/build/godot/addons/gdharness_runtime/error_report.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +31 -0
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +12 -1
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +3 -2
- package/build/index.js +378 -58
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -7283,7 +7283,7 @@ class GameLog {
|
|
|
7283
7283
|
}
|
|
7284
7284
|
var HEADLINE, COLOUR_CODE, STREAMS, ADMITTED;
|
|
7285
7285
|
var init_game_log = __esm(() => {
|
|
7286
|
-
HEADLINE = /^(USER )?(SCRIPT ERROR|ERROR|WARNING):\s?(.*)$/;
|
|
7286
|
+
HEADLINE = /^(USER )?(SCRIPT ERROR|SHADER ERROR|ERROR|WARNING):\s?(.*)$/;
|
|
7287
7287
|
COLOUR_CODE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
7288
7288
|
STREAMS = ["stdout", "stderr", "transcript"];
|
|
7289
7289
|
ADMITTED = {
|
|
@@ -16015,7 +16015,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16015
16015
|
andChildren: {
|
|
16016
16016
|
type: "boolean",
|
|
16017
16017
|
ops: ["stop"],
|
|
16018
|
-
description: "stop: also end what the game started for itself, with OS.create_process or otherwise, such as a bench's workers. Only the
|
|
16018
|
+
description: "stop: also end what the game started for itself, with OS.create_process or otherwise, such as a bench's workers and whatever those started in turn. Only the processes under the run's, to any depth, that are games of this project are ended: announced as one, or this project's engine run with --path on this project by its command line, which is how a bench whose workers deliberately carry no runtime is reached. They are named under endedChildren, with what identified each under identifiedBy; any other process under the run is left and named under childrenLeft, and childrenUnknown is true when nothing could be listed, with the note saying whether the platform would not or the run has no process to list under. Default false, and the run's process is ended either way."
|
|
16019
16019
|
}
|
|
16020
16020
|
},
|
|
16021
16021
|
requires: [],
|
|
@@ -16038,7 +16038,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16038
16038
|
},
|
|
16039
16039
|
{
|
|
16040
16040
|
name: "editor_output",
|
|
16041
|
-
description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering.",
|
|
16041
|
+
description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering.",
|
|
16042
16042
|
parameters: {
|
|
16043
16043
|
severity: {
|
|
16044
16044
|
type: "string",
|
|
@@ -16155,7 +16155,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16155
16155
|
says: {
|
|
16156
16156
|
type: "string",
|
|
16157
16157
|
ops: ["find"],
|
|
16158
|
-
description: "find: part of what the node has written on it, case-insensitively, which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern."
|
|
16158
|
+
description: "find: part of what the node has written on it, case-insensitively, which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one."
|
|
16159
16159
|
},
|
|
16160
16160
|
limit: {
|
|
16161
16161
|
type: "number",
|
|
@@ -16373,7 +16373,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16373
16373
|
says: {
|
|
16374
16374
|
type: "string",
|
|
16375
16375
|
ops: ["until"],
|
|
16376
|
-
description: "until: wait for these words to appear anywhere under nodePath instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Case-insensitive, part of a line, hidden nodes included. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property."
|
|
16376
|
+
description: "until: wait for these words to appear anywhere under nodePath instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Case-insensitive, part of a line, hidden nodes included; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property."
|
|
16377
16377
|
},
|
|
16378
16378
|
timeoutMs: {
|
|
16379
16379
|
type: "number",
|
|
@@ -36420,29 +36420,133 @@ var init_lsp_client = __esm(() => {
|
|
|
36420
36420
|
|
|
36421
36421
|
// src/process-children.ts
|
|
36422
36422
|
import { execFile as execFile5 } from "node:child_process";
|
|
36423
|
+
import { basename as basename2 } from "node:path";
|
|
36423
36424
|
import { promisify as promisify5 } from "node:util";
|
|
36424
|
-
async function
|
|
36425
|
+
async function processTree() {
|
|
36425
36426
|
try {
|
|
36426
|
-
const printed = process.platform === "win32" ? await askWindows(
|
|
36427
|
-
return printed
|
|
36428
|
-
} catch
|
|
36429
|
-
if (process.platform !== "win32" && error.code === 1) {
|
|
36430
|
-
return [];
|
|
36431
|
-
}
|
|
36427
|
+
const printed = process.platform === "win32" ? await askWindows() : await askPosix();
|
|
36428
|
+
return parseProcessTable(printed);
|
|
36429
|
+
} catch {
|
|
36432
36430
|
return;
|
|
36433
36431
|
}
|
|
36434
36432
|
}
|
|
36435
|
-
|
|
36433
|
+
function parseProcessTable(printed) {
|
|
36434
|
+
const tree = new Map;
|
|
36435
|
+
for (const line of printed.split(/\r?\n/)) {
|
|
36436
|
+
const fields = /^\s*(\d+)\s+(\d+)(?:\s+(.*))?$/.exec(line);
|
|
36437
|
+
if (fields === null) {
|
|
36438
|
+
continue;
|
|
36439
|
+
}
|
|
36440
|
+
const pid = Number(fields[1]);
|
|
36441
|
+
const parent = Number(fields[2]);
|
|
36442
|
+
if (pid > 0) {
|
|
36443
|
+
tree.set(pid, { parent, command: (fields[3] ?? "").trim() });
|
|
36444
|
+
}
|
|
36445
|
+
}
|
|
36446
|
+
return tree;
|
|
36447
|
+
}
|
|
36448
|
+
function childrenIn(tree, pid) {
|
|
36449
|
+
const children = [];
|
|
36450
|
+
for (const [child, listed] of tree) {
|
|
36451
|
+
if (listed.parent === pid) {
|
|
36452
|
+
children.push(child);
|
|
36453
|
+
}
|
|
36454
|
+
}
|
|
36455
|
+
return children;
|
|
36456
|
+
}
|
|
36457
|
+
function descendantsIn(tree, pid) {
|
|
36458
|
+
const found = [];
|
|
36459
|
+
const queue = [pid];
|
|
36460
|
+
const seen = new Set([pid]);
|
|
36461
|
+
while (queue.length > 0) {
|
|
36462
|
+
const next = queue.shift();
|
|
36463
|
+
if (next === undefined) {
|
|
36464
|
+
break;
|
|
36465
|
+
}
|
|
36466
|
+
for (const child of childrenIn(tree, next)) {
|
|
36467
|
+
if (!seen.has(child)) {
|
|
36468
|
+
seen.add(child);
|
|
36469
|
+
found.push(child);
|
|
36470
|
+
queue.push(child);
|
|
36471
|
+
}
|
|
36472
|
+
}
|
|
36473
|
+
}
|
|
36474
|
+
return found;
|
|
36475
|
+
}
|
|
36476
|
+
function ancestorsIn(tree, pid, root) {
|
|
36477
|
+
const chain = [];
|
|
36478
|
+
const seen = new Set([pid]);
|
|
36479
|
+
let parent = tree.get(pid)?.parent;
|
|
36480
|
+
while (parent !== undefined && !seen.has(parent)) {
|
|
36481
|
+
chain.push(parent);
|
|
36482
|
+
if (parent === root) {
|
|
36483
|
+
break;
|
|
36484
|
+
}
|
|
36485
|
+
seen.add(parent);
|
|
36486
|
+
parent = tree.get(parent)?.parent;
|
|
36487
|
+
}
|
|
36488
|
+
return chain;
|
|
36489
|
+
}
|
|
36490
|
+
function readCommandLine(command) {
|
|
36491
|
+
const words = commandWords(command);
|
|
36492
|
+
const executable = words[0] === undefined ? "" : basename2(words[0].replaceAll("\\", "/"));
|
|
36493
|
+
const at = words.indexOf("--path");
|
|
36494
|
+
let projectPath = null;
|
|
36495
|
+
if (at !== -1 && at + 1 < words.length) {
|
|
36496
|
+
const taken = [];
|
|
36497
|
+
for (const word of words.slice(at + 1)) {
|
|
36498
|
+
if (taken.length > 0 && (word.startsWith("-") || /^[a-z]+:\/\//.test(word))) {
|
|
36499
|
+
break;
|
|
36500
|
+
}
|
|
36501
|
+
taken.push(word);
|
|
36502
|
+
}
|
|
36503
|
+
projectPath = taken.join(" ");
|
|
36504
|
+
}
|
|
36505
|
+
return {
|
|
36506
|
+
executable,
|
|
36507
|
+
projectPath,
|
|
36508
|
+
editor: words.some((word) => word === "-e" || word === "--editor")
|
|
36509
|
+
};
|
|
36510
|
+
}
|
|
36511
|
+
function commandWords(command) {
|
|
36512
|
+
const words = [];
|
|
36513
|
+
let word = "";
|
|
36514
|
+
let quoted = false;
|
|
36515
|
+
let started = false;
|
|
36516
|
+
for (const char of command) {
|
|
36517
|
+
if (char === '"') {
|
|
36518
|
+
quoted = !quoted;
|
|
36519
|
+
started = true;
|
|
36520
|
+
} else if (!quoted && /\s/.test(char)) {
|
|
36521
|
+
if (started) {
|
|
36522
|
+
words.push(word);
|
|
36523
|
+
word = "";
|
|
36524
|
+
started = false;
|
|
36525
|
+
}
|
|
36526
|
+
} else {
|
|
36527
|
+
word += char;
|
|
36528
|
+
started = true;
|
|
36529
|
+
}
|
|
36530
|
+
}
|
|
36531
|
+
if (started) {
|
|
36532
|
+
words.push(word);
|
|
36533
|
+
}
|
|
36534
|
+
return words;
|
|
36535
|
+
}
|
|
36536
|
+
async function askWindows() {
|
|
36436
36537
|
const { stdout } = await run5("powershell.exe", [
|
|
36437
36538
|
"-NoProfile",
|
|
36438
36539
|
"-NonInteractive",
|
|
36439
36540
|
"-Command",
|
|
36440
|
-
|
|
36441
|
-
], { timeout: ASK_TIMEOUT_MS, windowsHide: true });
|
|
36541
|
+
'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId) $($_.ParentProcessId) $($_.CommandLine)" }'
|
|
36542
|
+
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
36442
36543
|
return stdout;
|
|
36443
36544
|
}
|
|
36444
|
-
async function askPosix(
|
|
36445
|
-
const { stdout } = await run5("
|
|
36545
|
+
async function askPosix() {
|
|
36546
|
+
const { stdout } = await run5("ps", ["-e", "-o", "pid=,ppid=,args="], {
|
|
36547
|
+
timeout: ASK_TIMEOUT_MS,
|
|
36548
|
+
maxBuffer: 16 * 1024 * 1024
|
|
36549
|
+
});
|
|
36446
36550
|
return stdout;
|
|
36447
36551
|
}
|
|
36448
36552
|
var run5, ASK_TIMEOUT_MS = 15000;
|
|
@@ -36663,7 +36767,7 @@ var init_restart_note = __esm(() => {
|
|
|
36663
36767
|
});
|
|
36664
36768
|
|
|
36665
36769
|
// src/runtime-client.ts
|
|
36666
|
-
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync15, unlinkSync } from "node:fs";
|
|
36770
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync15, statSync as statSync3, unlinkSync } from "node:fs";
|
|
36667
36771
|
import { createConnection as createConnection3 } from "node:net";
|
|
36668
36772
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
36669
36773
|
import { join as join17, resolve as resolve4 } from "node:path";
|
|
@@ -36684,6 +36788,15 @@ function runtimeDirectories(variables = process.env) {
|
|
|
36684
36788
|
}
|
|
36685
36789
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
36686
36790
|
}
|
|
36791
|
+
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
36792
|
+
for (const directory of directories) {
|
|
36793
|
+
const path = join17(directory, `runtime-${pid}.log`);
|
|
36794
|
+
if (existsSync13(path)) {
|
|
36795
|
+
return path;
|
|
36796
|
+
}
|
|
36797
|
+
}
|
|
36798
|
+
return null;
|
|
36799
|
+
}
|
|
36687
36800
|
function processAlive(pid) {
|
|
36688
36801
|
try {
|
|
36689
36802
|
process.kill(pid, 0);
|
|
@@ -36772,6 +36885,11 @@ function announcedIn(directory) {
|
|
|
36772
36885
|
}
|
|
36773
36886
|
const found = [];
|
|
36774
36887
|
for (const entry of readdirSync5(directory)) {
|
|
36888
|
+
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
36889
|
+
if (report) {
|
|
36890
|
+
sweepErrorReport(join17(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
36891
|
+
continue;
|
|
36892
|
+
}
|
|
36775
36893
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
36776
36894
|
if (!match) {
|
|
36777
36895
|
continue;
|
|
@@ -36793,6 +36911,16 @@ function announcedIn(directory) {
|
|
|
36793
36911
|
}
|
|
36794
36912
|
return found;
|
|
36795
36913
|
}
|
|
36914
|
+
function sweepErrorReport(path, pid, now = Date.now()) {
|
|
36915
|
+
if (processAlive(pid)) {
|
|
36916
|
+
return;
|
|
36917
|
+
}
|
|
36918
|
+
try {
|
|
36919
|
+
if (now - statSync3(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
|
|
36920
|
+
unlinkSync(path);
|
|
36921
|
+
}
|
|
36922
|
+
} catch {}
|
|
36923
|
+
}
|
|
36796
36924
|
async function announcedSince(projectPath, before, waiting = {}) {
|
|
36797
36925
|
const wanted = resolve4(projectPath);
|
|
36798
36926
|
const directories = waiting.directories ?? runtimeDirectories();
|
|
@@ -36963,16 +37091,18 @@ function runtimeRequest(endpoint, command, params, timeoutMs) {
|
|
|
36963
37091
|
});
|
|
36964
37092
|
});
|
|
36965
37093
|
}
|
|
36966
|
-
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1;
|
|
37094
|
+
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1;
|
|
36967
37095
|
var init_runtime_client = __esm(() => {
|
|
36968
37096
|
ANNOUNCEMENT_PATTERN = /^runtime-(\d+)\.json$/;
|
|
37097
|
+
ERROR_REPORT_PATTERN = /^runtime-(\d+)\.log$/;
|
|
37098
|
+
ERROR_REPORT_KEEP_MS = 60 * 60 * 1000;
|
|
36969
37099
|
gone = [];
|
|
36970
37100
|
});
|
|
36971
37101
|
|
|
36972
37102
|
// src/run-record.ts
|
|
36973
37103
|
import { execFileSync } from "node:child_process";
|
|
36974
|
-
import { mkdirSync as mkdirSync9, openSync, readdirSync as readdirSync6, readFileSync as readFileSync16, rmSync as rmSync9, statSync as
|
|
36975
|
-
import { basename as
|
|
37104
|
+
import { mkdirSync as mkdirSync9, openSync, readdirSync as readdirSync6, readFileSync as readFileSync16, rmSync as rmSync9, statSync as statSync4, writeFileSync as writeFileSync11 } from "node:fs";
|
|
37105
|
+
import { basename as basename3, join as join18 } from "node:path";
|
|
36976
37106
|
function runsDirectory() {
|
|
36977
37107
|
return join18(runtimeDirectory(), "runs");
|
|
36978
37108
|
}
|
|
@@ -37188,7 +37318,7 @@ function judgeRun(record, running, needed) {
|
|
|
37188
37318
|
if (running.startedAt !== undefined && running.startedAt - record.startedAt > SAME_RUN_WINDOW_MS) {
|
|
37189
37319
|
return false;
|
|
37190
37320
|
}
|
|
37191
|
-
const engine = record.command === undefined ? null :
|
|
37321
|
+
const engine = record.command === undefined ? null : basename3(record.command);
|
|
37192
37322
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
37193
37323
|
const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
|
|
37194
37324
|
if (wanted !== null && !said.includes(wanted)) {
|
|
@@ -37219,7 +37349,7 @@ function clearRunRecord(ours = () => true) {
|
|
|
37219
37349
|
}
|
|
37220
37350
|
function ageMs(path, now) {
|
|
37221
37351
|
try {
|
|
37222
|
-
return now -
|
|
37352
|
+
return now - statSync4(path).mtimeMs;
|
|
37223
37353
|
} catch {
|
|
37224
37354
|
return 0;
|
|
37225
37355
|
}
|
|
@@ -37268,7 +37398,7 @@ import {
|
|
|
37268
37398
|
readSync
|
|
37269
37399
|
} from "node:fs";
|
|
37270
37400
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37271
|
-
import { basename as
|
|
37401
|
+
import { basename as basename4, dirname as dirname11, join as join19, normalize as normalize3, relative as relative3 } from "node:path";
|
|
37272
37402
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
37273
37403
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
37274
37404
|
import { promisify as promisify7 } from "node:util";
|
|
@@ -37322,16 +37452,43 @@ function endedPreviousRun(ended) {
|
|
|
37322
37452
|
}
|
|
37323
37453
|
return ended.pid ?? true;
|
|
37324
37454
|
}
|
|
37455
|
+
function engineNamesOf(executable) {
|
|
37456
|
+
return CONSOLE_WRAPPER.test(executable) ? [executable, executable.replace(CONSOLE_WRAPPER, "$1")] : [executable];
|
|
37457
|
+
}
|
|
37458
|
+
function gameBetween(tree, pid, root, games) {
|
|
37459
|
+
return ancestorsIn(tree, pid, root).some((one) => one !== root && games.has(one));
|
|
37460
|
+
}
|
|
37461
|
+
function endChildrenAmong(started) {
|
|
37462
|
+
if (started.unknown !== false) {
|
|
37463
|
+
return { ended: [], left: [], unknown: started.unknown };
|
|
37464
|
+
}
|
|
37465
|
+
for (const game of started.games) {
|
|
37466
|
+
try {
|
|
37467
|
+
process.kill(game.pid);
|
|
37468
|
+
} catch {}
|
|
37469
|
+
}
|
|
37470
|
+
return { ended: started.games, left: [...started.others], unknown: false };
|
|
37471
|
+
}
|
|
37325
37472
|
function aboutTheChildren(ended, throughEditor) {
|
|
37473
|
+
const reaches = "andChildren ends what it started that is a game of this project: announced as one, or this project's engine run with --path on it.";
|
|
37326
37474
|
if (ended === null) {
|
|
37327
|
-
return throughEditor ?
|
|
37475
|
+
return throughEditor ? `Anything that game started for itself, with OS.create_process or otherwise, is not the editor's to stop and was not signalled; ${reaches}` : `Anything that game started for itself, with OS.create_process or otherwise, is a separate process this stop did not signal; ${reaches}`;
|
|
37328
37476
|
}
|
|
37329
|
-
if (ended.unknown) {
|
|
37477
|
+
if (ended.unknown === "platform") {
|
|
37330
37478
|
return "andChildren was asked for and this platform would not list what the game had started, so nothing else was ended.";
|
|
37331
37479
|
}
|
|
37480
|
+
if (ended.unknown === "no process") {
|
|
37481
|
+
return "andChildren was asked for and this run has no process id to list under: the editor plays it and its game announced none, so nothing else was ended.";
|
|
37482
|
+
}
|
|
37332
37483
|
const counted = (n) => `${n} ${n === 1 ? "process" : "processes"}`;
|
|
37333
|
-
const
|
|
37334
|
-
const
|
|
37484
|
+
const announced = ended.ended.filter((one) => one.how === "announced").length;
|
|
37485
|
+
const byCommandLine = ended.ended.length - announced;
|
|
37486
|
+
const how = [
|
|
37487
|
+
announced === 0 ? "" : `${announced} announced as ${announced === 1 ? "a game" : "games"} of this project`,
|
|
37488
|
+
byCommandLine === 0 ? "" : `${byCommandLine} this project's engine run with --path on it, by ${byCommandLine === 1 ? "its" : "their"} command line`
|
|
37489
|
+
].filter((part) => part !== "").join(" and ");
|
|
37490
|
+
const endedToo = ended.ended.length === 0 ? "The game had started nothing that is a game of this project, announced as one or this project's engine run with --path on it, so nothing else was ended." : `${counted(ended.ended.length)} the game had started for itself ${ended.ended.length === 1 ? "was" : "were"} ended with it, named under endedChildren with what identified ${ended.ended.length === 1 ? "it" : "each"} under identifiedBy: ${how}.`;
|
|
37491
|
+
const leftToo = ended.left.length === 0 ? "" : ` ${counted(ended.left.length)} it had started ${ended.left.length === 1 ? "was" : "were"} left, named under childrenLeft: neither announced as a game of this project nor this project's engine run with --path on it, so what ${ended.left.length === 1 ? "it is" : "they are"} cannot be told from here.`;
|
|
37335
37492
|
return `${endedToo}${leftToo}`;
|
|
37336
37493
|
}
|
|
37337
37494
|
function endedToStartThis(ended) {
|
|
@@ -38397,7 +38554,7 @@ class GodotServer {
|
|
|
38397
38554
|
const name = application["config/name"];
|
|
38398
38555
|
const mainScene = application["run/main_scene"];
|
|
38399
38556
|
const info = {
|
|
38400
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
38557
|
+
name: typeof name === "string" && name !== "" ? name : basename4(project.value.path),
|
|
38401
38558
|
path: project.value.path,
|
|
38402
38559
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
38403
38560
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -39209,6 +39366,7 @@ class GodotServer {
|
|
|
39209
39366
|
});
|
|
39210
39367
|
this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
39211
39368
|
const started = this.spawnKeptGame(engine.value, cmdArgs, project.value.path);
|
|
39369
|
+
started.announcedBefore = alreadyPlaying;
|
|
39212
39370
|
this.activeProcess = started;
|
|
39213
39371
|
const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && given.value.length > 0 ? " The editor cannot be handed arguments for a game it plays, so this one was started " + "here: the debug_* tools answer only for a game the editor is playing." : "";
|
|
39214
39372
|
const endedForThis = endedToStartThis(ended);
|
|
@@ -39258,7 +39416,7 @@ class GodotServer {
|
|
|
39258
39416
|
}
|
|
39259
39417
|
});
|
|
39260
39418
|
const going = this.currentRun();
|
|
39261
|
-
if (endpoint !== null && going
|
|
39419
|
+
if (endpoint !== null && going !== null && going.announcedPid === undefined) {
|
|
39262
39420
|
going.announcedPid = endpoint.pid;
|
|
39263
39421
|
}
|
|
39264
39422
|
return runtimeVerdict(endpoint, {
|
|
@@ -39329,7 +39487,11 @@ class GodotServer {
|
|
|
39329
39487
|
});
|
|
39330
39488
|
}
|
|
39331
39489
|
drainEditorOutput(game) {
|
|
39332
|
-
if (!game.throughEditor
|
|
39490
|
+
if (!game.throughEditor) {
|
|
39491
|
+
return;
|
|
39492
|
+
}
|
|
39493
|
+
this.drainErrorReport(game);
|
|
39494
|
+
if (!this.dapClient) {
|
|
39333
39495
|
return;
|
|
39334
39496
|
}
|
|
39335
39497
|
if (game.transcript === null) {
|
|
@@ -39344,9 +39506,44 @@ class GodotServer {
|
|
|
39344
39506
|
}
|
|
39345
39507
|
this.recordWhatItBrokeOn(game);
|
|
39346
39508
|
}
|
|
39509
|
+
drainErrorReport(game) {
|
|
39510
|
+
if (game.errorReport === undefined) {
|
|
39511
|
+
const its = this.announcedPidOf(game);
|
|
39512
|
+
const found = its === undefined ? null : errorReportOf(its);
|
|
39513
|
+
if (found === null) {
|
|
39514
|
+
return;
|
|
39515
|
+
}
|
|
39516
|
+
game.errorReport = found;
|
|
39517
|
+
game.errorReportOffset = 0;
|
|
39518
|
+
}
|
|
39519
|
+
let handle;
|
|
39520
|
+
try {
|
|
39521
|
+
handle = openSync2(game.errorReport, "r");
|
|
39522
|
+
} catch {
|
|
39523
|
+
return;
|
|
39524
|
+
}
|
|
39525
|
+
try {
|
|
39526
|
+
const from = game.errorReportOffset ?? 0;
|
|
39527
|
+
const size = fstatSync(handle).size;
|
|
39528
|
+
if (size <= from) {
|
|
39529
|
+
return;
|
|
39530
|
+
}
|
|
39531
|
+
const buffer = Buffer.alloc(size - from);
|
|
39532
|
+
const read = readSync(handle, buffer, 0, buffer.length, from);
|
|
39533
|
+
game.errorReportOffset = from + read;
|
|
39534
|
+
const reported = buffer.subarray(0, read);
|
|
39535
|
+
if (game.transcript !== null) {
|
|
39536
|
+
appendFileSync(game.transcript, reported);
|
|
39537
|
+
} else {
|
|
39538
|
+
game.log.append("stderr", reported);
|
|
39539
|
+
}
|
|
39540
|
+
} finally {
|
|
39541
|
+
closeSync(handle);
|
|
39542
|
+
}
|
|
39543
|
+
}
|
|
39347
39544
|
recordWhatItBrokeOn(game) {
|
|
39348
39545
|
const halt = this.dapClient?.whereItStopped() ?? null;
|
|
39349
|
-
if (halt?.reason !== "exception" || halt.text === "" || game.brokeOn === halt.text) {
|
|
39546
|
+
if (halt?.reason !== "exception" || halt.text === "" || game.brokeOn === halt.text || game.errorReport !== undefined) {
|
|
39350
39547
|
return;
|
|
39351
39548
|
}
|
|
39352
39549
|
game.brokeOn = halt.text;
|
|
@@ -39544,12 +39741,16 @@ class GodotServer {
|
|
|
39544
39741
|
this.activeProcess = picked;
|
|
39545
39742
|
}
|
|
39546
39743
|
announcedPidOf(run, announced) {
|
|
39547
|
-
if (
|
|
39744
|
+
if (run.announcedPid !== undefined) {
|
|
39745
|
+
return run.announcedPid;
|
|
39746
|
+
}
|
|
39747
|
+
const ours = this.announcedOfTheRunsProject(run, announced ?? runtimesAnnounced().running);
|
|
39748
|
+
if (run.pid !== null && ours.some((one) => one.pid === run.pid)) {
|
|
39749
|
+
run.announcedPid = run.pid;
|
|
39548
39750
|
return run.announcedPid;
|
|
39549
39751
|
}
|
|
39550
39752
|
const before = run.announcedBefore ?? new Set;
|
|
39551
|
-
const
|
|
39552
|
-
const candidates = fresh.filter((one) => this.playedByOurEditor(one, run.projectPath));
|
|
39753
|
+
const candidates = ours.filter((one) => !before.has(one.pid) && this.isTheGameOf(run, one, run.projectPath));
|
|
39553
39754
|
const theOne = candidates.length === 1 ? candidates[0] : undefined;
|
|
39554
39755
|
if (theOne !== undefined) {
|
|
39555
39756
|
run.announcedPid = theOne.pid;
|
|
@@ -39564,8 +39765,68 @@ class GodotServer {
|
|
|
39564
39765
|
if (projectPath !== undefined && run.projectPath !== null && !isSameDirectory(run.projectPath, projectPath)) {
|
|
39565
39766
|
return;
|
|
39566
39767
|
}
|
|
39567
|
-
const its =
|
|
39568
|
-
return its !==
|
|
39768
|
+
const its = this.announcedPidOf(run, running);
|
|
39769
|
+
return its !== undefined && running.some((one) => one.pid === its) ? its : undefined;
|
|
39770
|
+
}
|
|
39771
|
+
async ownGameThroughTheTree(running, projectPath) {
|
|
39772
|
+
const run = this.currentRun();
|
|
39773
|
+
if (run === null || !stillRunning(run) || run.announcedPid !== undefined) {
|
|
39774
|
+
return;
|
|
39775
|
+
}
|
|
39776
|
+
if (projectPath !== undefined && run.projectPath !== null && !isSameDirectory(run.projectPath, projectPath)) {
|
|
39777
|
+
return;
|
|
39778
|
+
}
|
|
39779
|
+
const its = await this.tiedThroughTheTree(run, running);
|
|
39780
|
+
return its !== undefined && running.some((one) => one.pid === its) ? its : undefined;
|
|
39781
|
+
}
|
|
39782
|
+
async tiedThroughTheTree(run, announced) {
|
|
39783
|
+
if (run.announcedPid !== undefined) {
|
|
39784
|
+
return run.announcedPid;
|
|
39785
|
+
}
|
|
39786
|
+
const ours = this.announcedOfTheRunsProject(run, announced);
|
|
39787
|
+
if (ours.length === 0) {
|
|
39788
|
+
return;
|
|
39789
|
+
}
|
|
39790
|
+
const tree = await processTree();
|
|
39791
|
+
if (tree === undefined) {
|
|
39792
|
+
return;
|
|
39793
|
+
}
|
|
39794
|
+
return this.tieThroughTheTree(run, tree, new Set(ours.map((one) => one.pid)));
|
|
39795
|
+
}
|
|
39796
|
+
async gameProcessOf(run) {
|
|
39797
|
+
if (run.gamePid !== undefined) {
|
|
39798
|
+
return run.gamePid;
|
|
39799
|
+
}
|
|
39800
|
+
const announced = runtimesAnnounced().running;
|
|
39801
|
+
const tied = await this.tiedThroughTheTree(run, announced);
|
|
39802
|
+
if (tied !== undefined) {
|
|
39803
|
+
return tied;
|
|
39804
|
+
}
|
|
39805
|
+
const tree = await processTree();
|
|
39806
|
+
if (tree === undefined) {
|
|
39807
|
+
return;
|
|
39808
|
+
}
|
|
39809
|
+
const found = this.gameByCommandLineIn(run, tree);
|
|
39810
|
+
if (found !== undefined) {
|
|
39811
|
+
run.gamePid = found;
|
|
39812
|
+
}
|
|
39813
|
+
return found;
|
|
39814
|
+
}
|
|
39815
|
+
tieThroughTheTree(run, tree, games) {
|
|
39816
|
+
const root = run.pid ?? (run.throughEditor ? this.godotBridge.getStatus().editorPid : undefined);
|
|
39817
|
+
if (root === undefined) {
|
|
39818
|
+
return;
|
|
39819
|
+
}
|
|
39820
|
+
if (games.has(root)) {
|
|
39821
|
+
run.announcedPid = root;
|
|
39822
|
+
return root;
|
|
39823
|
+
}
|
|
39824
|
+
const nearest = descendantsIn(tree, root).filter((one) => games.has(one) && !gameBetween(tree, one, root, games));
|
|
39825
|
+
const theOne = nearest.length === 1 ? nearest[0] : undefined;
|
|
39826
|
+
if (theOne !== undefined) {
|
|
39827
|
+
run.announcedPid = theOne;
|
|
39828
|
+
}
|
|
39829
|
+
return theOne;
|
|
39569
39830
|
}
|
|
39570
39831
|
playedByOurEditor(endpoint, projectPath) {
|
|
39571
39832
|
const editor = this.godotBridge.getStatus().editorPid;
|
|
@@ -39643,6 +39904,13 @@ class GodotServer {
|
|
|
39643
39904
|
announcedForOurProject(announced) {
|
|
39644
39905
|
return this.allAnnouncedForOurProject(announced)[0] ?? null;
|
|
39645
39906
|
}
|
|
39907
|
+
announcedOfTheRunsProject(run, announced) {
|
|
39908
|
+
const project = run.projectPath;
|
|
39909
|
+
if (project === null) {
|
|
39910
|
+
return this.allAnnouncedForOurProject(announced);
|
|
39911
|
+
}
|
|
39912
|
+
return announced.filter((one) => one.project.path !== "" && isSameDirectory(project, one.project.path));
|
|
39913
|
+
}
|
|
39646
39914
|
allAnnouncedForOurProject(announced) {
|
|
39647
39915
|
const status = this.godotBridge.getStatus();
|
|
39648
39916
|
const mine = this.ownProject ?? (status.connected ? status.projectPath ?? null : null);
|
|
@@ -39757,7 +40025,8 @@ class GodotServer {
|
|
|
39757
40025
|
});
|
|
39758
40026
|
const hold = await this.holdOf(run, going);
|
|
39759
40027
|
const wanted = readBoolean(args, "cpu") ?? false;
|
|
39760
|
-
const
|
|
40028
|
+
const announced = this.announcedPidOf(run);
|
|
40029
|
+
const processToAsk = wanted ? announced ?? (stillRunning(run) ? await this.gameProcessOf(run) : undefined) ?? run.pid ?? null : null;
|
|
39761
40030
|
const cpuSeconds = wanted && processToAsk !== null && stillRunning(run) ? await cpuSecondsOf(processToAsk) : undefined;
|
|
39762
40031
|
const notes = [];
|
|
39763
40032
|
if (wanted && cpuSeconds === undefined && stillRunning(run)) {
|
|
@@ -39788,7 +40057,7 @@ class GodotServer {
|
|
|
39788
40057
|
running: going,
|
|
39789
40058
|
exitCode: run.exitCode,
|
|
39790
40059
|
through: run.throughEditor ? "editor" : "gdharness",
|
|
39791
|
-
pid: run.pid ??
|
|
40060
|
+
pid: run.pid ?? announced ?? run.announcedPid ?? null,
|
|
39792
40061
|
errors: run.log.count("error"),
|
|
39793
40062
|
warnings: run.log.count("warning"),
|
|
39794
40063
|
clean: run.consoleLost === true ? undefined : run.log.count("error") === 0,
|
|
@@ -39816,18 +40085,19 @@ class GodotServer {
|
|
|
39816
40085
|
this.drainTranscript(stopped);
|
|
39817
40086
|
const wasRunning = await this.runStillGoing(stopped);
|
|
39818
40087
|
const endedPid = stopped.pid ?? this.announcedPidOf(stopped) ?? null;
|
|
39819
|
-
const children = readBoolean(args, "andChildren") === true && wasRunning
|
|
40088
|
+
const children = readBoolean(args, "andChildren") === true && wasRunning ? await this.whatTheRunStarted(stopped) : null;
|
|
39820
40089
|
this.logDebug("Stopping the running game");
|
|
39821
40090
|
await this.endActiveGame("editor_run stop");
|
|
39822
|
-
const ended = children === null ? null :
|
|
40091
|
+
const ended = children === null ? null : endChildrenAmong(children);
|
|
39823
40092
|
return this.jsonTextResponse({
|
|
39824
40093
|
stopped: true,
|
|
39825
40094
|
through: stopped.throughEditor ? "editor" : "gdharness",
|
|
39826
40095
|
endedPid,
|
|
39827
40096
|
...ended === null ? {} : {
|
|
39828
|
-
endedChildren: ended.ended,
|
|
40097
|
+
endedChildren: ended.ended.map((one) => one.pid),
|
|
40098
|
+
identifiedBy: ended.ended.length === 0 ? undefined : Object.fromEntries(ended.ended.map((one) => [String(one.pid), one.identifiedBy])),
|
|
39829
40099
|
childrenLeft: ended.left.length === 0 ? undefined : ended.left,
|
|
39830
|
-
childrenUnknown: ended.unknown ?
|
|
40100
|
+
childrenUnknown: ended.unknown === false ? undefined : true
|
|
39831
40101
|
},
|
|
39832
40102
|
exitedBeforeStop: stopped.exitCode !== null || !wasRunning,
|
|
39833
40103
|
exitCode: stopped.exitCode,
|
|
@@ -39838,24 +40108,73 @@ class GodotServer {
|
|
|
39838
40108
|
entries: forAnswer(stopped.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries)
|
|
39839
40109
|
});
|
|
39840
40110
|
}
|
|
39841
|
-
|
|
39842
|
-
|
|
39843
|
-
|
|
39844
|
-
|
|
39845
|
-
|
|
39846
|
-
const
|
|
39847
|
-
const
|
|
39848
|
-
|
|
39849
|
-
|
|
39850
|
-
|
|
40111
|
+
async whatTheRunStarted(run) {
|
|
40112
|
+
const tree = await processTree();
|
|
40113
|
+
if (tree === undefined) {
|
|
40114
|
+
return { unknown: "platform" };
|
|
40115
|
+
}
|
|
40116
|
+
const announced = this.announcedOfTheRunsProject(run, runtimesAnnounced().running);
|
|
40117
|
+
const games = new Set(announced.map((one) => one.pid));
|
|
40118
|
+
const own = this.announcedPidOf(run, announced) ?? this.tieThroughTheTree(run, tree, games) ?? run.gamePid ?? this.gameByCommandLineIn(run, tree);
|
|
40119
|
+
if (own !== undefined && run.announcedPid === undefined) {
|
|
40120
|
+
run.gamePid = own;
|
|
40121
|
+
}
|
|
40122
|
+
const roots = [run.pid, own].filter((one) => one !== null && one !== undefined);
|
|
40123
|
+
if (roots.length === 0) {
|
|
40124
|
+
return { unknown: "no process" };
|
|
40125
|
+
}
|
|
40126
|
+
const under = new Set(roots.flatMap((root) => descendantsIn(tree, root)));
|
|
40127
|
+
for (const root of roots) {
|
|
40128
|
+
under.delete(root);
|
|
40129
|
+
}
|
|
40130
|
+
const started = [...under].sort((a, b) => a - b);
|
|
40131
|
+
const found = [];
|
|
40132
|
+
const others = [];
|
|
40133
|
+
for (const pid of started) {
|
|
40134
|
+
if (games.has(pid)) {
|
|
40135
|
+
found.push({ pid, how: "announced", identifiedBy: "announced as a game of this project" });
|
|
39851
40136
|
continue;
|
|
39852
40137
|
}
|
|
39853
|
-
|
|
39854
|
-
|
|
39855
|
-
|
|
39856
|
-
|
|
40138
|
+
const engines = [...roots, ...roots.flatMap((root) => ancestorsIn(tree, pid, root))];
|
|
40139
|
+
const read = this.theProjectsEngine(run, tree, pid, engines);
|
|
40140
|
+
if (read !== null) {
|
|
40141
|
+
found.push({
|
|
40142
|
+
pid,
|
|
40143
|
+
how: "command line",
|
|
40144
|
+
identifiedBy: `${read.executable} run with --path ${read.projectPath ?? ""}, this project's engine on this project`
|
|
40145
|
+
});
|
|
40146
|
+
continue;
|
|
40147
|
+
}
|
|
40148
|
+
others.push(pid);
|
|
40149
|
+
}
|
|
40150
|
+
return { unknown: false, games: found, others };
|
|
40151
|
+
}
|
|
40152
|
+
theProjectsEngine(run, tree, pid, engines) {
|
|
40153
|
+
const project = run.projectPath;
|
|
40154
|
+
if (project === null) {
|
|
40155
|
+
return null;
|
|
40156
|
+
}
|
|
40157
|
+
const read = readCommandLine(tree.get(pid)?.command ?? "");
|
|
40158
|
+
if (read.executable === "" || read.editor || read.projectPath === null || !isSameDirectory(read.projectPath, project)) {
|
|
40159
|
+
return null;
|
|
40160
|
+
}
|
|
40161
|
+
const names = engines.flatMap((one) => engineNamesOf(readCommandLine(tree.get(one)?.command ?? "").executable));
|
|
40162
|
+
const sameName = (one, other) => one !== "" && (process.platform === "win32" ? one.toLowerCase() === other.toLowerCase() : one === other);
|
|
40163
|
+
return names.some((name) => sameName(name, read.executable)) ? read : null;
|
|
40164
|
+
}
|
|
40165
|
+
gameByCommandLineIn(run, tree) {
|
|
40166
|
+
const root = run.pid ?? (run.throughEditor ? this.godotBridge.getStatus().editorPid : undefined);
|
|
40167
|
+
if (root === undefined || run.projectPath === null) {
|
|
40168
|
+
return;
|
|
40169
|
+
}
|
|
40170
|
+
const rootRead = readCommandLine(tree.get(root)?.command ?? "");
|
|
40171
|
+
const rootIsTheGame = !rootRead.editor && !CONSOLE_WRAPPER.test(rootRead.executable) && rootRead.projectPath !== null && isSameDirectory(rootRead.projectPath, run.projectPath);
|
|
40172
|
+
if (rootIsTheGame) {
|
|
40173
|
+
return;
|
|
39857
40174
|
}
|
|
39858
|
-
|
|
40175
|
+
const isEngine = (pid) => this.theProjectsEngine(run, tree, pid, [root]) !== null;
|
|
40176
|
+
const nearest = descendantsIn(tree, root).filter((pid) => isEngine(pid) && !ancestorsIn(tree, pid, root).some((one) => one !== root && isEngine(one)));
|
|
40177
|
+
return nearest.length === 1 ? nearest[0] : undefined;
|
|
39859
40178
|
}
|
|
39860
40179
|
async handleRescanFilesystem(args) {
|
|
39861
40180
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 30000;
|
|
@@ -40052,7 +40371,7 @@ class GodotServer {
|
|
|
40052
40371
|
const { op: _op, projectPath, pid, ...params } = asParams(args);
|
|
40053
40372
|
const announced = runtimesAnnounced();
|
|
40054
40373
|
const wanted = typeof projectPath === "string" ? projectPath : this.ownProject ?? undefined;
|
|
40055
|
-
const own = typeof pid === "number" ? undefined : this.ownGameAmong(announced.running, wanted);
|
|
40374
|
+
const own = typeof pid === "number" ? undefined : this.ownGameAmong(announced.running, wanted) ?? await this.ownGameThroughTheTree(announced.running, wanted);
|
|
40056
40375
|
const choice = chooseRuntime(announced.running, wanted, announced.unspoken, typeof pid === "number" ? pid : own);
|
|
40057
40376
|
if ("problem" in choice) {
|
|
40058
40377
|
return this.createErrorResponse(choice.problem);
|
|
@@ -40160,7 +40479,7 @@ async function runGodotServer() {
|
|
|
40160
40479
|
const server = new GodotServer;
|
|
40161
40480
|
await server.run();
|
|
40162
40481
|
}
|
|
40163
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CACHE_WRITE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
40482
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, CONSOLE_WRAPPER, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CACHE_WRITE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
40164
40483
|
var init_server3 = __esm(() => {
|
|
40165
40484
|
init_mcp();
|
|
40166
40485
|
init_stdio2();
|
|
@@ -40195,6 +40514,7 @@ var init_server3 = __esm(() => {
|
|
|
40195
40514
|
init_update_check();
|
|
40196
40515
|
run7 = promisify7(execFile7);
|
|
40197
40516
|
__dirname2 = dirname11(fileURLToPath4(import.meta.url));
|
|
40517
|
+
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
40198
40518
|
VALUE_NAMES = {
|
|
40199
40519
|
string: "a string",
|
|
40200
40520
|
number: "a number",
|
|
@@ -41719,7 +42039,9 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
41719
42039
|
false is a runtime that is not coming and true is a game still on its way up.
|
|
41720
42040
|
- Read \`editor_output\` after every run. It returns the engine's errors and warnings as entries
|
|
41721
42041
|
with their backtraces, and a \`clean\` verdict, so a run that printed an error is one call away
|
|
41722
|
-
from being known. For a
|
|
42042
|
+
from being known. For a run the editor plays, the errors come from the game through the runtime
|
|
42043
|
+
addon, so a played game without the addon is judged on its prints alone and \`clean\` there says
|
|
42044
|
+
less. For a long run it also names a transcript file: read that for output and this
|
|
41723
42045
|
answer for state. Never decide a run has ended by watching the file stop growing, or a process
|
|
41724
42046
|
list, or a timeout; a bench between prints looks exactly like a bench that died to everything else.
|
|
41725
42047
|
\`endedBy\` says whether gdharness ended it, and \`running\` is asked of the operating system
|
|
@@ -41752,7 +42074,7 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
41752
42074
|
| Where a 3D node is on screen | \`runtime_inspect\` \`rect\`, rather than unprojecting by hand |
|
|
41753
42075
|
| Wait for something in a running game | \`runtime_wait\`, never a sleep; \`until\` with \`says\` for a panel that rebuilds its own labels |
|
|
41754
42076
|
| Wait for a run to finish | \`editor_run wait\`, never a sleep or a shell loop on the pid: it answers as \`editor_output\` does, once the run is over |
|
|
41755
|
-
| End a bench and the workers it opened | \`editor_run stop\` with \`andChildren\`, never a process listing by scene name: it ends
|
|
42077
|
+
| End a bench and the workers it opened | \`editor_run stop\` with \`andChildren\`, never a process listing by scene name: it ends every process under the run's, to any depth, that is a game of the project, announced as one or the project's engine run with \`--path\` on it, names them under \`endedChildren\` with what identified each, and leaves and names any other |
|
|
41756
42078
|
| A picture, for a person who asked to see one | \`runtime_capture\` |
|
|
41757
42079
|
|
|
41758
42080
|
These need the runtime autoload, which \`gdharness setup\` registers. \`runtime_capture\` needs a
|