gdharness 1.2.38 → 1.2.39
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 +475 -256
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +7 -4
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +10 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +8 -4
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +5 -0
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +2 -2
- package/build/index.js +4299 -4076
- package/build/keeper.js +1 -0
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -11424,7 +11424,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11424
11424
|
name: "project_info",
|
|
11425
11425
|
title: "Project info",
|
|
11426
11426
|
effect: "read",
|
|
11427
|
-
description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, with optional sections on top.",
|
|
11427
|
+
description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, other counting every remaining file, leaving out the addons gdharness installed and any folder holding a .gdignore, with optional sections on top.",
|
|
11428
11428
|
parameters: {
|
|
11429
11429
|
projectPath: PROJECT_PATH,
|
|
11430
11430
|
include: {
|
|
@@ -11666,7 +11666,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11666
11666
|
},
|
|
11667
11667
|
reimport: {
|
|
11668
11668
|
effect: "destructive",
|
|
11669
|
-
summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
|
|
11669
|
+
summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. When nothing needs reimporting, nothing runs, and the answer has the two lists empty, a note saying so, and no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
|
|
11670
11670
|
requires: []
|
|
11671
11671
|
},
|
|
11672
11672
|
uid: {
|
|
@@ -11750,7 +11750,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11750
11750
|
timeoutMs: {
|
|
11751
11751
|
type: "integer",
|
|
11752
11752
|
minimum: 1,
|
|
11753
|
-
description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path."
|
|
11753
|
+
description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors."
|
|
11754
11754
|
},
|
|
11755
11755
|
printed: {
|
|
11756
11756
|
type: "string",
|
|
@@ -12421,7 +12421,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12421
12421
|
requires: []
|
|
12422
12422
|
}
|
|
12423
12423
|
},
|
|
12424
|
-
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
|
|
12424
|
+
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. The runtime addon's own functions, gdharness answering its tools inside the game, are not among them: their self time is runtimeAddonMs, over runtimeAddonFunctions functions. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. frames counts the frames of the profile that arrived, and the engine's totals also hold the work after the last of them, so once complete a function run every frame can show a call more than frames. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
|
|
12425
12425
|
defaultOperation: "start"
|
|
12426
12426
|
},
|
|
12427
12427
|
{
|
|
@@ -34899,11 +34899,51 @@ var init_stdio = __esm(() => {
|
|
|
34899
34899
|
swallowsErrorsAfterClose = Symbol("swallowsErrorsAfterClose");
|
|
34900
34900
|
});
|
|
34901
34901
|
|
|
34902
|
+
// src/autoload-failure.ts
|
|
34903
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
34904
|
+
import { join as join16 } from "node:path";
|
|
34905
|
+
function scriptsAutoloaded(projectPath) {
|
|
34906
|
+
const scripts = new Map;
|
|
34907
|
+
let settings;
|
|
34908
|
+
try {
|
|
34909
|
+
settings = parseProjectGodot(readFileSync13(join16(projectPath, "project.godot"), "utf8"));
|
|
34910
|
+
} catch {
|
|
34911
|
+
return scripts;
|
|
34912
|
+
}
|
|
34913
|
+
for (const [name, path] of autoloadPaths(settings)) {
|
|
34914
|
+
if (!path.endsWith(".tscn")) {
|
|
34915
|
+
scripts.set(`res://${path}`, name);
|
|
34916
|
+
continue;
|
|
34917
|
+
}
|
|
34918
|
+
const scene = join16(projectPath, path);
|
|
34919
|
+
if (!existsSync15(scene)) {
|
|
34920
|
+
continue;
|
|
34921
|
+
}
|
|
34922
|
+
try {
|
|
34923
|
+
for (const named of readFileSync13(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
|
|
34924
|
+
scripts.set(named[1] ?? "", name);
|
|
34925
|
+
}
|
|
34926
|
+
} catch {}
|
|
34927
|
+
}
|
|
34928
|
+
return scripts;
|
|
34929
|
+
}
|
|
34930
|
+
function autoloadNotCompiled(entry, autoloaded) {
|
|
34931
|
+
const path = NOT_COMPILED.exec(entry.text)?.[1];
|
|
34932
|
+
const name = path === undefined ? undefined : autoloaded.get(path);
|
|
34933
|
+
return path === undefined || name === undefined ? null : { name, path };
|
|
34934
|
+
}
|
|
34935
|
+
var NOT_COMPILED;
|
|
34936
|
+
var init_autoload_failure = __esm(() => {
|
|
34937
|
+
init_resources();
|
|
34938
|
+
init_setup();
|
|
34939
|
+
NOT_COMPILED = /^Failed to load script "(res:\/\/[^"]+)" with error "Compilation failed"/;
|
|
34940
|
+
});
|
|
34941
|
+
|
|
34902
34942
|
// src/runtime-client.ts
|
|
34903
|
-
import { existsSync as
|
|
34943
|
+
import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
34904
34944
|
import { createConnection } from "node:net";
|
|
34905
34945
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
34906
|
-
import { dirname as dirname10, join as
|
|
34946
|
+
import { dirname as dirname10, join as join17, resolve as resolve4 } from "node:path";
|
|
34907
34947
|
import { setTimeout as delay } from "node:timers/promises";
|
|
34908
34948
|
function runtimeDirectory(variables = process.env) {
|
|
34909
34949
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -34911,20 +34951,20 @@ function runtimeDirectory(variables = process.env) {
|
|
|
34911
34951
|
return explicit;
|
|
34912
34952
|
}
|
|
34913
34953
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
34914
|
-
return
|
|
34954
|
+
return join17(perUser ?? tmpdir4(), "gdharness");
|
|
34915
34955
|
}
|
|
34916
34956
|
function runtimeDirectories(variables = process.env) {
|
|
34917
34957
|
const candidates = [runtimeDirectory(variables)];
|
|
34918
34958
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
34919
34959
|
for (const base of fallbacks) {
|
|
34920
|
-
candidates.push(
|
|
34960
|
+
candidates.push(join17(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
34921
34961
|
}
|
|
34922
34962
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
34923
34963
|
}
|
|
34924
34964
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
34925
34965
|
for (const directory of directories) {
|
|
34926
|
-
const path =
|
|
34927
|
-
if (
|
|
34966
|
+
const path = join17(directory, `runtime-${pid}.log`);
|
|
34967
|
+
if (existsSync16(path)) {
|
|
34928
34968
|
return path;
|
|
34929
34969
|
}
|
|
34930
34970
|
}
|
|
@@ -34955,7 +34995,7 @@ function reportAlreadyInTranscript(transcript, report) {
|
|
|
34955
34995
|
function parseAnnouncement(file, pid) {
|
|
34956
34996
|
let fields;
|
|
34957
34997
|
try {
|
|
34958
|
-
fields = asParams(JSON.parse(
|
|
34998
|
+
fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
|
|
34959
34999
|
} catch {
|
|
34960
35000
|
return { kind: "rubbish" };
|
|
34961
35001
|
}
|
|
@@ -35021,7 +35061,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
35021
35061
|
function wentAway(file, pid) {
|
|
35022
35062
|
let project = "";
|
|
35023
35063
|
try {
|
|
35024
|
-
const fields = asParams(JSON.parse(
|
|
35064
|
+
const fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
|
|
35025
35065
|
project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
|
|
35026
35066
|
} catch {
|
|
35027
35067
|
return;
|
|
@@ -35064,7 +35104,7 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
|
|
|
35064
35104
|
}).map((one) => one.file));
|
|
35065
35105
|
}
|
|
35066
35106
|
function announcedIn(directory) {
|
|
35067
|
-
if (!
|
|
35107
|
+
if (!existsSync16(directory)) {
|
|
35068
35108
|
return [];
|
|
35069
35109
|
}
|
|
35070
35110
|
const found = [];
|
|
@@ -35073,14 +35113,14 @@ function announcedIn(directory) {
|
|
|
35073
35113
|
for (const entry of readdirSync10(directory)) {
|
|
35074
35114
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
35075
35115
|
if (report) {
|
|
35076
|
-
sweepErrorReport(
|
|
35116
|
+
sweepErrorReport(join17(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
35077
35117
|
continue;
|
|
35078
35118
|
}
|
|
35079
35119
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
35080
35120
|
if (!match) {
|
|
35081
35121
|
continue;
|
|
35082
35122
|
}
|
|
35083
|
-
const file =
|
|
35123
|
+
const file = join17(directory, entry);
|
|
35084
35124
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
35085
35125
|
const running = alive(pid);
|
|
35086
35126
|
entries.push({ file, pid, running });
|
|
@@ -35108,7 +35148,7 @@ function announcedIn(directory) {
|
|
|
35108
35148
|
found.push(announced);
|
|
35109
35149
|
}
|
|
35110
35150
|
const held = new Set(entries.map((one) => one.file));
|
|
35111
|
-
const here =
|
|
35151
|
+
const here = join17(directory, ".");
|
|
35112
35152
|
for (const file of verdicts.keys()) {
|
|
35113
35153
|
if (dirname10(file) === here && !held.has(file)) {
|
|
35114
35154
|
verdicts.delete(file);
|
|
@@ -35117,7 +35157,7 @@ function announcedIn(directory) {
|
|
|
35117
35157
|
return found;
|
|
35118
35158
|
}
|
|
35119
35159
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
35120
|
-
const files = directories.map((directory) =>
|
|
35160
|
+
const files = directories.map((directory) => join17(directory, `runtime-${pid}.json`)).filter((file) => existsSync16(file));
|
|
35121
35161
|
const first = files[0];
|
|
35122
35162
|
if (first === undefined) {
|
|
35123
35163
|
return;
|
|
@@ -35281,7 +35321,36 @@ function keepLate(id, answer) {
|
|
|
35281
35321
|
function lateAnswer(id) {
|
|
35282
35322
|
return lateAnswers.get(id);
|
|
35283
35323
|
}
|
|
35284
|
-
function
|
|
35324
|
+
function placeOf(endpoint) {
|
|
35325
|
+
return `${dialAddress(endpoint.address)}:${endpoint.port}`;
|
|
35326
|
+
}
|
|
35327
|
+
function waitForAPlace(game, granted) {
|
|
35328
|
+
const line = untaken.get(game) ?? { held: 0, waiting: [] };
|
|
35329
|
+
untaken.set(game, line);
|
|
35330
|
+
const grant = () => {
|
|
35331
|
+
line.held += 1;
|
|
35332
|
+
let given = false;
|
|
35333
|
+
granted(() => {
|
|
35334
|
+
if (given) {
|
|
35335
|
+
return;
|
|
35336
|
+
}
|
|
35337
|
+
given = true;
|
|
35338
|
+
line.held -= 1;
|
|
35339
|
+
const next = line.waiting.shift();
|
|
35340
|
+
if (next !== undefined) {
|
|
35341
|
+
next();
|
|
35342
|
+
} else if (line.held === 0 && untaken.get(game) === line) {
|
|
35343
|
+
untaken.delete(game);
|
|
35344
|
+
}
|
|
35345
|
+
});
|
|
35346
|
+
};
|
|
35347
|
+
if (line.held < UNTAKEN_PER_GAME) {
|
|
35348
|
+
grant();
|
|
35349
|
+
} else {
|
|
35350
|
+
line.waiting.push(grant);
|
|
35351
|
+
}
|
|
35352
|
+
}
|
|
35353
|
+
function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "drop") {
|
|
35285
35354
|
const id = nextRequestId++;
|
|
35286
35355
|
const sentAt = Date.now();
|
|
35287
35356
|
return new Promise((settle) => {
|
|
@@ -35289,16 +35358,32 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35289
35358
|
let late = false;
|
|
35290
35359
|
let buffered = "";
|
|
35291
35360
|
let welcomed = false;
|
|
35292
|
-
|
|
35361
|
+
let socket = null;
|
|
35362
|
+
let refusals = 0;
|
|
35363
|
+
let redial = null;
|
|
35364
|
+
let giveBackThePlace = null;
|
|
35365
|
+
const taken = () => {
|
|
35366
|
+
giveBackThePlace?.();
|
|
35367
|
+
giveBackThePlace = null;
|
|
35368
|
+
};
|
|
35293
35369
|
let lateTimer = null;
|
|
35294
35370
|
const finish = (reply) => {
|
|
35371
|
+
taken();
|
|
35372
|
+
if (redial !== null) {
|
|
35373
|
+
clearTimeout(redial);
|
|
35374
|
+
redial = null;
|
|
35375
|
+
}
|
|
35295
35376
|
if (late) {
|
|
35296
35377
|
if (lateTimer !== null) {
|
|
35297
35378
|
clearTimeout(lateTimer);
|
|
35298
35379
|
}
|
|
35299
|
-
socket
|
|
35300
|
-
keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
|
|
35380
|
+
socket?.destroy();
|
|
35301
35381
|
late = false;
|
|
35382
|
+
if (typeof afterTheWait === "function") {
|
|
35383
|
+
afterTheWait(reply);
|
|
35384
|
+
return;
|
|
35385
|
+
}
|
|
35386
|
+
keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
|
|
35302
35387
|
return;
|
|
35303
35388
|
}
|
|
35304
35389
|
if (done) {
|
|
@@ -35306,12 +35391,24 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35306
35391
|
}
|
|
35307
35392
|
done = true;
|
|
35308
35393
|
clearTimeout(timer);
|
|
35309
|
-
socket
|
|
35394
|
+
socket?.destroy();
|
|
35310
35395
|
settle(reply);
|
|
35311
35396
|
};
|
|
35312
35397
|
const timer = setTimeout(() => {
|
|
35398
|
+
if (socket === null) {
|
|
35399
|
+
finish(refusals > 0 ? {
|
|
35400
|
+
ok: false,
|
|
35401
|
+
reason: "refused",
|
|
35402
|
+
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: it refused ${refusals === 1 ? "the connection" : `all ${refusals} connections tried`} within ${timeoutMs}ms. Nothing listens there while a game is starting or shutting down, and the engine refuses connections while more are waiting for the game than the eight it queues, which several servers calling one game at once, or a long frame or a breakpoint holding it, can make it do. Nothing was sent, so nothing ran.`
|
|
35403
|
+
} : {
|
|
35404
|
+
ok: false,
|
|
35405
|
+
reason: "busy",
|
|
35406
|
+
message: `'${command}' was not sent to the game (${describe2(endpoint)}) within ${timeoutMs}ms: ${UNTAKEN_PER_GAME} connections from this server were already waiting for the game to take them, which it does between frames, so a long frame or a breakpoint holds them. Nothing was sent, so nothing ran.`
|
|
35407
|
+
});
|
|
35408
|
+
return;
|
|
35409
|
+
}
|
|
35313
35410
|
const still = welcomed ? `The game (${describe2(endpoint)}) has the request and did not answer '${command}' within ${timeoutMs}ms.` : `The game (${describe2(endpoint)}) did not answer '${command}' within ${timeoutMs}ms and had not taken the request: its frame has not ended since it was sent, which a long frame or a breakpoint does, and the request runs when it does.`;
|
|
35314
|
-
if (
|
|
35411
|
+
if (afterTheWait === "drop") {
|
|
35315
35412
|
finish({
|
|
35316
35413
|
ok: false,
|
|
35317
35414
|
reason: "busy",
|
|
@@ -35321,7 +35418,9 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35321
35418
|
}
|
|
35322
35419
|
done = true;
|
|
35323
35420
|
late = true;
|
|
35324
|
-
|
|
35421
|
+
if (afterTheWait === "keep") {
|
|
35422
|
+
keepLate(id, { state: "waiting", command, sentAt });
|
|
35423
|
+
}
|
|
35325
35424
|
lateTimer = setTimeout(() => {
|
|
35326
35425
|
finish({
|
|
35327
35426
|
ok: false,
|
|
@@ -35331,6 +35430,15 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35331
35430
|
}, LATE_ANSWER_MS);
|
|
35332
35431
|
lateTimer.unref();
|
|
35333
35432
|
socket.unref();
|
|
35433
|
+
if (afterTheWait !== "keep") {
|
|
35434
|
+
settle({
|
|
35435
|
+
ok: false,
|
|
35436
|
+
reason: "busy",
|
|
35437
|
+
stillWithTheGame: true,
|
|
35438
|
+
message: `${still} Nothing was cancelled: the game runs it when it gets to it, and what it answers then is not kept, so ask again once the game answers. A game held at a breakpoint instead is one debug_state stack answers about.`
|
|
35439
|
+
});
|
|
35440
|
+
return;
|
|
35441
|
+
}
|
|
35334
35442
|
settle({
|
|
35335
35443
|
ok: false,
|
|
35336
35444
|
reason: "busy",
|
|
@@ -35338,71 +35446,112 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35338
35446
|
message: `${still} Nothing was cancelled: a call that takes longer than the wait goes on and does everything it was asked, so read the state back rather than repeating it. runtime_invoke op result with requestId ${id} answers with its reply once it comes. A game held at a breakpoint instead is one debug_state stack answers about.`
|
|
35339
35447
|
});
|
|
35340
35448
|
}, timeoutMs);
|
|
35341
|
-
|
|
35342
|
-
|
|
35343
|
-
socket
|
|
35449
|
+
const open = () => {
|
|
35450
|
+
const opened = createConnection({ port: endpoint.port, host: dialAddress(endpoint.address) });
|
|
35451
|
+
socket = opened;
|
|
35452
|
+
opened.setEncoding("utf8");
|
|
35453
|
+
opened.on("connect", () => {
|
|
35454
|
+
opened.write(`${JSON.stringify({ id, command, params })}
|
|
35344
35455
|
`);
|
|
35345
|
-
|
|
35346
|
-
|
|
35347
|
-
|
|
35348
|
-
|
|
35456
|
+
});
|
|
35457
|
+
opened.on("data", (chunk) => {
|
|
35458
|
+
buffered += chunk;
|
|
35459
|
+
let newline = buffered.indexOf(`
|
|
35349
35460
|
`);
|
|
35350
|
-
|
|
35351
|
-
|
|
35352
|
-
|
|
35353
|
-
|
|
35461
|
+
while (newline !== -1 && (!done || late)) {
|
|
35462
|
+
const line = buffered.slice(0, newline).trim();
|
|
35463
|
+
buffered = buffered.slice(newline + 1);
|
|
35464
|
+
newline = buffered.indexOf(`
|
|
35354
35465
|
`);
|
|
35355
|
-
|
|
35356
|
-
|
|
35357
|
-
|
|
35358
|
-
|
|
35359
|
-
|
|
35360
|
-
|
|
35361
|
-
|
|
35362
|
-
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
|
|
35366
|
-
|
|
35367
|
-
|
|
35368
|
-
|
|
35369
|
-
|
|
35370
|
-
|
|
35371
|
-
|
|
35372
|
-
|
|
35373
|
-
|
|
35374
|
-
|
|
35466
|
+
if (line === "") {
|
|
35467
|
+
continue;
|
|
35468
|
+
}
|
|
35469
|
+
let message;
|
|
35470
|
+
try {
|
|
35471
|
+
message = asParams(JSON.parse(line));
|
|
35472
|
+
} catch {
|
|
35473
|
+
continue;
|
|
35474
|
+
}
|
|
35475
|
+
if (!welcomed) {
|
|
35476
|
+
welcomed = true;
|
|
35477
|
+
taken();
|
|
35478
|
+
if (readString(message, "type") === "welcome") {
|
|
35479
|
+
const spoken = readNumber(message, "protocol");
|
|
35480
|
+
if (spoken !== RUNTIME_PROTOCOL) {
|
|
35481
|
+
finish({
|
|
35482
|
+
ok: false,
|
|
35483
|
+
reason: "protocol",
|
|
35484
|
+
message: `The game (${describe2(endpoint)}) speaks runtime protocol ${spoken ?? "unknown"} and this server speaks ${RUNTIME_PROTOCOL}. Reinstall the runtime addon into the project.`
|
|
35485
|
+
});
|
|
35486
|
+
return;
|
|
35487
|
+
}
|
|
35488
|
+
continue;
|
|
35375
35489
|
}
|
|
35490
|
+
}
|
|
35491
|
+
if (message["id"] !== id) {
|
|
35376
35492
|
continue;
|
|
35377
35493
|
}
|
|
35494
|
+
if (readString(message, "type") === "error") {
|
|
35495
|
+
finish({
|
|
35496
|
+
ok: false,
|
|
35497
|
+
reason: "error",
|
|
35498
|
+
message: readString(message, "message") ?? "unknown error"
|
|
35499
|
+
});
|
|
35500
|
+
return;
|
|
35501
|
+
}
|
|
35502
|
+
finish({ ok: true, payload: message, endpoint });
|
|
35378
35503
|
}
|
|
35379
|
-
|
|
35380
|
-
|
|
35504
|
+
});
|
|
35505
|
+
opened.on("error", (error) => {
|
|
35506
|
+
if (socket !== opened) {
|
|
35507
|
+
return;
|
|
35381
35508
|
}
|
|
35382
|
-
if (
|
|
35383
|
-
|
|
35509
|
+
if (error.code === "ECONNREFUSED" && !done) {
|
|
35510
|
+
if (alive(endpoint.pid)) {
|
|
35511
|
+
refusals += 1;
|
|
35512
|
+
socket = null;
|
|
35513
|
+
opened.destroy();
|
|
35514
|
+
redial = setTimeout(() => {
|
|
35515
|
+
redial = null;
|
|
35516
|
+
open();
|
|
35517
|
+
}, REDIAL_MS);
|
|
35518
|
+
return;
|
|
35519
|
+
}
|
|
35520
|
+
finish({
|
|
35521
|
+
ok: false,
|
|
35522
|
+
reason: "refused",
|
|
35523
|
+
message: `The game (${describe2(endpoint)}) has ended: its port refused the connection and its process is gone.`
|
|
35524
|
+
});
|
|
35384
35525
|
return;
|
|
35385
35526
|
}
|
|
35386
|
-
finish({
|
|
35387
|
-
|
|
35388
|
-
|
|
35389
|
-
|
|
35390
|
-
|
|
35391
|
-
ok: false,
|
|
35392
|
-
reason: "refused",
|
|
35393
|
-
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting, or be shutting down.`
|
|
35527
|
+
finish({
|
|
35528
|
+
ok: false,
|
|
35529
|
+
reason: "refused",
|
|
35530
|
+
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting or be shutting down.`
|
|
35531
|
+
});
|
|
35394
35532
|
});
|
|
35395
|
-
|
|
35396
|
-
|
|
35397
|
-
|
|
35398
|
-
|
|
35399
|
-
|
|
35400
|
-
|
|
35533
|
+
opened.on("close", () => {
|
|
35534
|
+
if (socket !== opened) {
|
|
35535
|
+
return;
|
|
35536
|
+
}
|
|
35537
|
+
finish({
|
|
35538
|
+
ok: false,
|
|
35539
|
+
reason: "refused",
|
|
35540
|
+
message: `The game (${describe2(endpoint)}) closed the connection before answering '${command}'.`
|
|
35541
|
+
});
|
|
35401
35542
|
});
|
|
35543
|
+
};
|
|
35544
|
+
waitForAPlace(placeOf(endpoint), (giveBack) => {
|
|
35545
|
+
if (done) {
|
|
35546
|
+
giveBack();
|
|
35547
|
+
return;
|
|
35548
|
+
}
|
|
35549
|
+
giveBackThePlace = giveBack;
|
|
35550
|
+
open();
|
|
35402
35551
|
});
|
|
35403
35552
|
});
|
|
35404
35553
|
}
|
|
35405
|
-
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers;
|
|
35554
|
+
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers, UNTAKEN_PER_GAME = 4, REDIAL_MS = 50, untaken;
|
|
35406
35555
|
var init_runtime_client = __esm(() => {
|
|
35407
35556
|
init_alive();
|
|
35408
35557
|
init_launch();
|
|
@@ -35415,17 +35564,18 @@ var init_runtime_client = __esm(() => {
|
|
|
35415
35564
|
verdicts = new Map;
|
|
35416
35565
|
LATE_ANSWER_MS = 30 * 60000;
|
|
35417
35566
|
lateAnswers = new Map;
|
|
35567
|
+
untaken = new Map;
|
|
35418
35568
|
});
|
|
35419
35569
|
|
|
35420
35570
|
// src/boot-note.ts
|
|
35421
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
35422
|
-
import { dirname as dirname11, join as
|
|
35571
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
35572
|
+
import { dirname as dirname11, join as join18 } from "node:path";
|
|
35423
35573
|
function bootNotePath(projectPath) {
|
|
35424
|
-
return
|
|
35574
|
+
return join18(projectPath, ".godot", "gdharness-boot.json");
|
|
35425
35575
|
}
|
|
35426
35576
|
function readScenes(projectPath) {
|
|
35427
35577
|
try {
|
|
35428
|
-
return asParams(asParams(JSON.parse(
|
|
35578
|
+
return asParams(asParams(JSON.parse(readFileSync15(bootNotePath(projectPath), "utf8")))["scenes"]);
|
|
35429
35579
|
} catch {
|
|
35430
35580
|
return {};
|
|
35431
35581
|
}
|
|
@@ -35467,10 +35617,10 @@ var init_boot_note = __esm(() => {
|
|
|
35467
35617
|
});
|
|
35468
35618
|
|
|
35469
35619
|
// src/breakpoint-note.ts
|
|
35470
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
35471
|
-
import { dirname as dirname12, isAbsolute as isAbsolute4, join as
|
|
35620
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
35621
|
+
import { dirname as dirname12, isAbsolute as isAbsolute4, join as join19, relative as relative3 } from "node:path";
|
|
35472
35622
|
function breakpointNotePath(projectPath) {
|
|
35473
|
-
return
|
|
35623
|
+
return join19(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
35474
35624
|
}
|
|
35475
35625
|
function writeBreakpointNote(projectPath, held) {
|
|
35476
35626
|
const path = breakpointNotePath(projectPath);
|
|
@@ -35496,7 +35646,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
35496
35646
|
}
|
|
35497
35647
|
function readBreakpointNote(projectPath) {
|
|
35498
35648
|
try {
|
|
35499
|
-
const parsed = JSON.parse(
|
|
35649
|
+
const parsed = JSON.parse(readFileSync16(breakpointNotePath(projectPath), "utf8"));
|
|
35500
35650
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
|
|
35501
35651
|
return [];
|
|
35502
35652
|
}
|
|
@@ -35511,7 +35661,7 @@ function readBreakpointNote(projectPath) {
|
|
|
35511
35661
|
}
|
|
35512
35662
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
35513
35663
|
if (lines.length > 0) {
|
|
35514
|
-
held.push({ scriptPath: realPathOr(
|
|
35664
|
+
held.push({ scriptPath: realPathOr(join19(projectPath, script)), lines });
|
|
35515
35665
|
}
|
|
35516
35666
|
}
|
|
35517
35667
|
return held;
|
|
@@ -35560,14 +35710,14 @@ var init_whole_file = __esm(() => {
|
|
|
35560
35710
|
});
|
|
35561
35711
|
|
|
35562
35712
|
// src/class-note.ts
|
|
35563
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
35564
|
-
import { dirname as dirname13, join as
|
|
35713
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
|
|
35714
|
+
import { dirname as dirname13, join as join20 } from "node:path";
|
|
35565
35715
|
function classNotePath(projectPath) {
|
|
35566
|
-
return
|
|
35716
|
+
return join20(projectPath, ".godot", "gdharness-classes.json");
|
|
35567
35717
|
}
|
|
35568
35718
|
function readClassNote(projectPath) {
|
|
35569
35719
|
try {
|
|
35570
|
-
const fields = asParams(JSON.parse(
|
|
35720
|
+
const fields = asParams(JSON.parse(readFileSync17(classNotePath(projectPath), "utf8")));
|
|
35571
35721
|
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
35572
35722
|
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
35573
35723
|
return null;
|
|
@@ -35609,16 +35759,16 @@ var init_class_note = __esm(() => {
|
|
|
35609
35759
|
});
|
|
35610
35760
|
|
|
35611
35761
|
// src/config-pin.ts
|
|
35612
|
-
import { existsSync as
|
|
35613
|
-
import { join as
|
|
35762
|
+
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
|
|
35763
|
+
import { join as join21 } from "node:path";
|
|
35614
35764
|
function versionsNamedInConfig(projectPath) {
|
|
35615
|
-
const config =
|
|
35616
|
-
if (!
|
|
35765
|
+
const config = join21(projectPath, ".mcp.json");
|
|
35766
|
+
if (!existsSync17(config)) {
|
|
35617
35767
|
return [];
|
|
35618
35768
|
}
|
|
35619
35769
|
let text;
|
|
35620
35770
|
try {
|
|
35621
|
-
text =
|
|
35771
|
+
text = readFileSync18(config, "utf8");
|
|
35622
35772
|
JSON.parse(text);
|
|
35623
35773
|
} catch {
|
|
35624
35774
|
return [];
|
|
@@ -35639,10 +35789,10 @@ var init_config_pin = __esm(() => {
|
|
|
35639
35789
|
});
|
|
35640
35790
|
|
|
35641
35791
|
// src/cut-test-run.ts
|
|
35642
|
-
import { existsSync as
|
|
35643
|
-
import { dirname as dirname14, join as
|
|
35792
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync19, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
35793
|
+
import { dirname as dirname14, join as join22 } from "node:path";
|
|
35644
35794
|
function keptAt(projectPath) {
|
|
35645
|
-
return
|
|
35795
|
+
return join22(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
35646
35796
|
}
|
|
35647
35797
|
function keepCutTestRun(projectPath, run) {
|
|
35648
35798
|
const path = keptAt(projectPath);
|
|
@@ -35654,11 +35804,11 @@ function keepCutTestRun(projectPath, run) {
|
|
|
35654
35804
|
}
|
|
35655
35805
|
function takeCutTestRun(projectPath) {
|
|
35656
35806
|
const path = keptAt(projectPath);
|
|
35657
|
-
if (!
|
|
35807
|
+
if (!existsSync18(path)) {
|
|
35658
35808
|
return null;
|
|
35659
35809
|
}
|
|
35660
35810
|
try {
|
|
35661
|
-
const run = JSON.parse(
|
|
35811
|
+
const run = JSON.parse(readFileSync19(path, "utf8"));
|
|
35662
35812
|
rmSync9(path, { force: true });
|
|
35663
35813
|
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
35664
35814
|
} catch {
|
|
@@ -36471,27 +36621,27 @@ var init_dap_client = __esm(() => {
|
|
|
36471
36621
|
import { createHash as createHash4 } from "node:crypto";
|
|
36472
36622
|
import {
|
|
36473
36623
|
closeSync,
|
|
36474
|
-
existsSync as
|
|
36624
|
+
existsSync as existsSync19,
|
|
36475
36625
|
mkdtempSync as mkdtempSync2,
|
|
36476
36626
|
openSync,
|
|
36477
|
-
readFileSync as
|
|
36627
|
+
readFileSync as readFileSync20,
|
|
36478
36628
|
rmSync as rmSync10,
|
|
36479
36629
|
statSync as statSync9,
|
|
36480
36630
|
writeFileSync as writeFileSync12,
|
|
36481
36631
|
writeSync
|
|
36482
36632
|
} from "node:fs";
|
|
36483
36633
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
36484
|
-
import { join as
|
|
36634
|
+
import { join as join23 } from "node:path";
|
|
36485
36635
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
36486
36636
|
function helperAssembly(directory = tmpdir5()) {
|
|
36487
|
-
return
|
|
36637
|
+
return join23(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
36488
36638
|
}
|
|
36489
36639
|
async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
36490
36640
|
const assembly = helperAssembly(directory);
|
|
36491
36641
|
const lock = `${assembly}.compiling`;
|
|
36492
36642
|
const until = Date.now() + budgetMs;
|
|
36493
36643
|
while (Date.now() < until) {
|
|
36494
|
-
if (
|
|
36644
|
+
if (existsSync19(assembly)) {
|
|
36495
36645
|
return null;
|
|
36496
36646
|
}
|
|
36497
36647
|
let mine = false;
|
|
@@ -36509,7 +36659,7 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36509
36659
|
}
|
|
36510
36660
|
}
|
|
36511
36661
|
try {
|
|
36512
|
-
const holder = Number(
|
|
36662
|
+
const holder = Number(readFileSync20(lock, "utf8").trim());
|
|
36513
36663
|
const old = Date.now() - statSync9(lock).mtimeMs > budgetMs;
|
|
36514
36664
|
if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
|
|
36515
36665
|
rmSync10(lock, { force: true });
|
|
@@ -36524,8 +36674,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36524
36674
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
36525
36675
|
}
|
|
36526
36676
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
36527
|
-
const scratch = mkdtempSync2(
|
|
36528
|
-
const source =
|
|
36677
|
+
const scratch = mkdtempSync2(join23(directory, "gdharness-desktop-source-"));
|
|
36678
|
+
const source = join23(scratch, "helper.cs");
|
|
36529
36679
|
writeFileSync12(source, SOURCE, "utf8");
|
|
36530
36680
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
36531
36681
|
const script = [
|
|
@@ -36563,7 +36713,7 @@ async function compileHelper(directory, assembly, budgetMs) {
|
|
|
36563
36713
|
});
|
|
36564
36714
|
compiler.once("close", (code) => {
|
|
36565
36715
|
clearTimeout(timer);
|
|
36566
|
-
resolve(
|
|
36716
|
+
resolve(existsSync19(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
|
|
36567
36717
|
});
|
|
36568
36718
|
});
|
|
36569
36719
|
} finally {
|
|
@@ -36730,25 +36880,25 @@ var init_desktop = __esm(() => {
|
|
|
36730
36880
|
// src/editor-log.ts
|
|
36731
36881
|
import {
|
|
36732
36882
|
closeSync as closeSync2,
|
|
36733
|
-
existsSync as
|
|
36883
|
+
existsSync as existsSync20,
|
|
36734
36884
|
mkdirSync as mkdirSync10,
|
|
36735
36885
|
openSync as openSync2,
|
|
36736
|
-
readFileSync as
|
|
36886
|
+
readFileSync as readFileSync21,
|
|
36737
36887
|
readSync,
|
|
36738
36888
|
rmSync as rmSync11,
|
|
36739
36889
|
statSync as statSync10,
|
|
36740
36890
|
writeFileSync as writeFileSync13
|
|
36741
36891
|
} from "node:fs";
|
|
36742
|
-
import { dirname as dirname15, join as
|
|
36892
|
+
import { dirname as dirname15, join as join24 } from "node:path";
|
|
36743
36893
|
function editorLogPath(projectPath) {
|
|
36744
|
-
return
|
|
36894
|
+
return join24(projectPath, ".godot", "gdharness-editor.log");
|
|
36745
36895
|
}
|
|
36746
36896
|
function editorLogNotePath(projectPath) {
|
|
36747
|
-
return
|
|
36897
|
+
return join24(projectPath, ".godot", "gdharness-editor.json");
|
|
36748
36898
|
}
|
|
36749
36899
|
function readEditorLogNote(projectPath) {
|
|
36750
36900
|
try {
|
|
36751
|
-
const fields = asParams(JSON.parse(
|
|
36901
|
+
const fields = asParams(JSON.parse(readFileSync21(editorLogNotePath(projectPath), "utf8")));
|
|
36752
36902
|
const pid = readNumber(fields, "pid");
|
|
36753
36903
|
const startedAt = readString(fields, "startedAt");
|
|
36754
36904
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -36789,7 +36939,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
36789
36939
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
36790
36940
|
}
|
|
36791
36941
|
const path = editorLogPath(projectPath);
|
|
36792
|
-
if (!
|
|
36942
|
+
if (!existsSync20(path)) {
|
|
36793
36943
|
return { kind: "not written" };
|
|
36794
36944
|
}
|
|
36795
36945
|
const log = new GameLog;
|
|
@@ -36847,7 +36997,7 @@ function foldProgress(text) {
|
|
|
36847
36997
|
function theFirstOf(path) {
|
|
36848
36998
|
const whole = statSync10(path).size;
|
|
36849
36999
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
36850
|
-
return { text:
|
|
37000
|
+
return { text: readFileSync21(path, "utf8") };
|
|
36851
37001
|
}
|
|
36852
37002
|
const handle = openSync2(path, "r");
|
|
36853
37003
|
try {
|
|
@@ -37008,7 +37158,7 @@ var init_editor_log = __esm(() => {
|
|
|
37008
37158
|
});
|
|
37009
37159
|
|
|
37010
37160
|
// src/rename.ts
|
|
37011
|
-
import { existsSync as
|
|
37161
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync22, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
37012
37162
|
import { dirname as dirname16, posix as posix2 } from "node:path";
|
|
37013
37163
|
|
|
37014
37164
|
class Script {
|
|
@@ -37713,11 +37863,11 @@ function rewritesOf(plan, texts, move) {
|
|
|
37713
37863
|
}
|
|
37714
37864
|
function applyRewrites(rewrites) {
|
|
37715
37865
|
for (const rewrite of rewrites) {
|
|
37716
|
-
const now =
|
|
37866
|
+
const now = readFileSync22(rewrite.absolute, "utf8");
|
|
37717
37867
|
if (now !== rewrite.before) {
|
|
37718
37868
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
37719
37869
|
}
|
|
37720
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
37870
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync21(rewrite.writeTo)) {
|
|
37721
37871
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
37722
37872
|
}
|
|
37723
37873
|
}
|
|
@@ -37731,7 +37881,7 @@ function applyRewrites(rewrites) {
|
|
|
37731
37881
|
let movedUid = false;
|
|
37732
37882
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
37733
37883
|
unlinkSync3(rewrite.absolute);
|
|
37734
|
-
if (
|
|
37884
|
+
if (existsSync21(`${rewrite.absolute}.uid`)) {
|
|
37735
37885
|
renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
37736
37886
|
movedUid = true;
|
|
37737
37887
|
}
|
|
@@ -37777,10 +37927,10 @@ var init_rename = __esm(() => {
|
|
|
37777
37927
|
});
|
|
37778
37928
|
|
|
37779
37929
|
// src/lsp_client.ts
|
|
37780
|
-
import { existsSync as
|
|
37930
|
+
import { existsSync as existsSync22, realpathSync as realpathSync4 } from "node:fs";
|
|
37781
37931
|
import { readFile, realpath } from "node:fs/promises";
|
|
37782
37932
|
import { createConnection as createConnection3 } from "node:net";
|
|
37783
|
-
import { dirname as dirname17, join as
|
|
37933
|
+
import { dirname as dirname17, join as join25, resolve as resolve5 } from "node:path";
|
|
37784
37934
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
37785
37935
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
37786
37936
|
function initializeTimeoutMs() {
|
|
@@ -38458,7 +38608,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
38458
38608
|
} catch {
|
|
38459
38609
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
38460
38610
|
}
|
|
38461
|
-
if (!
|
|
38611
|
+
if (!existsSync22(join25(projectPath, "project.godot"))) {
|
|
38462
38612
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
38463
38613
|
}
|
|
38464
38614
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -38618,8 +38768,8 @@ var init_lsp_client = __esm(() => {
|
|
|
38618
38768
|
});
|
|
38619
38769
|
|
|
38620
38770
|
// src/rename-symbol.ts
|
|
38621
|
-
import { existsSync as
|
|
38622
|
-
import { basename as basename7, dirname as dirname18, join as
|
|
38771
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync5, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
|
|
38772
|
+
import { basename as basename7, dirname as dirname18, join as join26, relative as relative4, resolve as resolve6 } from "node:path";
|
|
38623
38773
|
function resourcePathOf(projectPath, absolute) {
|
|
38624
38774
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
38625
38775
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -38632,7 +38782,7 @@ function onDiskSpelling(path) {
|
|
|
38632
38782
|
let at = resolve6(path);
|
|
38633
38783
|
for (;; ) {
|
|
38634
38784
|
try {
|
|
38635
|
-
return
|
|
38785
|
+
return join26(realpathSync5.native(at), ...missing.reverse());
|
|
38636
38786
|
} catch {
|
|
38637
38787
|
const parent = dirname18(at);
|
|
38638
38788
|
if (parent === at) {
|
|
@@ -38685,7 +38835,7 @@ async function renameSymbol(request, services) {
|
|
|
38685
38835
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
38686
38836
|
};
|
|
38687
38837
|
}
|
|
38688
|
-
if (
|
|
38838
|
+
if (existsSync23(request.newScriptPath)) {
|
|
38689
38839
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
38690
38840
|
}
|
|
38691
38841
|
}
|
|
@@ -38861,7 +39011,7 @@ async function renameSymbol(request, services) {
|
|
|
38861
39011
|
scripts,
|
|
38862
39012
|
moves: new Map([[declaringPath, moveTo]]),
|
|
38863
39013
|
named: [declaringPath],
|
|
38864
|
-
exists: (path) =>
|
|
39014
|
+
exists: (path) => existsSync23(`${projectPath}/${path.slice("res://".length)}`)
|
|
38865
39015
|
}, plan);
|
|
38866
39016
|
}
|
|
38867
39017
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
|
|
@@ -39060,15 +39210,15 @@ ${mention.kind}`;
|
|
|
39060
39210
|
};
|
|
39061
39211
|
}
|
|
39062
39212
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
39063
|
-
const directory =
|
|
39213
|
+
const directory = join26(projectPath, ".godot", "gdharness-renames");
|
|
39064
39214
|
mkdirSync12(directory, { recursive: true });
|
|
39065
39215
|
detailWrites += 1;
|
|
39066
|
-
const path =
|
|
39216
|
+
const path = join26(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
39067
39217
|
writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
|
|
39068
39218
|
`, "utf8");
|
|
39069
39219
|
const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
39070
39220
|
for (const old of kept.slice(10)) {
|
|
39071
|
-
rmSync12(
|
|
39221
|
+
rmSync12(join26(directory, old), { force: true });
|
|
39072
39222
|
}
|
|
39073
39223
|
return path;
|
|
39074
39224
|
}
|
|
@@ -39109,18 +39259,18 @@ var init_rename_symbol = __esm(() => {
|
|
|
39109
39259
|
|
|
39110
39260
|
// src/file-move.ts
|
|
39111
39261
|
import {
|
|
39112
|
-
existsSync as
|
|
39262
|
+
existsSync as existsSync24,
|
|
39113
39263
|
mkdirSync as mkdirSync13,
|
|
39114
39264
|
readdirSync as readdirSync12,
|
|
39115
|
-
readFileSync as
|
|
39265
|
+
readFileSync as readFileSync23,
|
|
39116
39266
|
renameSync as renameSync4,
|
|
39117
39267
|
statSync as statSync11,
|
|
39118
39268
|
writeFileSync as writeFileSync16
|
|
39119
39269
|
} from "node:fs";
|
|
39120
|
-
import { basename as basename8, dirname as dirname19, extname as extname3, join as
|
|
39270
|
+
import { basename as basename8, dirname as dirname19, extname as extname3, join as join27, relative as relative5 } from "node:path";
|
|
39121
39271
|
function filesUnder(directory) {
|
|
39122
39272
|
return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
39123
|
-
const path =
|
|
39273
|
+
const path = join27(directory, entry.name);
|
|
39124
39274
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
39125
39275
|
});
|
|
39126
39276
|
}
|
|
@@ -39141,8 +39291,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
39141
39291
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
39142
39292
|
}
|
|
39143
39293
|
if (at < steps.length - 1) {
|
|
39144
|
-
directory =
|
|
39145
|
-
if (
|
|
39294
|
+
directory = join27(directory, step);
|
|
39295
|
+
if (existsSync24(join27(directory, ".gdignore"))) {
|
|
39146
39296
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
39147
39297
|
}
|
|
39148
39298
|
}
|
|
@@ -39154,12 +39304,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
39154
39304
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
39155
39305
|
return null;
|
|
39156
39306
|
}
|
|
39157
|
-
return
|
|
39307
|
+
return existsSync24(join27(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
39158
39308
|
}
|
|
39159
39309
|
function binaryResources(projectPath) {
|
|
39160
39310
|
const found = [];
|
|
39161
39311
|
const visit = (directory, prefix) => {
|
|
39162
|
-
if (
|
|
39312
|
+
if (existsSync24(join27(directory, ".gdignore"))) {
|
|
39163
39313
|
return;
|
|
39164
39314
|
}
|
|
39165
39315
|
for (const entry of readdirSync12(directory, { withFileTypes: true })) {
|
|
@@ -39168,7 +39318,7 @@ function binaryResources(projectPath) {
|
|
|
39168
39318
|
}
|
|
39169
39319
|
const path = `${prefix}${entry.name}`;
|
|
39170
39320
|
if (entry.isDirectory()) {
|
|
39171
|
-
visit(
|
|
39321
|
+
visit(join27(directory, entry.name), `${path}/`);
|
|
39172
39322
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
39173
39323
|
found.push(path);
|
|
39174
39324
|
}
|
|
@@ -39178,7 +39328,7 @@ function binaryResources(projectPath) {
|
|
|
39178
39328
|
return found;
|
|
39179
39329
|
}
|
|
39180
39330
|
function targetPathOf(projectPath, absolute) {
|
|
39181
|
-
const inside = relative5(onDiskSpelling(projectPath),
|
|
39331
|
+
const inside = relative5(onDiskSpelling(projectPath), join27(onDiskSpelling(dirname19(absolute)), basename8(absolute)));
|
|
39182
39332
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
39183
39333
|
return null;
|
|
39184
39334
|
}
|
|
@@ -39196,7 +39346,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39196
39346
|
if (to === null) {
|
|
39197
39347
|
return { ok: false, reason: `newPath must be a path inside the project, and ${absoluteTo} is not.` };
|
|
39198
39348
|
}
|
|
39199
|
-
if (!
|
|
39349
|
+
if (!existsSync24(absoluteFrom)) {
|
|
39200
39350
|
return { ok: false, reason: `There is nothing at ${from} to move.` };
|
|
39201
39351
|
}
|
|
39202
39352
|
if (from === to) {
|
|
@@ -39225,7 +39375,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39225
39375
|
if (isDirectory && to.startsWith(`${from}/`)) {
|
|
39226
39376
|
return { ok: false, reason: `${from} cannot be moved into itself.` };
|
|
39227
39377
|
}
|
|
39228
|
-
if (
|
|
39378
|
+
if (existsSync24(absoluteTo) && !sameFile(absoluteFrom, absoluteTo)) {
|
|
39229
39379
|
return { ok: false, reason: `${to} already exists, so nothing was moved there.` };
|
|
39230
39380
|
}
|
|
39231
39381
|
const unseen = unseenByTheEngine(projectPath, to);
|
|
@@ -39233,7 +39383,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39233
39383
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
39234
39384
|
}
|
|
39235
39385
|
if (!isDirectory) {
|
|
39236
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null &&
|
|
39386
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync24(join27(projectPath, path.slice("res://".length))));
|
|
39237
39387
|
if (owner !== undefined && owner !== null) {
|
|
39238
39388
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
39239
39389
|
}
|
|
@@ -39255,7 +39405,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39255
39405
|
} else {
|
|
39256
39406
|
moves.set(from, to);
|
|
39257
39407
|
for (const sidecar of SIDECARS) {
|
|
39258
|
-
if (
|
|
39408
|
+
if (existsSync24(`${absoluteFrom}${sidecar}`)) {
|
|
39259
39409
|
moves.set(`${from}${sidecar}`, `${to}${sidecar}`);
|
|
39260
39410
|
}
|
|
39261
39411
|
}
|
|
@@ -39267,12 +39417,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39267
39417
|
scripts,
|
|
39268
39418
|
moves,
|
|
39269
39419
|
named: [from],
|
|
39270
|
-
exists: (path) =>
|
|
39420
|
+
exists: (path) => existsSync24(join27(projectPath, path.slice("res://".length)))
|
|
39271
39421
|
}, { edits: [], mentions: [] });
|
|
39272
39422
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
39273
39423
|
const binaries = [];
|
|
39274
39424
|
for (const path of binaryResources(projectPath)) {
|
|
39275
|
-
const bytes =
|
|
39425
|
+
const bytes = readFileSync23(join27(projectPath, path.slice("res://".length)));
|
|
39276
39426
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
39277
39427
|
if (names !== undefined) {
|
|
39278
39428
|
binaries.push({
|
|
@@ -39291,7 +39441,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39291
39441
|
}));
|
|
39292
39442
|
const left = leftAloneReport(mentions);
|
|
39293
39443
|
const rewrites = rewritesOf(plan, texts, null);
|
|
39294
|
-
const absoluteOf = (path) =>
|
|
39444
|
+
const absoluteOf = (path) => join27(projectPath, path.slice("res://".length));
|
|
39295
39445
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
39296
39446
|
const moved = [...moves].map(([source, target]) => {
|
|
39297
39447
|
const rewrite = rewritten.get(source);
|
|
@@ -39333,18 +39483,18 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39333
39483
|
}
|
|
39334
39484
|
function applyFileMove(outcome) {
|
|
39335
39485
|
for (const rewrite of outcome.inPlace) {
|
|
39336
|
-
if (
|
|
39486
|
+
if (readFileSync23(rewrite.absolute, "utf8") !== rewrite.before) {
|
|
39337
39487
|
throw new Error(`${rewrite.path} changed on disk while the move was being worked out; nothing was written`);
|
|
39338
39488
|
}
|
|
39339
39489
|
}
|
|
39340
39490
|
for (const file of outcome.moved) {
|
|
39341
|
-
if (!
|
|
39491
|
+
if (!existsSync24(file.absoluteFrom)) {
|
|
39342
39492
|
throw new Error(`${file.from} is gone from disk; nothing was written`);
|
|
39343
39493
|
}
|
|
39344
|
-
if (file.text !== null &&
|
|
39494
|
+
if (file.text !== null && readFileSync23(file.absoluteFrom, "utf8") !== file.text.before) {
|
|
39345
39495
|
throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
|
|
39346
39496
|
}
|
|
39347
|
-
if (
|
|
39497
|
+
if (existsSync24(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
|
|
39348
39498
|
throw new Error(`${file.to} already exists; nothing was written`);
|
|
39349
39499
|
}
|
|
39350
39500
|
}
|
|
@@ -43537,10 +43687,10 @@ var init_keeper_channel = () => {};
|
|
|
43537
43687
|
|
|
43538
43688
|
// src/lifecycle-log.ts
|
|
43539
43689
|
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync12 } from "node:fs";
|
|
43540
|
-
import { dirname as dirname20, join as
|
|
43690
|
+
import { dirname as dirname20, join as join28 } from "node:path";
|
|
43541
43691
|
import process11 from "node:process";
|
|
43542
43692
|
function lifecycleLogPath() {
|
|
43543
|
-
return
|
|
43693
|
+
return join28(runtimeDirectory(), "servers.log");
|
|
43544
43694
|
}
|
|
43545
43695
|
function writeLifecycle(event, fields = {}) {
|
|
43546
43696
|
const path = lifecycleLogPath();
|
|
@@ -43860,10 +44010,10 @@ var init_prompts = __esm(() => {
|
|
|
43860
44010
|
});
|
|
43861
44011
|
|
|
43862
44012
|
// src/restart-note.ts
|
|
43863
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
43864
|
-
import { dirname as dirname21, join as
|
|
44013
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync24, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
44014
|
+
import { dirname as dirname21, join as join29 } from "node:path";
|
|
43865
44015
|
function restartNotePath(projectPath) {
|
|
43866
|
-
return
|
|
44016
|
+
return join29(projectPath, ".godot", "gdharness-restart.json");
|
|
43867
44017
|
}
|
|
43868
44018
|
function noteRestartBegun(note) {
|
|
43869
44019
|
const path = restartNotePath(note.projectPath);
|
|
@@ -43877,7 +44027,7 @@ function noteRestartBegun(note) {
|
|
|
43877
44027
|
}
|
|
43878
44028
|
function restartOwed(projectPath) {
|
|
43879
44029
|
try {
|
|
43880
|
-
const parsed = JSON.parse(
|
|
44030
|
+
const parsed = JSON.parse(readFileSync24(restartNotePath(projectPath), "utf8"));
|
|
43881
44031
|
if (typeof parsed !== "object" || parsed === null) {
|
|
43882
44032
|
return null;
|
|
43883
44033
|
}
|
|
@@ -43903,17 +44053,17 @@ var init_restart_note = __esm(() => {
|
|
|
43903
44053
|
|
|
43904
44054
|
// src/run-record.ts
|
|
43905
44055
|
import { createHash as createHash5 } from "node:crypto";
|
|
43906
|
-
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as
|
|
43907
|
-
import { basename as basename9, dirname as dirname22, join as
|
|
44056
|
+
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as readFileSync25, realpathSync as realpathSync6, rmSync as rmSync14, statSync as statSync13 } from "node:fs";
|
|
44057
|
+
import { basename as basename9, dirname as dirname22, join as join30, resolve as resolve7 } from "node:path";
|
|
43908
44058
|
function runsDirectory() {
|
|
43909
|
-
return
|
|
44059
|
+
return join30(runtimeDirectory(), "runs");
|
|
43910
44060
|
}
|
|
43911
44061
|
function realAsFarAsItExists(path) {
|
|
43912
44062
|
const missing = [];
|
|
43913
44063
|
let at = path;
|
|
43914
44064
|
for (;; ) {
|
|
43915
44065
|
try {
|
|
43916
|
-
return
|
|
44066
|
+
return join30(realpathSync6.native(at), ...missing);
|
|
43917
44067
|
} catch {
|
|
43918
44068
|
const parent = dirname22(at);
|
|
43919
44069
|
if (parent === at) {
|
|
@@ -43930,10 +44080,10 @@ function projectKey(projectPath) {
|
|
|
43930
44080
|
return createHash5("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
43931
44081
|
}
|
|
43932
44082
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
43933
|
-
return
|
|
44083
|
+
return join30(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
43934
44084
|
}
|
|
43935
44085
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
43936
|
-
return
|
|
44086
|
+
return join30(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
43937
44087
|
}
|
|
43938
44088
|
function writeLatestRun(servedBy, latest) {
|
|
43939
44089
|
try {
|
|
@@ -43943,7 +44093,7 @@ function writeLatestRun(servedBy, latest) {
|
|
|
43943
44093
|
}
|
|
43944
44094
|
function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
43945
44095
|
try {
|
|
43946
|
-
const fields = JSON.parse(
|
|
44096
|
+
const fields = JSON.parse(readFileSync25(latestRunPath(servedBy, directory), "utf8"));
|
|
43947
44097
|
if (typeof fields !== "object" || fields === null) {
|
|
43948
44098
|
return null;
|
|
43949
44099
|
}
|
|
@@ -43958,20 +44108,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
43958
44108
|
}
|
|
43959
44109
|
function openTranscript(startedAt) {
|
|
43960
44110
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
43961
|
-
const path =
|
|
44111
|
+
const path = join30(runsDirectory(), `run-${startedAt}.log`);
|
|
43962
44112
|
return { path, fd: openSync3(path, "a") };
|
|
43963
44113
|
}
|
|
43964
44114
|
function profilePath(startedAt) {
|
|
43965
|
-
return
|
|
44115
|
+
return join30(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
43966
44116
|
}
|
|
43967
44117
|
function profileMarkPath(startedAt) {
|
|
43968
|
-
return
|
|
44118
|
+
return join30(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
43969
44119
|
}
|
|
43970
44120
|
function noteIsAbout(note, playing) {
|
|
43971
44121
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
43972
44122
|
}
|
|
43973
44123
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
43974
|
-
return
|
|
44124
|
+
return join30(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
43975
44125
|
}
|
|
43976
44126
|
function writeEditorRunNote(note) {
|
|
43977
44127
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
@@ -43982,7 +44132,7 @@ function writeEditorRunNote(note) {
|
|
|
43982
44132
|
}
|
|
43983
44133
|
function readEditorRunNote(projectPath) {
|
|
43984
44134
|
for (const directory of runtimeDirectories()) {
|
|
43985
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
44135
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join30(directory, "runs", "editor-run.json"));
|
|
43986
44136
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
43987
44137
|
return note;
|
|
43988
44138
|
}
|
|
@@ -43992,7 +44142,7 @@ function readEditorRunNote(projectPath) {
|
|
|
43992
44142
|
function editorNoteAt(path) {
|
|
43993
44143
|
let parsed;
|
|
43994
44144
|
try {
|
|
43995
|
-
parsed = JSON.parse(
|
|
44145
|
+
parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
43996
44146
|
} catch {
|
|
43997
44147
|
return null;
|
|
43998
44148
|
}
|
|
@@ -44016,7 +44166,7 @@ function readRunRecord(projectPath) {
|
|
|
44016
44166
|
if (own !== null) {
|
|
44017
44167
|
return own;
|
|
44018
44168
|
}
|
|
44019
|
-
const shared = recordAt(
|
|
44169
|
+
const shared = recordAt(join30(directory, "runs", SHARED_NOTE));
|
|
44020
44170
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
44021
44171
|
return shared;
|
|
44022
44172
|
}
|
|
@@ -44028,13 +44178,13 @@ function everyRunRecord() {
|
|
|
44028
44178
|
for (const directory of runtimeDirectories()) {
|
|
44029
44179
|
let entries;
|
|
44030
44180
|
try {
|
|
44031
|
-
entries = readdirSync13(
|
|
44181
|
+
entries = readdirSync13(join30(directory, "runs"));
|
|
44032
44182
|
} catch {
|
|
44033
44183
|
continue;
|
|
44034
44184
|
}
|
|
44035
44185
|
for (const entry of entries) {
|
|
44036
44186
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
44037
|
-
const record = recordAt(
|
|
44187
|
+
const record = recordAt(join30(directory, "runs", entry));
|
|
44038
44188
|
if (record !== null) {
|
|
44039
44189
|
found.push(record);
|
|
44040
44190
|
}
|
|
@@ -44046,7 +44196,7 @@ function everyRunRecord() {
|
|
|
44046
44196
|
function recordAt(path) {
|
|
44047
44197
|
let parsed;
|
|
44048
44198
|
try {
|
|
44049
|
-
parsed = JSON.parse(
|
|
44199
|
+
parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
44050
44200
|
} catch {
|
|
44051
44201
|
return null;
|
|
44052
44202
|
}
|
|
@@ -44099,7 +44249,7 @@ async function runningAs(pid) {
|
|
|
44099
44249
|
const began = await startedAt(pid, null);
|
|
44100
44250
|
const when = began === null ? {} : { startedAt: began };
|
|
44101
44251
|
if (process.platform === "linux") {
|
|
44102
|
-
const raw =
|
|
44252
|
+
const raw = readFileSync25(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
44103
44253
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
44104
44254
|
}
|
|
44105
44255
|
const args = (await runHidden("ps", ["-p", String(pid), "-o", "args="], { timeout: 15000 })).stdout.trim();
|
|
@@ -44242,7 +44392,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
44242
44392
|
}
|
|
44243
44393
|
function clearRunRecord(projectPath) {
|
|
44244
44394
|
for (const directory of runtimeDirectories()) {
|
|
44245
|
-
const shared =
|
|
44395
|
+
const shared = join30(directory, "runs", SHARED_NOTE);
|
|
44246
44396
|
const sharedRecord = recordAt(shared);
|
|
44247
44397
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
44248
44398
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
@@ -44277,7 +44427,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
44277
44427
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
44278
44428
|
continue;
|
|
44279
44429
|
}
|
|
44280
|
-
const path =
|
|
44430
|
+
const path = join30(directory, entry);
|
|
44281
44431
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
44282
44432
|
continue;
|
|
44283
44433
|
}
|
|
@@ -44298,7 +44448,7 @@ var init_run_record = __esm(() => {
|
|
|
44298
44448
|
});
|
|
44299
44449
|
|
|
44300
44450
|
// src/script-origins.ts
|
|
44301
|
-
import { join as
|
|
44451
|
+
import { join as join31 } from "node:path";
|
|
44302
44452
|
function unresolvedClassIn(message) {
|
|
44303
44453
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
44304
44454
|
}
|
|
@@ -44335,6 +44485,12 @@ async function originsOf(errors, declared, check) {
|
|
|
44335
44485
|
}
|
|
44336
44486
|
return { origins, unconfirmed };
|
|
44337
44487
|
}
|
|
44488
|
+
function compileErrorsIn(entries) {
|
|
44489
|
+
return entries.flatMap((entry) => {
|
|
44490
|
+
const at = entry.severity === "error" ? reloadedAt(entry) : null;
|
|
44491
|
+
return at === null ? [] : [{ ...at, message: entry.text }];
|
|
44492
|
+
});
|
|
44493
|
+
}
|
|
44338
44494
|
function reloadedAt(entry) {
|
|
44339
44495
|
for (const line of entry.detail) {
|
|
44340
44496
|
const at = RELOADED_AT.exec(line.trim());
|
|
@@ -44351,7 +44507,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
44351
44507
|
const ran = await runEngine(godotPath, [
|
|
44352
44508
|
"--headless",
|
|
44353
44509
|
"--log-file",
|
|
44354
|
-
|
|
44510
|
+
join31(logDir, "engine.log"),
|
|
44355
44511
|
"--path",
|
|
44356
44512
|
projectPath,
|
|
44357
44513
|
"--check-only",
|
|
@@ -44396,10 +44552,10 @@ var init_script_origins = __esm(() => {
|
|
|
44396
44552
|
});
|
|
44397
44553
|
|
|
44398
44554
|
// src/script-profile.ts
|
|
44399
|
-
import { readFileSync as
|
|
44555
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
44400
44556
|
function readProfileFile(path) {
|
|
44401
44557
|
try {
|
|
44402
|
-
const parsed = JSON.parse(
|
|
44558
|
+
const parsed = JSON.parse(readFileSync26(path, "utf8"));
|
|
44403
44559
|
return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
|
|
44404
44560
|
} catch {
|
|
44405
44561
|
return null;
|
|
@@ -44427,12 +44583,16 @@ function signatureParts(signature) {
|
|
|
44427
44583
|
function profiledFunctions(totals, limit, sourceOf = () => null) {
|
|
44428
44584
|
const perCallUs = perCallCostOf(totals);
|
|
44429
44585
|
const rows = rowsOf(totals, sourceOf).map((row) => withOverhead(row, perCallUs));
|
|
44430
|
-
const
|
|
44586
|
+
const called = Object.entries(totals.functions).filter(([signature, one]) => one.calls > 0 && !isCalibration(signature));
|
|
44587
|
+
const addon = called.filter(([signature]) => isRuntimeAddon(signature));
|
|
44588
|
+
const selfOf = (entries) => ms(entries.reduce((sum, [, one]) => sum + one.selfSeconds, 0));
|
|
44431
44589
|
return {
|
|
44432
44590
|
functions: rows.slice(0, limit).map((row) => placedLambda(row, sourceOf)),
|
|
44433
44591
|
omitted: Math.max(0, rows.length - limit),
|
|
44434
|
-
scriptMs,
|
|
44592
|
+
scriptMs: selfOf(called.filter(([signature]) => !isRuntimeAddon(signature))),
|
|
44435
44593
|
counted: rows.length,
|
|
44594
|
+
addonMs: selfOf(addon),
|
|
44595
|
+
addonFunctions: addon.length,
|
|
44436
44596
|
superCallers: rows.filter((row) => row.selfIncludesSuper === true).length,
|
|
44437
44597
|
perCallUs,
|
|
44438
44598
|
overheadDominated: rows.filter((row) => row.overheadMs !== undefined).length,
|
|
@@ -44440,6 +44600,9 @@ function profiledFunctions(totals, limit, sourceOf = () => null) {
|
|
|
44440
44600
|
overheadReach: rows.findLastIndex((row) => row.overheadMs !== undefined) + 1
|
|
44441
44601
|
};
|
|
44442
44602
|
}
|
|
44603
|
+
function isRuntimeAddon(signature) {
|
|
44604
|
+
return signatureParts(signature).script.startsWith(RUNTIME_ADDON2);
|
|
44605
|
+
}
|
|
44443
44606
|
function isCalibration(signature) {
|
|
44444
44607
|
const parts = signatureParts(signature);
|
|
44445
44608
|
return parts.script.endsWith(RUNTIME_SCRIPT) && (parts.function === PROFILER_PROBE || parts.function === PROFILER_CALIBRATOR);
|
|
@@ -44449,7 +44612,7 @@ function writeProfileMark(path, mark) {
|
|
|
44449
44612
|
}
|
|
44450
44613
|
function readProfileMark(path) {
|
|
44451
44614
|
try {
|
|
44452
|
-
const parsed = JSON.parse(
|
|
44615
|
+
const parsed = JSON.parse(readFileSync26(path, "utf8"));
|
|
44453
44616
|
return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
|
|
44454
44617
|
} catch {
|
|
44455
44618
|
return null;
|
|
@@ -44515,7 +44678,7 @@ function rowsOf(totals, sourceOf) {
|
|
|
44515
44678
|
}
|
|
44516
44679
|
return codes.get(script) ?? null;
|
|
44517
44680
|
};
|
|
44518
|
-
return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isCalibration(signature)).map(([signature, sum]) => {
|
|
44681
|
+
return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isRuntimeAddon(signature) && !isCalibration(signature)).map(([signature, sum]) => {
|
|
44519
44682
|
const parts = signatureParts(signature);
|
|
44520
44683
|
const code = codeOfScript(parts.script);
|
|
44521
44684
|
const property = propertyOf(parts.function);
|
|
@@ -44591,7 +44754,7 @@ function callersOf(totals, name, scripts) {
|
|
|
44591
44754
|
property
|
|
44592
44755
|
};
|
|
44593
44756
|
}
|
|
44594
|
-
var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", CALL_SITES = 40;
|
|
44757
|
+
var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", RUNTIME_ADDON2 = "res://addons/gdharness_runtime/", CALL_SITES = 40;
|
|
44595
44758
|
var init_script_profile = __esm(() => {
|
|
44596
44759
|
init_gdscript_source();
|
|
44597
44760
|
init_godot_variant();
|
|
@@ -44622,18 +44785,18 @@ import {
|
|
|
44622
44785
|
closeSync as closeSync3,
|
|
44623
44786
|
constants,
|
|
44624
44787
|
copyFileSync,
|
|
44625
|
-
existsSync as
|
|
44788
|
+
existsSync as existsSync25,
|
|
44626
44789
|
fstatSync,
|
|
44627
44790
|
mkdirSync as mkdirSync17,
|
|
44628
44791
|
openSync as openSync4,
|
|
44629
44792
|
readdirSync as readdirSync14,
|
|
44630
|
-
readFileSync as
|
|
44793
|
+
readFileSync as readFileSync27,
|
|
44631
44794
|
readSync as readSync2,
|
|
44632
44795
|
rmdirSync as rmdirSync2,
|
|
44633
44796
|
statSync as statSync14,
|
|
44634
44797
|
writeFileSync as writeFileSync18
|
|
44635
44798
|
} from "node:fs";
|
|
44636
|
-
import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as
|
|
44799
|
+
import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join32, normalize as normalize2, relative as relative6 } from "node:path";
|
|
44637
44800
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
44638
44801
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
44639
44802
|
function editorRestartTimeoutMs() {
|
|
@@ -44670,15 +44833,15 @@ function writtenFileRefusal(path, rule) {
|
|
|
44670
44833
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
44671
44834
|
}
|
|
44672
44835
|
const directory = realPathOr(dirname23(path));
|
|
44673
|
-
if (!
|
|
44836
|
+
if (!existsSync25(directory)) {
|
|
44674
44837
|
return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
|
|
44675
44838
|
}
|
|
44676
|
-
const where =
|
|
44839
|
+
const where = join32(directory, basename10(path));
|
|
44677
44840
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
44678
44841
|
if (inside !== undefined) {
|
|
44679
44842
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
44680
44843
|
}
|
|
44681
|
-
const taken = rule.writes(where).find((file) =>
|
|
44844
|
+
const taken = rule.writes(where).find((file) => existsSync25(file));
|
|
44682
44845
|
if (taken !== undefined) {
|
|
44683
44846
|
return taken === where ? `${named} ${path} already exists; ${what} is not written over a file. Name a new one.` : `${named} ${path} would write ${taken}, which already exists; ${what} is not written over a file. Name a new one.`;
|
|
44684
44847
|
}
|
|
@@ -45150,7 +45313,12 @@ function unlistedValues(schema, value, path) {
|
|
|
45150
45313
|
const fields = schema;
|
|
45151
45314
|
const listed = fields["enum"];
|
|
45152
45315
|
if (Array.isArray(listed) && typeof value === "string") {
|
|
45153
|
-
|
|
45316
|
+
if (listed.includes(value)) {
|
|
45317
|
+
return [];
|
|
45318
|
+
}
|
|
45319
|
+
return [
|
|
45320
|
+
listed.length === 1 ? `${path} only as ${String(listed[0])}, not ${JSON.stringify(value)}` : `${path} as one of ${listed.join(", ")}, not ${JSON.stringify(value)}`
|
|
45321
|
+
];
|
|
45154
45322
|
}
|
|
45155
45323
|
if (Array.isArray(value)) {
|
|
45156
45324
|
return value.flatMap((element, index) => unlistedValues(fields["items"], element, `${path}[${index}]`));
|
|
@@ -45214,7 +45382,7 @@ function notRunNote(notRun, failFast) {
|
|
|
45214
45382
|
function exportPresetRefusal(projectPath, preset) {
|
|
45215
45383
|
let text;
|
|
45216
45384
|
try {
|
|
45217
|
-
text =
|
|
45385
|
+
text = readFileSync27(join32(projectPath, "export_presets.cfg"), "utf8");
|
|
45218
45386
|
} catch {
|
|
45219
45387
|
return `This project has no export_presets.cfg, so it has no preset '${preset}' to export with. project_export add_preset writes one, as the editor's Project > Export does.`;
|
|
45220
45388
|
}
|
|
@@ -45280,7 +45448,7 @@ function filesIn(folder) {
|
|
|
45280
45448
|
try {
|
|
45281
45449
|
for (const entry of readdirSync14(folder, { withFileTypes: true })) {
|
|
45282
45450
|
if (entry.isFile()) {
|
|
45283
|
-
found.set(entry.name, statSync14(
|
|
45451
|
+
found.set(entry.name, statSync14(join32(folder, entry.name)).mtimeMs);
|
|
45284
45452
|
}
|
|
45285
45453
|
}
|
|
45286
45454
|
} catch {}
|
|
@@ -45310,7 +45478,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
45310
45478
|
return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
|
|
45311
45479
|
}
|
|
45312
45480
|
function editorAddonMissing(projectPath) {
|
|
45313
|
-
if (!
|
|
45481
|
+
if (!existsSync25(join32(projectPath, "project.godot"))) {
|
|
45314
45482
|
return null;
|
|
45315
45483
|
}
|
|
45316
45484
|
const report = inspectProject(projectPath);
|
|
@@ -45476,8 +45644,34 @@ function scriptErrorsNote(errors, {
|
|
|
45476
45644
|
const opening = unnamed === 0 ? `No tests ran: gdUnit4 could not load ${scripts === 1 ? "one script" : `${scripts} scripts`} while looking for suites, and it runs no suite at all when one fails to load.` : `No tests ran: ${scripts + unnamed} scripts did not compile, and gdUnit4 runs no suite at all when one fails to load. It could not load ${scripts === 1 ? "one" : scripts} of them while looking for suites, and ${unnamed === 1 ? "the one it did not name comes" : `the ${unnamed} it did not name come`} first.`;
|
|
45477
45645
|
return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
|
|
45478
45646
|
}
|
|
45479
|
-
function
|
|
45647
|
+
function autoloadNotCompiledNote(at, errors, stoppedEarly) {
|
|
45648
|
+
const first = errors.scriptErrors[0];
|
|
45649
|
+
const where = first === undefined ? "" : `, and the first error the engine raised compiling was at ${first.path}${first.line === null ? "" : `:${first.line}`}: ${first.message}`;
|
|
45650
|
+
const others = errors.scriptErrors.length - 1;
|
|
45651
|
+
const more = others > 0 ? ` ${others === 1 ? "One more error is" : `${others} more errors are`} under scriptErrors.` : "";
|
|
45652
|
+
const through = errors.failedThroughDependency.length;
|
|
45653
|
+
const dependents = through > 0 ? ` ${through === 1 ? "One script" : `${through} scripts`} failed only through ${first === undefined ? "it" : "that"}, listed under failedThroughDependency.` : "";
|
|
45654
|
+
const ended = stoppedEarly ? "The run was stopped as soon as the engine said so rather than left to its timeout" : "The run ended before that was read, and what its suites said is set aside";
|
|
45655
|
+
const stop = /[.!?]\)?$/.test(where) ? "" : ".";
|
|
45656
|
+
return `No result counts: the autoload ${at.name} (${at.path}) did not compile${where}${stop}${more}${dependents} Every suite runs with the autoloads, so a case calling into a script that did not compile fails the same way on every pass, and one waiting in a loop for the call to succeed never ends. ${ended}. script_diagnostics checks a script without running anything.`;
|
|
45657
|
+
}
|
|
45658
|
+
function repeatingScriptError(entries, byScript) {
|
|
45659
|
+
const last = entries.at(-1);
|
|
45660
|
+
if (last === undefined || !byScript(last)) {
|
|
45661
|
+
return null;
|
|
45662
|
+
}
|
|
45663
|
+
const where = last.detail[0] ?? null;
|
|
45664
|
+
const times = entries.filter((entry) => entry.text === last.text && (entry.detail[0] ?? null) === where && byScript(entry)).length;
|
|
45665
|
+
return times >= LOOPING_AFTER ? { text: last.text, where: where?.replace(/^at: /, "") ?? null, times } : null;
|
|
45666
|
+
}
|
|
45667
|
+
function timedOutVerdict(timeoutMs, silentForMs, repeating = null) {
|
|
45480
45668
|
const quiet = Math.round(silentForMs);
|
|
45669
|
+
if (repeating !== null) {
|
|
45670
|
+
return {
|
|
45671
|
+
verdict: `timed out after ${timeoutMs} ms repeating one script error, printed ${repeating.times} times: ${repeating.text}${repeating.where === null ? "" : ` (${repeating.where})`}. A line that fails on every pass of a loop does this, and a longer timeoutMs would not end it`,
|
|
45672
|
+
hung: false
|
|
45673
|
+
};
|
|
45674
|
+
}
|
|
45481
45675
|
return quiet >= Math.min(STALLED_AFTER_MS, timeoutMs / 2) ? {
|
|
45482
45676
|
verdict: `hung: killed after ${timeoutMs} ms, having printed nothing for the last ${quiet} ms`,
|
|
45483
45677
|
hung: true
|
|
@@ -45548,7 +45742,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
45548
45742
|
let projectGodot = null;
|
|
45549
45743
|
if (projectPath !== null) {
|
|
45550
45744
|
try {
|
|
45551
|
-
projectGodot =
|
|
45745
|
+
projectGodot = readFileSync27(join32(projectPath, "project.godot"), "utf8");
|
|
45552
45746
|
} catch {}
|
|
45553
45747
|
}
|
|
45554
45748
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -45572,7 +45766,7 @@ function projectSourceOf(projectPath) {
|
|
|
45572
45766
|
let source = null;
|
|
45573
45767
|
if (inside.ok) {
|
|
45574
45768
|
try {
|
|
45575
|
-
source =
|
|
45769
|
+
source = readFileSync27(inside.absolutePath, "utf8");
|
|
45576
45770
|
} catch {}
|
|
45577
45771
|
}
|
|
45578
45772
|
read.set(script, source);
|
|
@@ -45623,6 +45817,9 @@ function profileAnswer(reading, limit) {
|
|
|
45623
45817
|
if (reading.coveredMs > 0 && table.scriptMs > reading.coveredMs) {
|
|
45624
45818
|
notes.push(`scriptMs, ${table.scriptMs}ms, is more than the ${reading.coveredMs}ms the profile covers.${table.superCallers > 0 ? " The super calls above, counted twice, can account for some or all of that; whatever they do not is" : " One thread cannot spend more time than passes, so that is"} functions running on several threads at once, through WorkerThreadPool or Thread. The engine adds every thread's time to the same function and does not say which thread a call ran on, so a function's selfMs is summed over every thread it ran on rather than taken out of frames, and a function that waits for the others, through wait_for_group_task_completion or wait_to_finish, has the wait in its own selfMs as well.`);
|
|
45625
45819
|
}
|
|
45820
|
+
if (table.addonFunctions > 0) {
|
|
45821
|
+
notes.push(`The gdharness runtime addon's own functions took ${table.addonMs}ms of self time over ${table.addonFunctions === 1 ? "one function" : `${table.addonFunctions} functions`}, answering tools such as runtime_invoke and sending the profile. They are left out of functions, functionsCounted and scriptMs and summed under runtimeAddonMs, since they are not the game's work, though that time was spent in the game's frames.`);
|
|
45822
|
+
}
|
|
45626
45823
|
notes.push(...cappedNotes(totals));
|
|
45627
45824
|
if (totals.unreadable > 0) {
|
|
45628
45825
|
notes.push(`${totals.unreadable} message${totals.unreadable === 1 ? "" : "s"} from the game could not be read, so the totals have that many gaps.`);
|
|
@@ -45637,6 +45834,8 @@ function profileAnswer(reading, limit) {
|
|
|
45637
45834
|
scriptMs: table.scriptMs,
|
|
45638
45835
|
perCallOverheadUs: table.perCallUs ?? undefined,
|
|
45639
45836
|
functionsCounted: table.counted,
|
|
45837
|
+
runtimeAddonMs: table.addonFunctions > 0 ? table.addonMs : undefined,
|
|
45838
|
+
runtimeAddonFunctions: table.addonFunctions > 0 ? table.addonFunctions : undefined,
|
|
45640
45839
|
omitted: table.omitted > 0 ? table.omitted : undefined,
|
|
45641
45840
|
functions: table.functions,
|
|
45642
45841
|
callers: reading.callersOf === undefined ? undefined : callersAnswer(totals, reading.callersOf),
|
|
@@ -45722,7 +45921,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
45722
45921
|
}
|
|
45723
45922
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
45724
45923
|
try {
|
|
45725
|
-
return settingKeys(
|
|
45924
|
+
return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
45726
45925
|
} catch {
|
|
45727
45926
|
return true;
|
|
45728
45927
|
}
|
|
@@ -45737,7 +45936,7 @@ function uncaughtNote(uncaught) {
|
|
|
45737
45936
|
}
|
|
45738
45937
|
function elsewhereIn(projectPath, asked) {
|
|
45739
45938
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
45740
|
-
if (wanted === "" ||
|
|
45939
|
+
if (wanted === "" || existsSync25(join32(projectPath, wanted))) {
|
|
45741
45940
|
return "";
|
|
45742
45941
|
}
|
|
45743
45942
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -45840,7 +46039,7 @@ function isDirectory(path) {
|
|
|
45840
46039
|
}
|
|
45841
46040
|
}
|
|
45842
46041
|
function scriptsUnder(directory, prefix) {
|
|
45843
|
-
if (
|
|
46042
|
+
if (existsSync25(join32(directory, ".gdignore"))) {
|
|
45844
46043
|
return [];
|
|
45845
46044
|
}
|
|
45846
46045
|
const found = [];
|
|
@@ -45850,7 +46049,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
45850
46049
|
}
|
|
45851
46050
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
45852
46051
|
if (entry.isDirectory()) {
|
|
45853
|
-
found.push(...scriptsUnder(
|
|
46052
|
+
found.push(...scriptsUnder(join32(directory, entry.name), path));
|
|
45854
46053
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
45855
46054
|
found.push(path);
|
|
45856
46055
|
}
|
|
@@ -45858,7 +46057,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
45858
46057
|
return found;
|
|
45859
46058
|
}
|
|
45860
46059
|
function hasMainScene(projectFile) {
|
|
45861
|
-
const scene = parseProjectGodot(
|
|
46060
|
+
const scene = parseProjectGodot(readFileSync27(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
45862
46061
|
return typeof scene === "string" && scene !== "";
|
|
45863
46062
|
}
|
|
45864
46063
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -45866,13 +46065,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
45866
46065
|
return false;
|
|
45867
46066
|
}
|
|
45868
46067
|
try {
|
|
45869
|
-
return
|
|
46068
|
+
return readFileSync27(join32(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
45870
46069
|
} catch {
|
|
45871
46070
|
return false;
|
|
45872
46071
|
}
|
|
45873
46072
|
}
|
|
45874
46073
|
function editorPlaysHeadless(projectFile) {
|
|
45875
|
-
const runArgs = parseProjectGodot(
|
|
46074
|
+
const runArgs = parseProjectGodot(readFileSync27(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
45876
46075
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
45877
46076
|
}
|
|
45878
46077
|
function camelCased(params) {
|
|
@@ -45891,7 +46090,7 @@ function needsItsOwnProcess(asked) {
|
|
|
45891
46090
|
class GodotServer {
|
|
45892
46091
|
stdio = null;
|
|
45893
46092
|
locator = new GodotLocator;
|
|
45894
|
-
operationsScript =
|
|
46093
|
+
operationsScript = join32(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
45895
46094
|
godotBridge;
|
|
45896
46095
|
tools = buildToolDefinitions();
|
|
45897
46096
|
activeProcess = null;
|
|
@@ -46152,14 +46351,14 @@ class GodotServer {
|
|
|
46152
46351
|
}
|
|
46153
46352
|
noteTheStart() {
|
|
46154
46353
|
const cwd = process.cwd();
|
|
46155
|
-
const mcpJson =
|
|
46354
|
+
const mcpJson = join32(cwd, ".mcp.json");
|
|
46156
46355
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
46157
46356
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
46158
46357
|
writeLifecycle("started", {
|
|
46159
46358
|
argv: process.argv.slice(1),
|
|
46160
46359
|
cwd,
|
|
46161
46360
|
project: this.ownProject,
|
|
46162
|
-
mcpJson:
|
|
46361
|
+
mcpJson: existsSync25(mcpJson) ? mcpJson : null
|
|
46163
46362
|
});
|
|
46164
46363
|
if (predecessor !== null) {
|
|
46165
46364
|
writeLifecycle("replaced", { project: this.ownProject, predecessor });
|
|
@@ -46300,7 +46499,7 @@ class GodotServer {
|
|
|
46300
46499
|
return checked.response;
|
|
46301
46500
|
}
|
|
46302
46501
|
const named = args["projectPath"];
|
|
46303
|
-
if (typeof named === "string" &&
|
|
46502
|
+
if (typeof named === "string" && existsSync25(join32(named, "project.godot"))) {
|
|
46304
46503
|
this.lastProjectPath = named;
|
|
46305
46504
|
}
|
|
46306
46505
|
this.updates.refresh();
|
|
@@ -46336,7 +46535,7 @@ class GodotServer {
|
|
|
46336
46535
|
let notSaved;
|
|
46337
46536
|
try {
|
|
46338
46537
|
savedAt = answer.content.map((item, index) => {
|
|
46339
|
-
const file =
|
|
46538
|
+
const file = join32(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
46340
46539
|
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
46341
46540
|
return file;
|
|
46342
46541
|
});
|
|
@@ -46954,8 +47153,8 @@ class GodotServer {
|
|
|
46954
47153
|
if (path === undefined) {
|
|
46955
47154
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
46956
47155
|
}
|
|
46957
|
-
const file =
|
|
46958
|
-
if (!
|
|
47156
|
+
const file = join32(path, "project.godot");
|
|
47157
|
+
if (!existsSync25(file)) {
|
|
46959
47158
|
return {
|
|
46960
47159
|
ok: false,
|
|
46961
47160
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -47615,7 +47814,7 @@ class GodotServer {
|
|
|
47615
47814
|
if (!engine.ok) {
|
|
47616
47815
|
return engine.response;
|
|
47617
47816
|
}
|
|
47618
|
-
const application = parseProjectGodot(
|
|
47817
|
+
const application = parseProjectGodot(readFileSync27(project.value.file, "utf8"))["application"] ?? {};
|
|
47619
47818
|
const name = application["config/name"];
|
|
47620
47819
|
const mainScene = application["run/main_scene"];
|
|
47621
47820
|
const info = {
|
|
@@ -47713,7 +47912,7 @@ class GodotServer {
|
|
|
47713
47912
|
const exportArgs = [
|
|
47714
47913
|
"--headless",
|
|
47715
47914
|
"--log-file",
|
|
47716
|
-
|
|
47915
|
+
join32(exportLogs, "engine.log"),
|
|
47717
47916
|
"--path",
|
|
47718
47917
|
project.value.path,
|
|
47719
47918
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -47721,7 +47920,7 @@ class GodotServer {
|
|
|
47721
47920
|
output.absolutePath
|
|
47722
47921
|
];
|
|
47723
47922
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
47724
|
-
const writtenAt = () =>
|
|
47923
|
+
const writtenAt = () => existsSync25(output.absolutePath) ? statSync14(output.absolutePath).mtimeMs : null;
|
|
47725
47924
|
const caches = editorCacheDirectory(engine.value);
|
|
47726
47925
|
const exportOnce = async () => {
|
|
47727
47926
|
const before = writtenAt();
|
|
@@ -47804,10 +48003,10 @@ class GodotServer {
|
|
|
47804
48003
|
if (movie !== null) {
|
|
47805
48004
|
return this.createErrorResponse(movie);
|
|
47806
48005
|
}
|
|
47807
|
-
const missing = paths.value.filter((path) => !
|
|
48006
|
+
const missing = paths.value.filter((path) => !existsSync25(join32(project.value.path, path.slice("res://".length))));
|
|
47808
48007
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
47809
48008
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
47810
|
-
const noRunner = !
|
|
48009
|
+
const noRunner = !existsSync25(join32(project.value.path, runner));
|
|
47811
48010
|
if (asked.length === 0) {
|
|
47812
48011
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
47813
48012
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -47877,35 +48076,50 @@ class GodotServer {
|
|
|
47877
48076
|
lastSaid = Date.now();
|
|
47878
48077
|
});
|
|
47879
48078
|
const reporting = this.reportTestProgress(run.log);
|
|
48079
|
+
const autoloaded = scriptsAutoloaded(project.value.path);
|
|
47880
48080
|
let silentForMs = 0;
|
|
48081
|
+
const stopped = { for: null };
|
|
47881
48082
|
const timedOut = await new Promise((resolve) => {
|
|
48083
|
+
let read = 0;
|
|
48084
|
+
const watching = setInterval(() => {
|
|
48085
|
+
const entries = run.log.all;
|
|
48086
|
+
for (const entry of entries.slice(read)) {
|
|
48087
|
+
stopped.for ??= autoloadNotCompiled(entry, autoloaded);
|
|
48088
|
+
}
|
|
48089
|
+
read = entries.length;
|
|
48090
|
+
if (stopped.for !== null) {
|
|
48091
|
+
clearInterval(watching);
|
|
48092
|
+
run.process.kill();
|
|
48093
|
+
}
|
|
48094
|
+
}, 100);
|
|
47882
48095
|
const timer = setTimeout(() => {
|
|
48096
|
+
clearInterval(watching);
|
|
47883
48097
|
silentForMs = Date.now() - lastSaid;
|
|
47884
48098
|
run.process.kill();
|
|
47885
48099
|
resolve(true);
|
|
47886
48100
|
}, timeoutMs);
|
|
47887
|
-
|
|
47888
|
-
|
|
47889
|
-
resolve(false);
|
|
47890
|
-
});
|
|
47891
|
-
run.process.once("error", () => {
|
|
48101
|
+
const ended = () => {
|
|
48102
|
+
clearInterval(watching);
|
|
47892
48103
|
clearTimeout(timer);
|
|
47893
48104
|
resolve(false);
|
|
47894
|
-
}
|
|
48105
|
+
};
|
|
48106
|
+
run.process.once("exit", ended);
|
|
48107
|
+
run.process.once("error", ended);
|
|
47895
48108
|
});
|
|
47896
48109
|
clearInterval(reporting);
|
|
47897
|
-
if (timedOut && run.process.pid !== undefined) {
|
|
48110
|
+
if ((timedOut || stopped.for !== null) && run.process.pid !== undefined) {
|
|
47898
48111
|
await untilGone([run.process.pid]);
|
|
47899
48112
|
}
|
|
47900
48113
|
await run.settled;
|
|
47901
|
-
const
|
|
48114
|
+
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
48115
|
+
const reportsDir = join32(project.value.path, ".godot", ...ours.split("/"));
|
|
47902
48116
|
let report = null;
|
|
47903
48117
|
let reportProblem = null;
|
|
47904
48118
|
try {
|
|
47905
|
-
const written =
|
|
48119
|
+
const written = existsSync25(reportsDir) && notCompiled === null ? readdirSync14(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
47906
48120
|
const newest = written.at(-1);
|
|
47907
48121
|
if (newest !== undefined) {
|
|
47908
|
-
report = parseJUnit(
|
|
48122
|
+
report = parseJUnit(readFileSync27(join32(reportsDir, newest, "results.xml"), "utf8"));
|
|
47909
48123
|
}
|
|
47910
48124
|
} catch (error) {
|
|
47911
48125
|
reportProblem = errorMessage(error);
|
|
@@ -47937,14 +48151,14 @@ class GodotServer {
|
|
|
47937
48151
|
limit: Number.POSITIVE_INFINITY
|
|
47938
48152
|
}).entries;
|
|
47939
48153
|
const said = printed.map((entry) => entry.text);
|
|
47940
|
-
const nothingRan = timedOut ? null : whyNoReport(said, asked.join(" and "));
|
|
47941
|
-
const cutShort = timedOut ? timedOutVerdict(timeoutMs, silentForMs) : null;
|
|
48154
|
+
const nothingRan = timedOut || notCompiled !== null ? null : whyNoReport(said, asked.join(" and "));
|
|
48155
|
+
const cutShort = timedOut && notCompiled === null ? timedOutVerdict(timeoutMs, silentForMs, repeatingScriptError(run.log.all, (entry) => run.log.raisedByScript(entry))) : null;
|
|
47942
48156
|
const hung = cutShort?.hung ?? false;
|
|
47943
|
-
const printedErrors = timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
|
|
48157
|
+
const printedErrors = notCompiled !== null ? compileErrorsIn(run.log.all) : timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
|
|
47944
48158
|
const declaring = printedErrors.some((error) => unresolvedClassIn(error.message) !== null) ? await originsOf(printedErrors, declaredClasses(project.value.path), checkWithEngine(engine.value, project.value.path)) : { origins: [], unconfirmed: [] };
|
|
47945
48159
|
const scriptErrors = [...declaring.origins, ...printedErrors];
|
|
47946
48160
|
const brokenScripts = new Set(scriptErrors.map((error) => error.path)).size;
|
|
47947
|
-
const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
|
|
48161
|
+
const ranAs = notCompiled !== null ? `the autoload ${notCompiled.name} (${notCompiled.path}) did not compile, so no result counts` : cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
|
|
47948
48162
|
const nothingAt = missing.join(" and ");
|
|
47949
48163
|
const verdict = missing.length === 0 ? ranAs : `${ranAs}; nothing at ${nothingAt}`;
|
|
47950
48164
|
const missingNote = missing.length === 0 ? undefined : `Nothing at ${nothingAt}, so ${missing.length === 1 ? "it was" : "they were"} left out and only ${asked.join(", ")} ran; a run of fewer paths than were asked for is not called a pass.${elsewhereIn(project.value.path, missing[0] ?? "")}`;
|
|
@@ -47972,7 +48186,7 @@ class GodotServer {
|
|
|
47972
48186
|
}),
|
|
47973
48187
|
...running === null ? {} : { suiteRunning: running }
|
|
47974
48188
|
};
|
|
47975
|
-
const said = nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
48189
|
+
const said = notCompiled !== null ? autoloadNotCompiledNote(notCompiled, foldedScriptErrors(scriptErrors), stopped.for !== null) : nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
47976
48190
|
const note = missingNote === undefined ? said : `${said} ${missingNote}`;
|
|
47977
48191
|
if (callSignal()?.aborted === true) {
|
|
47978
48192
|
keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
|
|
@@ -48271,11 +48485,11 @@ class GodotServer {
|
|
|
48271
48485
|
const cached = cachedClasses(projectPath);
|
|
48272
48486
|
const sourceOf = (resourcePath) => {
|
|
48273
48487
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48274
|
-
if (!contained.ok || !
|
|
48488
|
+
if (!contained.ok || !existsSync25(contained.absolutePath)) {
|
|
48275
48489
|
return null;
|
|
48276
48490
|
}
|
|
48277
48491
|
try {
|
|
48278
|
-
return
|
|
48492
|
+
return readFileSync27(contained.absolutePath, "utf8");
|
|
48279
48493
|
} catch {
|
|
48280
48494
|
return null;
|
|
48281
48495
|
}
|
|
@@ -48365,11 +48579,11 @@ class GodotServer {
|
|
|
48365
48579
|
}
|
|
48366
48580
|
sourceOfClass(projectPath, resourcePath) {
|
|
48367
48581
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48368
|
-
if (!contained.ok || !
|
|
48582
|
+
if (!contained.ok || !existsSync25(contained.absolutePath)) {
|
|
48369
48583
|
return null;
|
|
48370
48584
|
}
|
|
48371
48585
|
try {
|
|
48372
|
-
return
|
|
48586
|
+
return readFileSync27(contained.absolutePath, "utf8");
|
|
48373
48587
|
} catch {
|
|
48374
48588
|
return null;
|
|
48375
48589
|
}
|
|
@@ -48527,7 +48741,7 @@ class GodotServer {
|
|
|
48527
48741
|
...Array.isArray(stack) ? {
|
|
48528
48742
|
stack: stack.map((frame) => ({
|
|
48529
48743
|
...frame,
|
|
48530
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
48744
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join32(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
48531
48745
|
}))
|
|
48532
48746
|
} : {}
|
|
48533
48747
|
});
|
|
@@ -48806,7 +49020,7 @@ class GodotServer {
|
|
|
48806
49020
|
return null;
|
|
48807
49021
|
}
|
|
48808
49022
|
try {
|
|
48809
|
-
return settingKeys(
|
|
49023
|
+
return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8"));
|
|
48810
49024
|
} catch {
|
|
48811
49025
|
return null;
|
|
48812
49026
|
}
|
|
@@ -49243,7 +49457,7 @@ class GodotServer {
|
|
|
49243
49457
|
}
|
|
49244
49458
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
49245
49459
|
const budgetMs = budget.ms;
|
|
49246
|
-
if (!
|
|
49460
|
+
if (!existsSync25(join32(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
49247
49461
|
return runtimeVerdict(null, {
|
|
49248
49462
|
addon: false,
|
|
49249
49463
|
budgetMs,
|
|
@@ -49523,7 +49737,7 @@ class GodotServer {
|
|
|
49523
49737
|
return 0;
|
|
49524
49738
|
}
|
|
49525
49739
|
try {
|
|
49526
|
-
return reportAlreadyInTranscript(
|
|
49740
|
+
return reportAlreadyInTranscript(readFileSync27(game.transcript), readFileSync27(report));
|
|
49527
49741
|
} catch (error) {
|
|
49528
49742
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
49529
49743
|
return 0;
|
|
@@ -50225,7 +50439,7 @@ class GodotServer {
|
|
|
50225
50439
|
};
|
|
50226
50440
|
const going = await this.runStillGoing(run);
|
|
50227
50441
|
const projectPath = run.projectPath;
|
|
50228
|
-
const projectGone = projectPath !== null && !
|
|
50442
|
+
const projectGone = projectPath !== null && !existsSync25(join32(projectPath, "project.godot"));
|
|
50229
50443
|
if (projectGone && callersName !== undefined) {
|
|
50230
50444
|
return this.createErrorResponse(`callersOf reads the project's scripts, and the run's project at ${projectPath} has no project.godot any more.`, ["editor_run profile without callersOf answers with the profile itself"]);
|
|
50231
50445
|
}
|
|
@@ -50572,7 +50786,7 @@ class GodotServer {
|
|
|
50572
50786
|
notes.push(endedWithoutACode({
|
|
50573
50787
|
throughEditor: run.throughEditor,
|
|
50574
50788
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
50575
|
-
couldAnnounce: run.projectPath !== null &&
|
|
50789
|
+
couldAnnounce: run.projectPath !== null && existsSync25(join32(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
50576
50790
|
endedHere: typeof run.endedHere === "string",
|
|
50577
50791
|
pickedUp: run.pickedUp === true
|
|
50578
50792
|
}));
|
|
@@ -51202,7 +51416,7 @@ class GodotServer {
|
|
|
51202
51416
|
if (!scriptPath.ok) {
|
|
51203
51417
|
return this.createErrorResponse(scriptPath.reason);
|
|
51204
51418
|
}
|
|
51205
|
-
if (!
|
|
51419
|
+
if (!existsSync25(scriptPath.absolutePath)) {
|
|
51206
51420
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
51207
51421
|
}
|
|
51208
51422
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -51616,7 +51830,7 @@ class GodotServer {
|
|
|
51616
51830
|
}
|
|
51617
51831
|
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51618
51832
|
const declared = new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*class_name\\s+${escaped}\\b`, "m");
|
|
51619
|
-
if (declared.test(
|
|
51833
|
+
if (declared.test(readFileSync27(scriptPath, "utf8"))) {
|
|
51620
51834
|
return null;
|
|
51621
51835
|
}
|
|
51622
51836
|
const checked = await this.classesTheEditorCannotSee(args);
|
|
@@ -51784,7 +51998,7 @@ class GodotServer {
|
|
|
51784
51998
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
51785
51999
|
const run = await this.currentRun();
|
|
51786
52000
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
51787
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
52001
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync25(join32(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
51788
52002
|
return null;
|
|
51789
52003
|
}
|
|
51790
52004
|
const already = runtimesAnnounced().running;
|
|
@@ -51874,7 +52088,7 @@ class GodotServer {
|
|
|
51874
52088
|
}
|
|
51875
52089
|
}
|
|
51876
52090
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
51877
|
-
const screenshotPath = screenshotDir ?
|
|
52091
|
+
const screenshotPath = screenshotDir ? join32(screenshotDir, "capture.png") : null;
|
|
51878
52092
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
51879
52093
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
51880
52094
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -51893,8 +52107,10 @@ ${staleNote}`);
|
|
|
51893
52107
|
}
|
|
51894
52108
|
this.callsOnGames.add(onGame);
|
|
51895
52109
|
const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
|
|
52110
|
+
let savedLater = false;
|
|
51896
52111
|
try {
|
|
51897
|
-
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs,
|
|
52112
|
+
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, expectsScreenshot ? () => discard(screenshotDir) : "keep");
|
|
52113
|
+
savedLater = !reply.ok && reply.stillWithTheGame === true;
|
|
51898
52114
|
if (!reply.ok && reply.requestId !== undefined) {
|
|
51899
52115
|
return this.jsonTextResponse({
|
|
51900
52116
|
pending: true,
|
|
@@ -51940,12 +52156,14 @@ Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefine
|
|
|
51940
52156
|
|
|
51941
52157
|
${staleNote}`}`
|
|
51942
52158
|
},
|
|
51943
|
-
{ type: "image", data:
|
|
52159
|
+
{ type: "image", data: readFileSync27(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
51944
52160
|
]
|
|
51945
52161
|
};
|
|
51946
52162
|
} finally {
|
|
51947
52163
|
this.callsOnGames.delete(onGame);
|
|
51948
|
-
|
|
52164
|
+
if (!savedLater) {
|
|
52165
|
+
discard(screenshotDir);
|
|
52166
|
+
}
|
|
51949
52167
|
}
|
|
51950
52168
|
}
|
|
51951
52169
|
handleLateAnswer(args) {
|
|
@@ -52055,12 +52273,13 @@ async function runGodotServer() {
|
|
|
52055
52273
|
const server = new GodotServer;
|
|
52056
52274
|
await server.run();
|
|
52057
52275
|
}
|
|
52058
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
52276
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
52059
52277
|
var init_server = __esm(() => {
|
|
52060
52278
|
init_dist();
|
|
52061
52279
|
init_stdio();
|
|
52062
52280
|
init_alive();
|
|
52063
52281
|
init_answer_json();
|
|
52282
|
+
init_autoload_failure();
|
|
52064
52283
|
init_boot_note();
|
|
52065
52284
|
init_breakpoint_note();
|
|
52066
52285
|
init_bridge_announce();
|
|
@@ -52174,8 +52393,8 @@ var init_server = __esm(() => {
|
|
|
52174
52393
|
init_bridge_announce();
|
|
52175
52394
|
init_errors();
|
|
52176
52395
|
init_godot_path();
|
|
52177
|
-
import { existsSync as
|
|
52178
|
-
import { join as
|
|
52396
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
52397
|
+
import { join as join33, resolve as resolve8 } from "node:path";
|
|
52179
52398
|
|
|
52180
52399
|
// src/harnesses.ts
|
|
52181
52400
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -53459,7 +53678,7 @@ class UsageError extends Refusal {
|
|
|
53459
53678
|
function projectArgument(at) {
|
|
53460
53679
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
53461
53680
|
const projectPath = resolve8(named ?? ".");
|
|
53462
|
-
if (!
|
|
53681
|
+
if (!existsSync26(join33(projectPath, "project.godot"))) {
|
|
53463
53682
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
53464
53683
|
}
|
|
53465
53684
|
return projectPath;
|
|
@@ -53504,7 +53723,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
53504
53723
|
return;
|
|
53505
53724
|
}
|
|
53506
53725
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
53507
|
-
if (!
|
|
53726
|
+
if (!existsSync26(join33(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
53508
53727
|
console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
|
|
53509
53728
|
}
|
|
53510
53729
|
}
|
|
@@ -53636,7 +53855,7 @@ async function setup() {
|
|
|
53636
53855
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
53637
53856
|
}
|
|
53638
53857
|
if (!args.includes("--no-skill")) {
|
|
53639
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
53858
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync26);
|
|
53640
53859
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
53641
53860
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53642
53861
|
}
|
|
@@ -53750,7 +53969,7 @@ async function upgrade() {
|
|
|
53750
53969
|
moved.push(took.from);
|
|
53751
53970
|
}
|
|
53752
53971
|
}
|
|
53753
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
53972
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync26), version)) {
|
|
53754
53973
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53755
53974
|
}
|
|
53756
53975
|
console.log(`
|